From fb27cfbcbd2865b0e731c4aae47df71778da805e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 25 Aug 2010 12:19:53 -0700 Subject: xenfs/xenbus: report partial reads/writes correctly copy_(to|from)_user return the number of uncopied bytes, so a successful return is 0, and any non-zero result indicates some degree of failure. Reported-by: "Jun Zhu (Intern)" Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/xenfs/xenbus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index 9d5b519d2e4c..d2a905826804 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -142,7 +142,7 @@ static ssize_t xenbus_file_read(struct file *filp, i += sz - ret; rb->cons += sz - ret; - if (ret != sz) { + if (ret != 0) { if (i == 0) i = -EFAULT; goto out; @@ -453,7 +453,7 @@ static ssize_t xenbus_file_write(struct file *filp, ret = copy_from_user(u->u.buffer + u->len, ubuf, len); - if (ret == len) { + if (ret != 0) { rc = -EFAULT; goto out; } -- cgit v1.2.3 From 6d6df2e412297b8047c407b3abcd045a67c96744 Mon Sep 17 00:00:00 2001 From: Diego Ongaro Date: Wed, 1 Sep 2010 09:18:54 -0700 Subject: xenbus: allow any xenbus command over /proc/xen/xenbus When xenstored is in another domain, we need to be able to send any command over xenbus. This doesn't pose a security problem because its up to xenstored to determine whether a given client is allowed to use a particular command anyway. From linux-2.5.18-xen.hg 68d582b0ad05. Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/xenfs/xenbus.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index d2a905826804..46cf4048e60e 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -486,21 +486,6 @@ static ssize_t xenbus_file_write(struct file *filp, msg_type = u->u.msg.type; switch (msg_type) { - case XS_TRANSACTION_START: - case XS_TRANSACTION_END: - case XS_DIRECTORY: - case XS_READ: - case XS_GET_PERMS: - case XS_RELEASE: - case XS_GET_DOMAIN_PATH: - case XS_WRITE: - case XS_MKDIR: - case XS_RM: - case XS_SET_PERMS: - /* Send out a transaction */ - ret = xenbus_write_transaction(msg_type, u); - break; - case XS_WATCH: case XS_UNWATCH: /* (Un)Ask for some path to be watched for changes */ @@ -508,7 +493,8 @@ static ssize_t xenbus_file_write(struct file *filp, break; default: - ret = -EINVAL; + /* Send out a transaction */ + ret = xenbus_write_transaction(msg_type, u); break; } if (ret != 0) -- cgit v1.2.3 From 76ce7618f9a24f7b13958c67f7d5ccfcdab71475 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Tue, 7 Sep 2010 11:42:18 -0400 Subject: xenbus: add missing wakeup in concurrent read/write If an application has a dedicated read thread watching xenbus and another thread writes an XS_WATCH message that generates a synthetic "OK" reply, this reply will be enqueued in the buffer without waking up the reader. This can cause a deadlock in the application if it then waits for the read thread to receive the queued message. Signed-off-by: Daniel De Graaf commit e752969f502a511e83f841aa01d6cd332e6d85a0 Author: Daniel De Graaf Date: Tue Sep 7 11:21:52 2010 -0400 xenbus: fix deadlock in concurrent read/write If an application has a dedicated read thread watching xenbus and another thread writes an XS_WATCH message that generates a synthetic "OK" reply, this reply will be enqueued in the buffer without waking up the reader. Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/xenfs/xenbus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index 46cf4048e60e..c4c7db8363e7 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -405,6 +405,7 @@ static int xenbus_write_watch(unsigned msg_type, struct xenbus_file_priv *u) mutex_lock(&u->reply_mutex); rc = queue_reply(&u->read_buffers, &reply, sizeof(reply)); + wake_up(&u->read_waitq); mutex_unlock(&u->reply_mutex); } -- cgit v1.2.3 From 7808121b9a1e44ef12fecd49fa6c268f27a150fc Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Wed, 8 Sep 2010 18:10:42 -0400 Subject: xenbus: avoid zero returns from read() It is possible to get a zero return from read() in instances where the queue is not empty but has no elements with data to deliver to the user. Since a zero return from read is an error indicator, resume waiting or return -EAGAIN (for a nonblocking fd) in this case. Signed-off-by: Daniel De Graaf Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/xenfs/xenbus.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index c4c7db8363e7..55791dd1105f 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -120,6 +120,7 @@ static ssize_t xenbus_file_read(struct file *filp, int ret; mutex_lock(&u->reply_mutex); +again: while (list_empty(&u->read_buffers)) { mutex_unlock(&u->reply_mutex); if (filp->f_flags & O_NONBLOCK) @@ -158,6 +159,8 @@ static ssize_t xenbus_file_read(struct file *filp, struct read_buffer, list); } } + if (i == 0) + goto again; out: mutex_unlock(&u->reply_mutex); -- cgit v1.2.3 From 6a5b3beff916a19e7672f8c0330b4f82ed367be2 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Mon, 20 Dec 2010 14:56:09 -0800 Subject: xenbus: Fix memory leak on release Pending responses were leaked on close. Signed-off-by: Daniel De Graaf Signed-off-by: Jan Beulich Signed-off-by: Jeremy Fitzhardinge --- drivers/xen/xenfs/xenbus.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index 55791dd1105f..8f6c7d4b3e4d 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -543,6 +543,7 @@ static int xenbus_file_release(struct inode *inode, struct file *filp) struct xenbus_file_priv *u = filp->private_data; struct xenbus_transaction_holder *trans, *tmp; struct watch_adapter *watch, *tmp_watch; + struct read_buffer *rb, *tmp_rb; /* * No need for locking here because there are no other users, @@ -561,6 +562,10 @@ static int xenbus_file_release(struct inode *inode, struct file *filp) free_watch_adapter(watch); } + list_for_each_entry_safe(rb, tmp_rb, &u->read_buffers, list) { + list_del(&rb->list); + kfree(rb); + } kfree(u); return 0; -- cgit v1.2.3 From 7c63dedcc52c8c1253b1deec387102ef788ed0b4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 20 Dec 2010 15:00:17 -0800 Subject: msm: qsd8x50: Platform data isn't init data Remove the SMC91x platform and resource data from initdata. These will continue to be accessed after init, and must remain available. Signed-off-by: Stephen Boyd Signed-off-by: David Brown --- arch/arm/mach-msm/board-qsd8x50.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c index ed2af4ad97ed..39925c1a819d 100644 --- a/arch/arm/mach-msm/board-qsd8x50.c +++ b/arch/arm/mach-msm/board-qsd8x50.c @@ -42,7 +42,7 @@ static const unsigned qsd8x50_surf_smc91x_gpio __initdata = 156; * at run-time: they vary from board to board, and the true * configuration won't be known until boot. */ -static struct resource smc91x_resources[] __initdata = { +static struct resource smc91x_resources[] = { [0] = { .flags = IORESOURCE_MEM, }, @@ -51,7 +51,7 @@ static struct resource smc91x_resources[] __initdata = { }, }; -static struct platform_device smc91x_device __initdata = { +static struct platform_device smc91x_device = { .name = "smc91x", .id = 0, .num_resources = ARRAY_SIZE(smc91x_resources), -- cgit v1.2.3 From e159489baa717dbae70f9903770a6a4990865887 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 9 Jan 2011 23:32:15 +0100 Subject: workqueue: relax lockdep annotation on flush_work() Currently, the lockdep annotation in flush_work() requires exclusive access on the workqueue the target work is queued on and triggers warning if a work is trying to flush another work on the same workqueue; however, this is no longer true as workqueues can now execute multiple works concurrently. This patch adds lock_map_acquire_read() and make process_one_work() hold read access to the workqueue while executing a work and start_flush_work() check for write access if concurrnecy level is one or the workqueue has a rescuer (as only one execution resource - the rescuer - is guaranteed to be available under memory pressure), and read access if higher. This better represents what's going on and removes spurious lockdep warnings which are triggered by fake dependency chain created through flush_work(). * Peter pointed out that flushing another work from a WQ_MEM_RECLAIM wq breaks forward progress guarantee under memory pressure. Condition check accordingly updated. Signed-off-by: Tejun Heo Reported-by: "Rafael J. Wysocki" Tested-by: "Rafael J. Wysocki" Cc: Peter Zijlstra Cc: stable@kernel.org --- include/linux/lockdep.h | 3 +++ kernel/workqueue.c | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 71c09b26c759..9f19430c7d07 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -522,12 +522,15 @@ static inline void print_irqtrace_events(struct task_struct *curr) #ifdef CONFIG_DEBUG_LOCK_ALLOC # ifdef CONFIG_PROVE_LOCKING # define lock_map_acquire(l) lock_acquire(l, 0, 0, 0, 2, NULL, _THIS_IP_) +# define lock_map_acquire_read(l) lock_acquire(l, 0, 0, 2, 2, NULL, _THIS_IP_) # else # define lock_map_acquire(l) lock_acquire(l, 0, 0, 0, 1, NULL, _THIS_IP_) +# define lock_map_acquire_read(l) lock_acquire(l, 0, 0, 2, 1, NULL, _THIS_IP_) # endif # define lock_map_release(l) lock_release(l, 1, _THIS_IP_) #else # define lock_map_acquire(l) do { } while (0) +# define lock_map_acquire_read(l) do { } while (0) # define lock_map_release(l) do { } while (0) #endif diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 8ee6ec82f88a..930c2390b77e 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -1840,7 +1840,7 @@ __acquires(&gcwq->lock) spin_unlock_irq(&gcwq->lock); work_clear_pending(work); - lock_map_acquire(&cwq->wq->lockdep_map); + lock_map_acquire_read(&cwq->wq->lockdep_map); lock_map_acquire(&lockdep_map); trace_workqueue_execute_start(work); f(work); @@ -2384,8 +2384,18 @@ static bool start_flush_work(struct work_struct *work, struct wq_barrier *barr, insert_wq_barrier(cwq, barr, work, worker); spin_unlock_irq(&gcwq->lock); - lock_map_acquire(&cwq->wq->lockdep_map); + /* + * If @max_active is 1 or rescuer is in use, flushing another work + * item on the same workqueue may lead to deadlock. Make sure the + * flusher is not running on the same workqueue by verifying write + * access. + */ + if (cwq->wq->saved_max_active == 1 || cwq->wq->flags & WQ_RESCUER) + lock_map_acquire(&cwq->wq->lockdep_map); + else + lock_map_acquire_read(&cwq->wq->lockdep_map); lock_map_release(&cwq->wq->lockdep_map); + return true; already_gone: spin_unlock_irq(&gcwq->lock); -- cgit v1.2.3 From 42c025f3de9042d9c9abd9a6f6205d1a0f4bcadf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 11 Jan 2011 15:58:49 +0100 Subject: workqueue: note the nested NOT_RUNNING test in worker_clr_flags() isn't a noop The nested NOT_RUNNING test in worker_clr_flags() is slightly misleading in that if NOT_RUNNING were a single flag the nested test would be always %true and thus noop. Add a comment noting that the test isn't a noop. Signed-off-by: Tejun Heo Cc: Hillf Danton Cc: Andrew Morton --- kernel/workqueue.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 930c2390b77e..11869faa6819 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -768,7 +768,11 @@ static inline void worker_clr_flags(struct worker *worker, unsigned int flags) worker->flags &= ~flags; - /* if transitioning out of NOT_RUNNING, increment nr_running */ + /* + * If transitioning out of NOT_RUNNING, increment nr_running. Note + * that the nested NOT_RUNNING is not a noop. NOT_RUNNING is mask + * of multiple flags, not a single flag. + */ if ((flags & WORKER_NOT_RUNNING) && (oflags & WORKER_NOT_RUNNING)) if (!(worker->flags & WORKER_NOT_RUNNING)) atomic_inc(get_gcwq_nr_running(gcwq->cpu)); -- cgit v1.2.3 From 2485b6464cf86a5bc361666838f2439c99c00567 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Tue, 11 Jan 2011 18:54:53 +0100 Subject: x86,percpu: Move out of place 64 bit ops into X86_64 section Some operations that operate on 64 bit operands are defined for 32 bit. Move them into the correct section. Signed-off-by: Christoph Lameter Signed-off-by: Tejun Heo --- arch/x86/include/asm/percpu.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/percpu.h b/arch/x86/include/asm/percpu.h index 8ee45167e817..3788f4649db4 100644 --- a/arch/x86/include/asm/percpu.h +++ b/arch/x86/include/asm/percpu.h @@ -414,8 +414,6 @@ do { \ #define this_cpu_xchg_1(pcp, nval) percpu_xchg_op(pcp, nval) #define this_cpu_xchg_2(pcp, nval) percpu_xchg_op(pcp, nval) #define this_cpu_xchg_4(pcp, nval) percpu_xchg_op(pcp, nval) -#define this_cpu_xchg_8(pcp, nval) percpu_xchg_op(pcp, nval) -#define this_cpu_cmpxchg_8(pcp, oval, nval) percpu_cmpxchg_op(pcp, oval, nval) #define irqsafe_cpu_add_1(pcp, val) percpu_add_op((pcp), val) #define irqsafe_cpu_add_2(pcp, val) percpu_add_op((pcp), val) @@ -432,8 +430,6 @@ do { \ #define irqsafe_cpu_xchg_1(pcp, nval) percpu_xchg_op(pcp, nval) #define irqsafe_cpu_xchg_2(pcp, nval) percpu_xchg_op(pcp, nval) #define irqsafe_cpu_xchg_4(pcp, nval) percpu_xchg_op(pcp, nval) -#define irqsafe_cpu_xchg_8(pcp, nval) percpu_xchg_op(pcp, nval) -#define irqsafe_cpu_cmpxchg_8(pcp, oval, nval) percpu_cmpxchg_op(pcp, oval, nval) #ifndef CONFIG_M386 #define __this_cpu_add_return_1(pcp, val) percpu_add_return_op(pcp, val) @@ -475,11 +471,15 @@ do { \ #define this_cpu_or_8(pcp, val) percpu_to_op("or", (pcp), val) #define this_cpu_xor_8(pcp, val) percpu_to_op("xor", (pcp), val) #define this_cpu_add_return_8(pcp, val) percpu_add_return_op(pcp, val) +#define this_cpu_xchg_8(pcp, nval) percpu_xchg_op(pcp, nval) +#define this_cpu_cmpxchg_8(pcp, oval, nval) percpu_cmpxchg_op(pcp, oval, nval) #define irqsafe_cpu_add_8(pcp, val) percpu_add_op((pcp), val) #define irqsafe_cpu_and_8(pcp, val) percpu_to_op("and", (pcp), val) #define irqsafe_cpu_or_8(pcp, val) percpu_to_op("or", (pcp), val) #define irqsafe_cpu_xor_8(pcp, val) percpu_to_op("xor", (pcp), val) +#define irqsafe_cpu_xchg_8(pcp, nval) percpu_xchg_op(pcp, nval) +#define irqsafe_cpu_cmpxchg_8(pcp, oval, nval) percpu_cmpxchg_op(pcp, oval, nval) #endif /* This is not atomic against other CPUs -- CPU preemption needs to be off */ -- cgit v1.2.3 From f00c9e44ad1a9660fe8cd3ca15b6cd9497172eab Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 15 Sep 2010 17:38:58 +0200 Subject: quota: Fix deadlock during path resolution As Al Viro pointed out path resolution during Q_QUOTAON calls to quotactl is prone to deadlocks. We hold s_umount semaphore for reading during the path resolution and resolution itself may need to acquire the semaphore for writing when e. g. autofs mountpoint is passed. Solve the problem by performing the resolution before we get hold of the superblock (and thus s_umount semaphore). The whole thing is complicated by the fact that some filesystems (OCFS2) ignore the path argument. So to distinguish between filesystem which want the path and which do not we introduce new .quota_on_meta callback which does not get the path. OCFS2 then uses this callback instead of old .quota_on. CC: Al Viro CC: Christoph Hellwig CC: Ted Ts'o CC: Joel Becker Signed-off-by: Jan Kara --- fs/ext3/super.c | 25 +++++++------------------ fs/ext4/super.c | 25 +++++++------------------ fs/ocfs2/super.c | 5 ++--- fs/quota/dquot.c | 18 ++---------------- fs/quota/quota.c | 41 +++++++++++++++++++++++++++-------------- fs/reiserfs/super.c | 17 ++++++----------- include/linux/quota.h | 5 ++++- include/linux/quotaops.h | 4 +--- 8 files changed, 56 insertions(+), 84 deletions(-) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index b7d0554631e4..0e0d391626be 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -755,7 +755,7 @@ static int ext3_release_dquot(struct dquot *dquot); static int ext3_mark_dquot_dirty(struct dquot *dquot); static int ext3_write_info(struct super_block *sb, int type); static int ext3_quota_on(struct super_block *sb, int type, int format_id, - char *path); + struct path *path); static int ext3_quota_on_mount(struct super_block *sb, int type); static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off); @@ -2885,27 +2885,20 @@ static int ext3_quota_on_mount(struct super_block *sb, int type) * Standard function to be called on quota_on */ static int ext3_quota_on(struct super_block *sb, int type, int format_id, - char *name) + struct path *path) { int err; - struct path path; if (!test_opt(sb, QUOTA)) return -EINVAL; - err = kern_path(name, LOOKUP_FOLLOW, &path); - if (err) - return err; - /* Quotafile not on the same filesystem? */ - if (path.mnt->mnt_sb != sb) { - path_put(&path); + if (path->mnt->mnt_sb != sb) return -EXDEV; - } /* Journaling quota? */ if (EXT3_SB(sb)->s_qf_names[type]) { /* Quotafile not of fs root? */ - if (path.dentry->d_parent != sb->s_root) + if (path->dentry->d_parent != sb->s_root) ext3_msg(sb, KERN_WARNING, "warning: Quota file not on filesystem root. " "Journaled quota will not work."); @@ -2915,7 +2908,7 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, * When we journal data on quota file, we have to flush journal to see * all updates to the file when we bypass pagecache... */ - if (ext3_should_journal_data(path.dentry->d_inode)) { + if (ext3_should_journal_data(path->dentry->d_inode)) { /* * We don't need to lock updates but journal_flush() could * otherwise be livelocked... @@ -2923,15 +2916,11 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, journal_lock_updates(EXT3_SB(sb)->s_journal); err = journal_flush(EXT3_SB(sb)->s_journal); journal_unlock_updates(EXT3_SB(sb)->s_journal); - if (err) { - path_put(&path); + if (err) return err; - } } - err = dquot_quota_on_path(sb, type, format_id, &path); - path_put(&path); - return err; + return dquot_quota_on(sb, type, format_id, path); } /* Read data from quotafile - avoid pagecache and such because we cannot afford diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 29c80f6d8b27..0f10ccd6bfc0 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1162,7 +1162,7 @@ static int ext4_release_dquot(struct dquot *dquot); static int ext4_mark_dquot_dirty(struct dquot *dquot); static int ext4_write_info(struct super_block *sb, int type); static int ext4_quota_on(struct super_block *sb, int type, int format_id, - char *path); + struct path *path); static int ext4_quota_off(struct super_block *sb, int type); static int ext4_quota_on_mount(struct super_block *sb, int type); static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data, @@ -4566,27 +4566,20 @@ static int ext4_quota_on_mount(struct super_block *sb, int type) * Standard function to be called on quota_on */ static int ext4_quota_on(struct super_block *sb, int type, int format_id, - char *name) + struct path *path) { int err; - struct path path; if (!test_opt(sb, QUOTA)) return -EINVAL; - err = kern_path(name, LOOKUP_FOLLOW, &path); - if (err) - return err; - /* Quotafile not on the same filesystem? */ - if (path.mnt->mnt_sb != sb) { - path_put(&path); + if (path->mnt->mnt_sb != sb) return -EXDEV; - } /* Journaling quota? */ if (EXT4_SB(sb)->s_qf_names[type]) { /* Quotafile not in fs root? */ - if (path.dentry->d_parent != sb->s_root) + if (path->dentry->d_parent != sb->s_root) ext4_msg(sb, KERN_WARNING, "Quota file not on filesystem root. " "Journaled quota will not work"); @@ -4597,7 +4590,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, * all updates to the file when we bypass pagecache... */ if (EXT4_SB(sb)->s_journal && - ext4_should_journal_data(path.dentry->d_inode)) { + ext4_should_journal_data(path->dentry->d_inode)) { /* * We don't need to lock updates but journal_flush() could * otherwise be livelocked... @@ -4605,15 +4598,11 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); err = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); - if (err) { - path_put(&path); + if (err) return err; - } } - err = dquot_quota_on_path(sb, type, format_id, &path); - path_put(&path); - return err; + return dquot_quota_on(sb, type, format_id, path); } static int ext4_quota_off(struct super_block *sb, int type) diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 17ff46fa8a10..31c3ffd2f8d0 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -993,8 +993,7 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb) } /* Handle quota on quotactl */ -static int ocfs2_quota_on(struct super_block *sb, int type, int format_id, - char *path) +static int ocfs2_quota_on(struct super_block *sb, int type, int format_id) { unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; @@ -1013,7 +1012,7 @@ static int ocfs2_quota_off(struct super_block *sb, int type) } static const struct quotactl_ops ocfs2_quotactl_ops = { - .quota_on = ocfs2_quota_on, + .quota_on_meta = ocfs2_quota_on, .quota_off = ocfs2_quota_off, .quota_sync = dquot_quota_sync, .get_info = dquot_get_dqinfo, diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 84becd3e4772..a2a622e079f0 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2189,8 +2189,8 @@ int dquot_resume(struct super_block *sb, int type) } EXPORT_SYMBOL(dquot_resume); -int dquot_quota_on_path(struct super_block *sb, int type, int format_id, - struct path *path) +int dquot_quota_on(struct super_block *sb, int type, int format_id, + struct path *path) { int error = security_quota_on(path->dentry); if (error) @@ -2204,20 +2204,6 @@ int dquot_quota_on_path(struct super_block *sb, int type, int format_id, DQUOT_LIMITS_ENABLED); return error; } -EXPORT_SYMBOL(dquot_quota_on_path); - -int dquot_quota_on(struct super_block *sb, int type, int format_id, char *name) -{ - struct path path; - int error; - - error = kern_path(name, LOOKUP_FOLLOW, &path); - if (!error) { - error = dquot_quota_on_path(sb, type, format_id, &path); - path_put(&path); - } - return error; -} EXPORT_SYMBOL(dquot_quota_on); /* diff --git a/fs/quota/quota.c b/fs/quota/quota.c index b299961e1edb..b34bdb25490c 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -64,18 +64,15 @@ static int quota_sync_all(int type) } static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id, - void __user *addr) + struct path *path) { - char *pathname; - int ret = -ENOSYS; - - pathname = getname(addr); - if (IS_ERR(pathname)) - return PTR_ERR(pathname); - if (sb->s_qcop->quota_on) - ret = sb->s_qcop->quota_on(sb, type, id, pathname); - putname(pathname); - return ret; + if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_on_meta) + return -ENOSYS; + if (sb->s_qcop->quota_on_meta) + return sb->s_qcop->quota_on_meta(sb, type, id); + if (IS_ERR(path)) + return PTR_ERR(path); + return sb->s_qcop->quota_on(sb, type, id, path); } static int quota_getfmt(struct super_block *sb, int type, void __user *addr) @@ -241,7 +238,7 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id, /* Copy parameters and call proper function */ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, - void __user *addr) + void __user *addr, struct path *path) { int ret; @@ -256,7 +253,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, switch (cmd) { case Q_QUOTAON: - return quota_quotaon(sb, type, cmd, id, addr); + return quota_quotaon(sb, type, cmd, id, path); case Q_QUOTAOFF: if (!sb->s_qcop->quota_off) return -ENOSYS; @@ -335,6 +332,7 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, { uint cmds, type; struct super_block *sb = NULL; + struct path path, *pathp = NULL; int ret; cmds = cmd >> SUBCMDSHIFT; @@ -351,12 +349,27 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, return -ENODEV; } + /* + * Path for quotaon has to be resolved before grabbing superblock + * because that gets s_umount sem which is also possibly needed by path + * resolution (think about autofs) and thus deadlocks could arise. + */ + if (cmds == Q_QUOTAON) { + ret = user_path_at(AT_FDCWD, addr, LOOKUP_FOLLOW, &path); + if (ret) + pathp = ERR_PTR(ret); + else + pathp = &path; + } + sb = quotactl_block(special); if (IS_ERR(sb)) return PTR_ERR(sb); - ret = do_quotactl(sb, type, cmds, id, addr); + ret = do_quotactl(sb, type, cmds, id, addr, pathp); drop_super(sb); + if (pathp && !IS_ERR(pathp)) + path_put(pathp); return ret; } diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 2575682a9ead..0aab04f46827 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -632,7 +632,7 @@ static int reiserfs_acquire_dquot(struct dquot *); static int reiserfs_release_dquot(struct dquot *); static int reiserfs_mark_dquot_dirty(struct dquot *); static int reiserfs_write_info(struct super_block *, int); -static int reiserfs_quota_on(struct super_block *, int, int, char *); +static int reiserfs_quota_on(struct super_block *, int, int, struct path *); static const struct dquot_operations reiserfs_quota_operations = { .write_dquot = reiserfs_write_dquot, @@ -2048,25 +2048,21 @@ static int reiserfs_quota_on_mount(struct super_block *sb, int type) * Standard function to be called on quota_on */ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, - char *name) + struct path *path) { int err; - struct path path; struct inode *inode; struct reiserfs_transaction_handle th; if (!(REISERFS_SB(sb)->s_mount_opt & (1 << REISERFS_QUOTA))) return -EINVAL; - err = kern_path(name, LOOKUP_FOLLOW, &path); - if (err) - return err; /* Quotafile not on the same filesystem? */ - if (path.mnt->mnt_sb != sb) { + if (path->mnt->mnt_sb != sb) { err = -EXDEV; goto out; } - inode = path.dentry->d_inode; + inode = path->dentry->d_inode; /* We must not pack tails for quota files on reiserfs for quota IO to work */ if (!(REISERFS_I(inode)->i_flags & i_nopack_mask)) { err = reiserfs_unpack(inode, NULL); @@ -2082,7 +2078,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, /* Journaling quota? */ if (REISERFS_SB(sb)->s_qf_names[type]) { /* Quotafile not of fs root? */ - if (path.dentry->d_parent != sb->s_root) + if (path->dentry->d_parent != sb->s_root) reiserfs_warning(sb, "super-6521", "Quota file not on filesystem root. " "Journalled quota will not work."); @@ -2101,9 +2097,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, if (err) goto out; } - err = dquot_quota_on_path(sb, type, format_id, &path); + err = dquot_quota_on(sb, type, format_id, path); out: - path_put(&path); return err; } diff --git a/include/linux/quota.h b/include/linux/quota.h index 94c1f03b50eb..9a85412e0db6 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -322,9 +322,12 @@ struct dquot_operations { qsize_t *(*get_reserved_space) (struct inode *); }; +struct path; + /* Operations handling requests from userspace */ struct quotactl_ops { - int (*quota_on)(struct super_block *, int, int, char *); + int (*quota_on)(struct super_block *, int, int, struct path *); + int (*quota_on_meta)(struct super_block *, int, int); int (*quota_off)(struct super_block *, int); int (*quota_sync)(struct super_block *, int, int); int (*get_info)(struct super_block *, int, struct if_dqinfo *); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 223b14cd129c..eb354f6f26b3 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -76,11 +76,9 @@ int dquot_mark_dquot_dirty(struct dquot *dquot); int dquot_file_open(struct inode *inode, struct file *file); -int dquot_quota_on(struct super_block *sb, int type, int format_id, - char *path); int dquot_enable(struct inode *inode, int type, int format_id, unsigned int flags); -int dquot_quota_on_path(struct super_block *sb, int type, int format_id, +int dquot_quota_on(struct super_block *sb, int type, int format_id, struct path *path); int dquot_quota_on_mount(struct super_block *sb, char *qf_name, int format_id, int type); -- cgit v1.2.3 From 86985db66ea2fda174615be05112a7d1b13645c4 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Wed, 3 Nov 2010 17:35:31 +0800 Subject: powerpc/85xx: add e500 HID1 bit definition Also make 74xx HID1 definition conditional. Signed-off-by: Li Yang Signed-off-by: Shaohui Xie Cc: Roy Zang Cc: Alexandre Bounine Signed-off-by: Kumar Gala --- arch/powerpc/include/asm/reg.h | 2 ++ arch/powerpc/include/asm/reg_booke.h | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index ff0005eec7dd..125fc1ad665d 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -283,6 +283,7 @@ #define HID0_NOPTI (1<<0) /* No-op dcbt and dcbst instr. */ #define SPRN_HID1 0x3F1 /* Hardware Implementation Register 1 */ +#ifdef CONFIG_6xx #define HID1_EMCP (1<<31) /* 7450 Machine Check Pin Enable */ #define HID1_DFS (1<<22) /* 7447A Dynamic Frequency Scaling */ #define HID1_PC0 (1<<16) /* 7450 PLL_CFG[0] */ @@ -292,6 +293,7 @@ #define HID1_SYNCBE (1<<11) /* 7450 ABE for sync, eieio */ #define HID1_ABE (1<<10) /* 7450 Address Broadcast Enable */ #define HID1_PS (1<<16) /* 750FX PLL selection */ +#endif #define SPRN_HID2 0x3F8 /* Hardware Implementation Register 2 */ #define SPRN_HID2_GEKKO 0x398 /* Gekko HID2 Register */ #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index 667a498eaee1..e68c69bf741a 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -246,6 +246,20 @@ store or cache line push */ #endif +/* Bit definitions for the HID1 */ +#ifdef CONFIG_E500 +/* e500v1/v2 */ +#define HID1_PLL_CFG_MASK 0xfc000000 /* PLL_CFG input pins */ +#define HID1_RFXE 0x00020000 /* Read fault exception enable */ +#define HID1_R1DPE 0x00008000 /* R1 data bus parity enable */ +#define HID1_R2DPE 0x00004000 /* R2 data bus parity enable */ +#define HID1_ASTME 0x00002000 /* Address bus streaming mode enable */ +#define HID1_ABE 0x00001000 /* Address broadcast enable */ +#define HID1_MPXTT 0x00000400 /* MPX re-map transfer type */ +#define HID1_ATS 0x00000080 /* Atomic status */ +#define HID1_MID_MASK 0x0000000f /* MID input pins */ +#endif + /* Bit definitions for the DBSR. */ /* * DBSR bits which have conflicting definitions on true Book E versus IBM 40x. -- cgit v1.2.3 From b5fb0cc7f1c90e3b00d40b64681efcbf8bcdeb9e Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Wed, 3 Nov 2010 17:36:37 +0800 Subject: powerpc/fsl_rio: Fix non-standard HID1 register access Moved setting of RFXE bit so we get machine checks on RIO errors into cpu_setup so that the RIO code isn't core specific. Signed-off-by: Shaohui Xie Cc: Li Yang Cc: Roy Zang Cc: Alexandre Bounine Signed-off-by: Kumar Gala --- arch/powerpc/kernel/cpu_setup_fsl_booke.S | 6 ++++++ arch/powerpc/sysdev/fsl_rio.c | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/cpu_setup_fsl_booke.S b/arch/powerpc/kernel/cpu_setup_fsl_booke.S index 894e64fa481e..5c518ad3445c 100644 --- a/arch/powerpc/kernel/cpu_setup_fsl_booke.S +++ b/arch/powerpc/kernel/cpu_setup_fsl_booke.S @@ -64,6 +64,12 @@ _GLOBAL(__setup_cpu_e500v2) bl __e500_icache_setup bl __e500_dcache_setup bl __setup_e500_ivors +#ifdef CONFIG_RAPIDIO + /* Ensure that RFXE is set */ + mfspr r3,SPRN_HID1 + oris r3,r3,HID1_RFXE@h + mtspr SPRN_HID1,r3 +#endif mtlr r4 blr _GLOBAL(__setup_cpu_e500mc) diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index 9725369d432a..4c518d17ee9b 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -1556,8 +1556,6 @@ int fsl_rio_setup(struct platform_device *dev) saved_mcheck_exception = ppc_md.machine_check_exception; ppc_md.machine_check_exception = fsl_rio_mcheck_exception; #endif - /* Ensure that RFXE is set */ - mtspr(SPRN_HID1, (mfspr(SPRN_HID1) | 0x20000)); return 0; err: -- cgit v1.2.3 From fd066e850351e21d1b385cde35fadf66761bc053 Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Wed, 27 Oct 2010 02:02:36 +0200 Subject: powerpc/mpc8308: fix USB DR controller initialization MPC8308 has ULPI pin muxing settings in SICRH register, bits 17-18 which is different from both MPC8313 and MPC8315. Also MPC8308 doesn't have REFSEL, UTMI_PHY_EN and OTG_PORT fields in the USB DR controller CONTROL register. Signed-off-by: Ilya Yanok Tested-by: Wolfgang Denk Acked-by: Wolfgang Denk Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc8308rdb.dts | 2 +- arch/powerpc/platforms/83xx/mpc83xx.h | 2 ++ arch/powerpc/platforms/83xx/usb.c | 21 ++++++++++++++++----- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/boot/dts/mpc8308rdb.dts b/arch/powerpc/boot/dts/mpc8308rdb.dts index a97eb2db5a18..1e2b88899443 100644 --- a/arch/powerpc/boot/dts/mpc8308rdb.dts +++ b/arch/powerpc/boot/dts/mpc8308rdb.dts @@ -109,7 +109,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; - compatible = "fsl,mpc8315-immr", "simple-bus"; + compatible = "fsl,mpc8308-immr", "simple-bus"; ranges = <0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 0fea8811d45b..82a434510d83 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -35,6 +35,8 @@ /* system i/o configuration register high */ #define MPC83XX_SICRH_OFFS 0x118 +#define MPC8308_SICRH_USB_MASK 0x000c0000 +#define MPC8308_SICRH_USB_ULPI 0x00040000 #define MPC834X_SICRH_USB_UTMI 0x00020000 #define MPC831X_SICRH_USB_MASK 0x000000e0 #define MPC831X_SICRH_USB_ULPI 0x000000a0 diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c index 3ba4bb7d41bb..2c64164722d0 100644 --- a/arch/powerpc/platforms/83xx/usb.c +++ b/arch/powerpc/platforms/83xx/usb.c @@ -127,7 +127,8 @@ int mpc831x_usb_cfg(void) /* Configure clock */ immr_node = of_get_parent(np); - if (immr_node && of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) + if (immr_node && (of_device_is_compatible(immr_node, "fsl,mpc8315-immr") || + of_device_is_compatible(immr_node, "fsl,mpc8308-immr"))) clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, MPC8315_SCCR_USB_MASK, MPC8315_SCCR_USB_DRCM_01); @@ -138,7 +139,11 @@ int mpc831x_usb_cfg(void) /* Configure pin mux for ULPI. There is no pin mux for UTMI */ if (prop && !strcmp(prop, "ulpi")) { - if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) { + if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { + clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, + MPC8308_SICRH_USB_MASK, + MPC8308_SICRH_USB_ULPI); + } else if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) { clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, MPC8315_SICRL_USB_MASK, MPC8315_SICRL_USB_ULPI); @@ -173,6 +178,9 @@ int mpc831x_usb_cfg(void) !strcmp(prop, "utmi"))) { u32 refsel; + if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) + goto out; + if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) refsel = CONTROL_REFSEL_24MHZ; else @@ -186,9 +194,11 @@ int mpc831x_usb_cfg(void) temp = CONTROL_PHY_CLK_SEL_ULPI; #ifdef CONFIG_USB_OTG /* Set OTG_PORT */ - dr_mode = of_get_property(np, "dr_mode", NULL); - if (dr_mode && !strcmp(dr_mode, "otg")) - temp |= CONTROL_OTG_PORT; + if (!of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) + temp |= CONTROL_OTG_PORT; + } #endif /* CONFIG_USB_OTG */ out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, temp); } else { @@ -196,6 +206,7 @@ int mpc831x_usb_cfg(void) ret = -EINVAL; } +out: iounmap(usb_regs); of_node_put(np); return ret; -- cgit v1.2.3 From b2e0861e51f2961954330dcafe6d148ee3ab5cff Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 3 Dec 2010 10:52:14 -0600 Subject: powerpc/85xx: fix compatible properties of the P1022DS DMA nodes used for audio In order to prevent the fsl_dma driver from claiming the DMA channels that the P1022DS audio driver needs, the compatible properties for those nodes must say "fsl,ssi-dma-channel" instead of "fsl,eloplus-dma-channel". Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/p1022ds.dts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/boot/dts/p1022ds.dts b/arch/powerpc/boot/dts/p1022ds.dts index 2bbecbb4cbf9..69422eb24d97 100644 --- a/arch/powerpc/boot/dts/p1022ds.dts +++ b/arch/powerpc/boot/dts/p1022ds.dts @@ -291,13 +291,13 @@ ranges = <0x0 0xc100 0x200>; cell-index = <1>; dma00: dma-channel@0 { - compatible = "fsl,eloplus-dma-channel"; + compatible = "fsl,ssi-dma-channel"; reg = <0x0 0x80>; cell-index = <0>; interrupts = <76 2>; }; dma01: dma-channel@80 { - compatible = "fsl,eloplus-dma-channel"; + compatible = "fsl,ssi-dma-channel"; reg = <0x80 0x80>; cell-index = <1>; interrupts = <77 2>; -- cgit v1.2.3 From b49d81ded47e9d01f7128fce50d224ccc2150960 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 14 Oct 2010 15:15:30 -0500 Subject: powerpc: fix warning when compiling immap_qe.h Fix the warnings genereted by arch/powerpc/include/asm/immap_qe.h when CONFIG_PHYS_ADDR_T_64BIT is defined: immap_qe.h: In function 'immrbar_virt_to_phys': immap_qe.h:472:8: warning: cast from pointer to integer of different size immap_qe.h:472:24: warning: cast from pointer to integer of different size immap_qe.h:473:5: warning: cast from pointer to integer of different size immap_qe.h:473:21: warning: cast from pointer to integer of different size immap_qe.h:474:36: warning: cast from pointer to integer of different size Note that the QE does not support 36-bit physical addresses, so even when CONFIG_PHYS_ADDR_T_64BIT is defined, the QE MURAM must be located below the 4GB boundary. Signed-off-by: Timur Tabi Signed-off-by: Kumar Gala --- arch/powerpc/include/asm/immap_qe.h | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/immap_qe.h b/arch/powerpc/include/asm/immap_qe.h index 4e10f508570a..0edb6842b13d 100644 --- a/arch/powerpc/include/asm/immap_qe.h +++ b/arch/powerpc/include/asm/immap_qe.h @@ -467,13 +467,22 @@ struct qe_immap { extern struct qe_immap __iomem *qe_immr; extern phys_addr_t get_qe_base(void); -static inline unsigned long immrbar_virt_to_phys(void *address) +/* + * Returns the offset within the QE address space of the given pointer. + * + * Note that the QE does not support 36-bit physical addresses, so if + * get_qe_base() returns a number above 4GB, the caller will probably fail. + */ +static inline phys_addr_t immrbar_virt_to_phys(void *address) { - if ( ((u32)address >= (u32)qe_immr) && - ((u32)address < ((u32)qe_immr + QE_IMMAP_SIZE)) ) - return (unsigned long)(address - (u32)qe_immr + - (u32)get_qe_base()); - return (unsigned long)virt_to_phys(address); + void *q = (void *)qe_immr; + + /* Is it a MURAM address? */ + if ((address >= q) && (address < (q + QE_IMMAP_SIZE))) + return get_qe_base() + (address - q); + + /* It's an address returned by kmalloc */ + return virt_to_phys(address); } #endif /* __KERNEL__ */ -- cgit v1.2.3 From 728674a7e466628df2aeec6d11a2ae1ef968fb67 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Jan 2011 12:03:00 -0800 Subject: tty: move hvc drivers to drivers/tty/hvc/ As requested by Arnd Bergmann, the hvc drivers are now moved to the drivers/tty/hvc/ directory. The virtio_console.c driver was also moved, as it required the hvc_console.h file to be able to be built, and it really is a hvc driver. Cc: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/Makefile | 13 - drivers/char/hvc_beat.c | 134 --- drivers/char/hvc_console.c | 914 ------------------- drivers/char/hvc_console.h | 119 --- drivers/char/hvc_dcc.c | 133 --- drivers/char/hvc_irq.c | 49 - drivers/char/hvc_iseries.c | 598 ------------- drivers/char/hvc_iucv.c | 1337 --------------------------- drivers/char/hvc_rtas.c | 133 --- drivers/char/hvc_tile.c | 68 -- drivers/char/hvc_udbg.c | 96 -- drivers/char/hvc_vio.c | 173 ---- drivers/char/hvc_xen.c | 271 ------ drivers/char/hvcs.c | 1604 --------------------------------- drivers/char/hvsi.c | 1314 --------------------------- drivers/char/virtio_console.c | 1838 -------------------------------------- drivers/tty/Makefile | 1 + drivers/tty/hvc/Makefile | 13 + drivers/tty/hvc/hvc_beat.c | 134 +++ drivers/tty/hvc/hvc_console.c | 914 +++++++++++++++++++ drivers/tty/hvc/hvc_console.h | 119 +++ drivers/tty/hvc/hvc_dcc.c | 133 +++ drivers/tty/hvc/hvc_irq.c | 49 + drivers/tty/hvc/hvc_iseries.c | 598 +++++++++++++ drivers/tty/hvc/hvc_iucv.c | 1337 +++++++++++++++++++++++++++ drivers/tty/hvc/hvc_rtas.c | 133 +++ drivers/tty/hvc/hvc_tile.c | 68 ++ drivers/tty/hvc/hvc_udbg.c | 96 ++ drivers/tty/hvc/hvc_vio.c | 173 ++++ drivers/tty/hvc/hvc_xen.c | 271 ++++++ drivers/tty/hvc/hvcs.c | 1604 +++++++++++++++++++++++++++++++++ drivers/tty/hvc/hvsi.c | 1314 +++++++++++++++++++++++++++ drivers/tty/hvc/virtio_console.c | 1838 ++++++++++++++++++++++++++++++++++++++ 33 files changed, 8795 insertions(+), 8794 deletions(-) delete mode 100644 drivers/char/hvc_beat.c delete mode 100644 drivers/char/hvc_console.c delete mode 100644 drivers/char/hvc_console.h delete mode 100644 drivers/char/hvc_dcc.c delete mode 100644 drivers/char/hvc_irq.c delete mode 100644 drivers/char/hvc_iseries.c delete mode 100644 drivers/char/hvc_iucv.c delete mode 100644 drivers/char/hvc_rtas.c delete mode 100644 drivers/char/hvc_tile.c delete mode 100644 drivers/char/hvc_udbg.c delete mode 100644 drivers/char/hvc_vio.c delete mode 100644 drivers/char/hvc_xen.c delete mode 100644 drivers/char/hvcs.c delete mode 100644 drivers/char/hvsi.c delete mode 100644 drivers/char/virtio_console.c create mode 100644 drivers/tty/hvc/Makefile create mode 100644 drivers/tty/hvc/hvc_beat.c create mode 100644 drivers/tty/hvc/hvc_console.c create mode 100644 drivers/tty/hvc/hvc_console.h create mode 100644 drivers/tty/hvc/hvc_dcc.c create mode 100644 drivers/tty/hvc/hvc_irq.c create mode 100644 drivers/tty/hvc/hvc_iseries.c create mode 100644 drivers/tty/hvc/hvc_iucv.c create mode 100644 drivers/tty/hvc/hvc_rtas.c create mode 100644 drivers/tty/hvc/hvc_tile.c create mode 100644 drivers/tty/hvc/hvc_udbg.c create mode 100644 drivers/tty/hvc/hvc_vio.c create mode 100644 drivers/tty/hvc/hvc_xen.c create mode 100644 drivers/tty/hvc/hvcs.c create mode 100644 drivers/tty/hvc/hvsi.c create mode 100644 drivers/tty/hvc/virtio_console.c diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 1e9dffb33778..5bc765d4c3ca 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -30,25 +30,12 @@ obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o -obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o -obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o -obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o -obj-$(CONFIG_HVC_TILE) += hvc_tile.o -obj-$(CONFIG_HVC_DCC) += hvc_dcc.o -obj-$(CONFIG_HVC_BEAT) += hvc_beat.o -obj-$(CONFIG_HVC_DRIVER) += hvc_console.o -obj-$(CONFIG_HVC_IRQ) += hvc_irq.o -obj-$(CONFIG_HVC_XEN) += hvc_xen.o -obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o -obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o -obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_VIOTAPE) += viotape.o -obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_IBM_BSR) += bsr.o obj-$(CONFIG_SGI_MBCS) += mbcs.o obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c deleted file mode 100644 index 5fe4631e2a61..000000000000 --- a/drivers/char/hvc_beat.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Beat hypervisor console driver - * - * (C) Copyright 2006 TOSHIBA CORPORATION - * - * This code is based on drivers/char/hvc_rtas.c: - * (C) Copyright IBM Corporation 2001-2005 - * (C) Copyright Red Hat, Inc. 2005 - * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hvc_console.h" - -extern int64_t beat_get_term_char(uint64_t, uint64_t *, uint64_t *, uint64_t *); -extern int64_t beat_put_term_char(uint64_t, uint64_t, uint64_t, uint64_t); - -struct hvc_struct *hvc_beat_dev = NULL; - -/* bug: only one queue is available regardless of vtermno */ -static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) -{ - static unsigned char q[sizeof(unsigned long) * 2] - __attribute__((aligned(sizeof(unsigned long)))); - static int qlen = 0; - u64 got; - -again: - if (qlen) { - if (qlen > cnt) { - memcpy(buf, q, cnt); - qlen -= cnt; - memmove(q + cnt, q, qlen); - return cnt; - } else { /* qlen <= cnt */ - int r; - - memcpy(buf, q, qlen); - r = qlen; - qlen = 0; - return r; - } - } - if (beat_get_term_char(vtermno, &got, - ((u64 *)q), ((u64 *)q) + 1) == 0) { - qlen = got; - goto again; - } - return 0; -} - -static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) -{ - unsigned long kb[2]; - int rest, nlen; - - for (rest = cnt; rest > 0; rest -= nlen) { - nlen = (rest > 16) ? 16 : rest; - memcpy(kb, buf, nlen); - beat_put_term_char(vtermno, nlen, kb[0], kb[1]); - buf += nlen; - } - return cnt; -} - -static const struct hv_ops hvc_beat_get_put_ops = { - .get_chars = hvc_beat_get_chars, - .put_chars = hvc_beat_put_chars, -}; - -static int hvc_beat_useit = 1; - -static int hvc_beat_config(char *p) -{ - hvc_beat_useit = simple_strtoul(p, NULL, 0); - return 0; -} - -static int __init hvc_beat_console_init(void) -{ - if (hvc_beat_useit && of_machine_is_compatible("Beat")) { - hvc_instantiate(0, 0, &hvc_beat_get_put_ops); - } - return 0; -} - -/* temp */ -static int __init hvc_beat_init(void) -{ - struct hvc_struct *hp; - - if (!firmware_has_feature(FW_FEATURE_BEAT)) - return -ENODEV; - - hp = hvc_alloc(0, NO_IRQ, &hvc_beat_get_put_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - hvc_beat_dev = hp; - return 0; -} - -static void __exit hvc_beat_exit(void) -{ - if (hvc_beat_dev) - hvc_remove(hvc_beat_dev); -} - -module_init(hvc_beat_init); -module_exit(hvc_beat_exit); - -__setup("hvc_beat=", hvc_beat_config); - -console_initcall(hvc_beat_console_init); diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c deleted file mode 100644 index e9cba13ee800..000000000000 --- a/drivers/char/hvc_console.c +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright (C) 2001 Anton Blanchard , IBM - * Copyright (C) 2001 Paul Mackerras , IBM - * Copyright (C) 2004 Benjamin Herrenschmidt , IBM Corp. - * Copyright (C) 2004 IBM Corporation - * - * Additional Author(s): - * Ryan S. Arnold - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "hvc_console.h" - -#define HVC_MAJOR 229 -#define HVC_MINOR 0 - -/* - * Wait this long per iteration while trying to push buffered data to the - * hypervisor before allowing the tty to complete a close operation. - */ -#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ - -/* - * These sizes are most efficient for vio, because they are the - * native transfer size. We could make them selectable in the - * future to better deal with backends that want other buffer sizes. - */ -#define N_OUTBUF 16 -#define N_INBUF 16 - -#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) - -static struct tty_driver *hvc_driver; -static struct task_struct *hvc_task; - -/* Picks up late kicks after list walk but before schedule() */ -static int hvc_kicked; - -static int hvc_init(void); - -#ifdef CONFIG_MAGIC_SYSRQ -static int sysrq_pressed; -#endif - -/* dynamic list of hvc_struct instances */ -static LIST_HEAD(hvc_structs); - -/* - * Protect the list of hvc_struct instances from inserts and removals during - * list traversal. - */ -static DEFINE_SPINLOCK(hvc_structs_lock); - -/* - * This value is used to assign a tty->index value to a hvc_struct based - * upon order of exposure via hvc_probe(), when we can not match it to - * a console candidate registered with hvc_instantiate(). - */ -static int last_hvc = -1; - -/* - * Do not call this function with either the hvc_structs_lock or the hvc_struct - * lock held. If successful, this function increments the kref reference - * count against the target hvc_struct so it should be released when finished. - */ -static struct hvc_struct *hvc_get_by_index(int index) -{ - struct hvc_struct *hp; - unsigned long flags; - - spin_lock(&hvc_structs_lock); - - list_for_each_entry(hp, &hvc_structs, next) { - spin_lock_irqsave(&hp->lock, flags); - if (hp->index == index) { - kref_get(&hp->kref); - spin_unlock_irqrestore(&hp->lock, flags); - spin_unlock(&hvc_structs_lock); - return hp; - } - spin_unlock_irqrestore(&hp->lock, flags); - } - hp = NULL; - - spin_unlock(&hvc_structs_lock); - return hp; -} - - -/* - * Initial console vtermnos for console API usage prior to full console - * initialization. Any vty adapter outside this range will not have usable - * console interfaces but can still be used as a tty device. This has to be - * static because kmalloc will not work during early console init. - */ -static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; -static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] = - {[0 ... MAX_NR_HVC_CONSOLES - 1] = -1}; - -/* - * Console APIs, NOT TTY. These APIs are available immediately when - * hvc_console_setup() finds adapters. - */ - -static void hvc_console_print(struct console *co, const char *b, - unsigned count) -{ - char c[N_OUTBUF] __ALIGNED__; - unsigned i = 0, n = 0; - int r, donecr = 0, index = co->index; - - /* Console access attempt outside of acceptable console range. */ - if (index >= MAX_NR_HVC_CONSOLES) - return; - - /* This console adapter was removed so it is not usable. */ - if (vtermnos[index] == -1) - return; - - while (count > 0 || i > 0) { - if (count > 0 && i < sizeof(c)) { - if (b[n] == '\n' && !donecr) { - c[i++] = '\r'; - donecr = 1; - } else { - c[i++] = b[n++]; - donecr = 0; - --count; - } - } else { - r = cons_ops[index]->put_chars(vtermnos[index], c, i); - if (r <= 0) { - /* throw away chars on error */ - i = 0; - } else if (r > 0) { - i -= r; - if (i > 0) - memmove(c, c+r, i); - } - } - } -} - -static struct tty_driver *hvc_console_device(struct console *c, int *index) -{ - if (vtermnos[c->index] == -1) - return NULL; - - *index = c->index; - return hvc_driver; -} - -static int __init hvc_console_setup(struct console *co, char *options) -{ - if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) - return -ENODEV; - - if (vtermnos[co->index] == -1) - return -ENODEV; - - return 0; -} - -static struct console hvc_console = { - .name = "hvc", - .write = hvc_console_print, - .device = hvc_console_device, - .setup = hvc_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/* - * Early console initialization. Precedes driver initialization. - * - * (1) we are first, and the user specified another driver - * -- index will remain -1 - * (2) we are first and the user specified no driver - * -- index will be set to 0, then we will fail setup. - * (3) we are first and the user specified our driver - * -- index will be set to user specified driver, and we will fail - * (4) we are after driver, and this initcall will register us - * -- if the user didn't specify a driver then the console will match - * - * Note that for cases 2 and 3, we will match later when the io driver - * calls hvc_instantiate() and call register again. - */ -static int __init hvc_console_init(void) -{ - register_console(&hvc_console); - return 0; -} -console_initcall(hvc_console_init); - -/* callback when the kboject ref count reaches zero. */ -static void destroy_hvc_struct(struct kref *kref) -{ - struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref); - unsigned long flags; - - spin_lock(&hvc_structs_lock); - - spin_lock_irqsave(&hp->lock, flags); - list_del(&(hp->next)); - spin_unlock_irqrestore(&hp->lock, flags); - - spin_unlock(&hvc_structs_lock); - - kfree(hp); -} - -/* - * hvc_instantiate() is an early console discovery method which locates - * consoles * prior to the vio subsystem discovering them. Hotplugged - * vty adapters do NOT get an hvc_instantiate() callback since they - * appear after early console init. - */ -int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) -{ - struct hvc_struct *hp; - - if (index < 0 || index >= MAX_NR_HVC_CONSOLES) - return -1; - - if (vtermnos[index] != -1) - return -1; - - /* make sure no no tty has been registered in this index */ - hp = hvc_get_by_index(index); - if (hp) { - kref_put(&hp->kref, destroy_hvc_struct); - return -1; - } - - vtermnos[index] = vtermno; - cons_ops[index] = ops; - - /* reserve all indices up to and including this index */ - if (last_hvc < index) - last_hvc = index; - - /* if this index is what the user requested, then register - * now (setup won't fail at this point). It's ok to just - * call register again if previously .setup failed. - */ - if (index == hvc_console.index) - register_console(&hvc_console); - - return 0; -} -EXPORT_SYMBOL_GPL(hvc_instantiate); - -/* Wake the sleeping khvcd */ -void hvc_kick(void) -{ - hvc_kicked = 1; - wake_up_process(hvc_task); -} -EXPORT_SYMBOL_GPL(hvc_kick); - -static void hvc_unthrottle(struct tty_struct *tty) -{ - hvc_kick(); -} - -/* - * The TTY interface won't be used until after the vio layer has exposed the vty - * adapter to the kernel. - */ -static int hvc_open(struct tty_struct *tty, struct file * filp) -{ - struct hvc_struct *hp; - unsigned long flags; - int rc = 0; - - /* Auto increments kref reference if found. */ - if (!(hp = hvc_get_by_index(tty->index))) - return -ENODEV; - - spin_lock_irqsave(&hp->lock, flags); - /* Check and then increment for fast path open. */ - if (hp->count++ > 0) { - tty_kref_get(tty); - spin_unlock_irqrestore(&hp->lock, flags); - hvc_kick(); - return 0; - } /* else count == 0 */ - - tty->driver_data = hp; - - hp->tty = tty_kref_get(tty); - - spin_unlock_irqrestore(&hp->lock, flags); - - if (hp->ops->notifier_add) - rc = hp->ops->notifier_add(hp, hp->data); - - /* - * If the notifier fails we return an error. The tty layer - * will call hvc_close() after a failed open but we don't want to clean - * up there so we'll clean up here and clear out the previously set - * tty fields and return the kref reference. - */ - if (rc) { - spin_lock_irqsave(&hp->lock, flags); - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); - tty_kref_put(tty); - tty->driver_data = NULL; - kref_put(&hp->kref, destroy_hvc_struct); - printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); - } - /* Force wakeup of the polling thread */ - hvc_kick(); - - return rc; -} - -static void hvc_close(struct tty_struct *tty, struct file * filp) -{ - struct hvc_struct *hp; - unsigned long flags; - - if (tty_hung_up_p(filp)) - return; - - /* - * No driver_data means that this close was issued after a failed - * hvc_open by the tty layer's release_dev() function and we can just - * exit cleanly because the kref reference wasn't made. - */ - if (!tty->driver_data) - return; - - hp = tty->driver_data; - - spin_lock_irqsave(&hp->lock, flags); - - if (--hp->count == 0) { - /* We are done with the tty pointer now. */ - hp->tty = NULL; - spin_unlock_irqrestore(&hp->lock, flags); - - if (hp->ops->notifier_del) - hp->ops->notifier_del(hp, hp->data); - - /* cancel pending tty resize work */ - cancel_work_sync(&hp->tty_resize); - - /* - * Chain calls chars_in_buffer() and returns immediately if - * there is no buffered data otherwise sleeps on a wait queue - * waking periodically to check chars_in_buffer(). - */ - tty_wait_until_sent(tty, HVC_CLOSE_WAIT); - } else { - if (hp->count < 0) - printk(KERN_ERR "hvc_close %X: oops, count is %d\n", - hp->vtermno, hp->count); - spin_unlock_irqrestore(&hp->lock, flags); - } - - tty_kref_put(tty); - kref_put(&hp->kref, destroy_hvc_struct); -} - -static void hvc_hangup(struct tty_struct *tty) -{ - struct hvc_struct *hp = tty->driver_data; - unsigned long flags; - int temp_open_count; - - if (!hp) - return; - - /* cancel pending tty resize work */ - cancel_work_sync(&hp->tty_resize); - - spin_lock_irqsave(&hp->lock, flags); - - /* - * The N_TTY line discipline has problems such that in a close vs - * open->hangup case this can be called after the final close so prevent - * that from happening for now. - */ - if (hp->count <= 0) { - spin_unlock_irqrestore(&hp->lock, flags); - return; - } - - temp_open_count = hp->count; - hp->count = 0; - hp->n_outbuf = 0; - hp->tty = NULL; - - spin_unlock_irqrestore(&hp->lock, flags); - - if (hp->ops->notifier_hangup) - hp->ops->notifier_hangup(hp, hp->data); - - while(temp_open_count) { - --temp_open_count; - tty_kref_put(tty); - kref_put(&hp->kref, destroy_hvc_struct); - } -} - -/* - * Push buffered characters whether they were just recently buffered or waiting - * on a blocked hypervisor. Call this function with hp->lock held. - */ -static int hvc_push(struct hvc_struct *hp) -{ - int n; - - n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); - if (n <= 0) { - if (n == 0) { - hp->do_wakeup = 1; - return 0; - } - /* throw away output on error; this happens when - there is no session connected to the vterm. */ - hp->n_outbuf = 0; - } else - hp->n_outbuf -= n; - if (hp->n_outbuf > 0) - memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); - else - hp->do_wakeup = 1; - - return n; -} - -static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct hvc_struct *hp = tty->driver_data; - unsigned long flags; - int rsize, written = 0; - - /* This write was probably executed during a tty close. */ - if (!hp) - return -EPIPE; - - if (hp->count <= 0) - return -EIO; - - spin_lock_irqsave(&hp->lock, flags); - - /* Push pending writes */ - if (hp->n_outbuf > 0) - hvc_push(hp); - - while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) { - if (rsize > count) - rsize = count; - memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); - count -= rsize; - buf += rsize; - hp->n_outbuf += rsize; - written += rsize; - hvc_push(hp); - } - spin_unlock_irqrestore(&hp->lock, flags); - - /* - * Racy, but harmless, kick thread if there is still pending data. - */ - if (hp->n_outbuf) - hvc_kick(); - - return written; -} - -/** - * hvc_set_winsz() - Resize the hvc tty terminal window. - * @work: work structure. - * - * The routine shall not be called within an atomic context because it - * might sleep. - * - * Locking: hp->lock - */ -static void hvc_set_winsz(struct work_struct *work) -{ - struct hvc_struct *hp; - unsigned long hvc_flags; - struct tty_struct *tty; - struct winsize ws; - - hp = container_of(work, struct hvc_struct, tty_resize); - - spin_lock_irqsave(&hp->lock, hvc_flags); - if (!hp->tty) { - spin_unlock_irqrestore(&hp->lock, hvc_flags); - return; - } - ws = hp->ws; - tty = tty_kref_get(hp->tty); - spin_unlock_irqrestore(&hp->lock, hvc_flags); - - tty_do_resize(tty, &ws); - tty_kref_put(tty); -} - -/* - * This is actually a contract between the driver and the tty layer outlining - * how much write room the driver can guarantee will be sent OR BUFFERED. This - * driver MUST honor the return value. - */ -static int hvc_write_room(struct tty_struct *tty) -{ - struct hvc_struct *hp = tty->driver_data; - - if (!hp) - return -1; - - return hp->outbuf_size - hp->n_outbuf; -} - -static int hvc_chars_in_buffer(struct tty_struct *tty) -{ - struct hvc_struct *hp = tty->driver_data; - - if (!hp) - return 0; - return hp->n_outbuf; -} - -/* - * timeout will vary between the MIN and MAX values defined here. By default - * and during console activity we will use a default MIN_TIMEOUT of 10. When - * the console is idle, we increase the timeout value on each pass through - * msleep until we reach the max. This may be noticeable as a brief (average - * one second) delay on the console before the console responds to input when - * there has been no input for some time. - */ -#define MIN_TIMEOUT (10) -#define MAX_TIMEOUT (2000) -static u32 timeout = MIN_TIMEOUT; - -#define HVC_POLL_READ 0x00000001 -#define HVC_POLL_WRITE 0x00000002 - -int hvc_poll(struct hvc_struct *hp) -{ - struct tty_struct *tty; - int i, n, poll_mask = 0; - char buf[N_INBUF] __ALIGNED__; - unsigned long flags; - int read_total = 0; - int written_total = 0; - - spin_lock_irqsave(&hp->lock, flags); - - /* Push pending writes */ - if (hp->n_outbuf > 0) - written_total = hvc_push(hp); - - /* Reschedule us if still some write pending */ - if (hp->n_outbuf > 0) { - poll_mask |= HVC_POLL_WRITE; - /* If hvc_push() was not able to write, sleep a few msecs */ - timeout = (written_total) ? 0 : MIN_TIMEOUT; - } - - /* No tty attached, just skip */ - tty = tty_kref_get(hp->tty); - if (tty == NULL) - goto bail; - - /* Now check if we can get data (are we throttled ?) */ - if (test_bit(TTY_THROTTLED, &tty->flags)) - goto throttled; - - /* If we aren't notifier driven and aren't throttled, we always - * request a reschedule - */ - if (!hp->irq_requested) - poll_mask |= HVC_POLL_READ; - - /* Read data if any */ - for (;;) { - int count = tty_buffer_request_room(tty, N_INBUF); - - /* If flip is full, just reschedule a later read */ - if (count == 0) { - poll_mask |= HVC_POLL_READ; - break; - } - - n = hp->ops->get_chars(hp->vtermno, buf, count); - if (n <= 0) { - /* Hangup the tty when disconnected from host */ - if (n == -EPIPE) { - spin_unlock_irqrestore(&hp->lock, flags); - tty_hangup(tty); - spin_lock_irqsave(&hp->lock, flags); - } else if ( n == -EAGAIN ) { - /* - * Some back-ends can only ensure a certain min - * num of bytes read, which may be > 'count'. - * Let the tty clear the flip buff to make room. - */ - poll_mask |= HVC_POLL_READ; - } - break; - } - for (i = 0; i < n; ++i) { -#ifdef CONFIG_MAGIC_SYSRQ - if (hp->index == hvc_console.index) { - /* Handle the SysRq Hack */ - /* XXX should support a sequence */ - if (buf[i] == '\x0f') { /* ^O */ - /* if ^O is pressed again, reset - * sysrq_pressed and flip ^O char */ - sysrq_pressed = !sysrq_pressed; - if (sysrq_pressed) - continue; - } else if (sysrq_pressed) { - handle_sysrq(buf[i]); - sysrq_pressed = 0; - continue; - } - } -#endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(tty, buf[i], 0); - } - - read_total += n; - } - throttled: - /* Wakeup write queue if necessary */ - if (hp->do_wakeup) { - hp->do_wakeup = 0; - tty_wakeup(tty); - } - bail: - spin_unlock_irqrestore(&hp->lock, flags); - - if (read_total) { - /* Activity is occurring, so reset the polling backoff value to - a minimum for performance. */ - timeout = MIN_TIMEOUT; - - tty_flip_buffer_push(tty); - } - if (tty) - tty_kref_put(tty); - - return poll_mask; -} -EXPORT_SYMBOL_GPL(hvc_poll); - -/** - * __hvc_resize() - Update terminal window size information. - * @hp: HVC console pointer - * @ws: Terminal window size structure - * - * Stores the specified window size information in the hvc structure of @hp. - * The function schedule the tty resize update. - * - * Locking: Locking free; the function MUST be called holding hp->lock - */ -void __hvc_resize(struct hvc_struct *hp, struct winsize ws) -{ - hp->ws = ws; - schedule_work(&hp->tty_resize); -} -EXPORT_SYMBOL_GPL(__hvc_resize); - -/* - * This kthread is either polling or interrupt driven. This is determined by - * calling hvc_poll() who determines whether a console adapter support - * interrupts. - */ -static int khvcd(void *unused) -{ - int poll_mask; - struct hvc_struct *hp; - - set_freezable(); - do { - poll_mask = 0; - hvc_kicked = 0; - try_to_freeze(); - wmb(); - if (!cpus_are_in_xmon()) { - spin_lock(&hvc_structs_lock); - list_for_each_entry(hp, &hvc_structs, next) { - poll_mask |= hvc_poll(hp); - } - spin_unlock(&hvc_structs_lock); - } else - poll_mask |= HVC_POLL_READ; - if (hvc_kicked) - continue; - set_current_state(TASK_INTERRUPTIBLE); - if (!hvc_kicked) { - if (poll_mask == 0) - schedule(); - else { - if (timeout < MAX_TIMEOUT) - timeout += (timeout >> 6) + 1; - - msleep_interruptible(timeout); - } - } - __set_current_state(TASK_RUNNING); - } while (!kthread_should_stop()); - - return 0; -} - -static const struct tty_operations hvc_ops = { - .open = hvc_open, - .close = hvc_close, - .write = hvc_write, - .hangup = hvc_hangup, - .unthrottle = hvc_unthrottle, - .write_room = hvc_write_room, - .chars_in_buffer = hvc_chars_in_buffer, -}; - -struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, - const struct hv_ops *ops, - int outbuf_size) -{ - struct hvc_struct *hp; - int i; - - /* We wait until a driver actually comes along */ - if (!hvc_driver) { - int err = hvc_init(); - if (err) - return ERR_PTR(err); - } - - hp = kzalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, - GFP_KERNEL); - if (!hp) - return ERR_PTR(-ENOMEM); - - hp->vtermno = vtermno; - hp->data = data; - hp->ops = ops; - hp->outbuf_size = outbuf_size; - hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; - - kref_init(&hp->kref); - - INIT_WORK(&hp->tty_resize, hvc_set_winsz); - spin_lock_init(&hp->lock); - spin_lock(&hvc_structs_lock); - - /* - * find index to use: - * see if this vterm id matches one registered for console. - */ - for (i=0; i < MAX_NR_HVC_CONSOLES; i++) - if (vtermnos[i] == hp->vtermno && - cons_ops[i] == hp->ops) - break; - - /* no matching slot, just use a counter */ - if (i >= MAX_NR_HVC_CONSOLES) - i = ++last_hvc; - - hp->index = i; - - list_add_tail(&(hp->next), &hvc_structs); - spin_unlock(&hvc_structs_lock); - - return hp; -} -EXPORT_SYMBOL_GPL(hvc_alloc); - -int hvc_remove(struct hvc_struct *hp) -{ - unsigned long flags; - struct tty_struct *tty; - - spin_lock_irqsave(&hp->lock, flags); - tty = tty_kref_get(hp->tty); - - if (hp->index < MAX_NR_HVC_CONSOLES) - vtermnos[hp->index] = -1; - - /* Don't whack hp->irq because tty_hangup() will need to free the irq. */ - - spin_unlock_irqrestore(&hp->lock, flags); - - /* - * We 'put' the instance that was grabbed when the kref instance - * was initialized using kref_init(). Let the last holder of this - * kref cause it to be removed, which will probably be the tty_vhangup - * below. - */ - kref_put(&hp->kref, destroy_hvc_struct); - - /* - * This function call will auto chain call hvc_hangup. - */ - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } - return 0; -} -EXPORT_SYMBOL_GPL(hvc_remove); - -/* Driver initialization: called as soon as someone uses hvc_alloc(). */ -static int hvc_init(void) -{ - struct tty_driver *drv; - int err; - - /* We need more than hvc_count adapters due to hotplug additions. */ - drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); - if (!drv) { - err = -ENOMEM; - goto out; - } - - drv->owner = THIS_MODULE; - drv->driver_name = "hvc"; - drv->name = "hvc"; - drv->major = HVC_MAJOR; - drv->minor_start = HVC_MINOR; - drv->type = TTY_DRIVER_TYPE_SYSTEM; - drv->init_termios = tty_std_termios; - drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; - tty_set_operations(drv, &hvc_ops); - - /* Always start the kthread because there can be hotplug vty adapters - * added later. */ - hvc_task = kthread_run(khvcd, NULL, "khvcd"); - if (IS_ERR(hvc_task)) { - printk(KERN_ERR "Couldn't create kthread for console.\n"); - err = PTR_ERR(hvc_task); - goto put_tty; - } - - err = tty_register_driver(drv); - if (err) { - printk(KERN_ERR "Couldn't register hvc console driver\n"); - goto stop_thread; - } - - /* - * Make sure tty is fully registered before allowing it to be - * found by hvc_console_device. - */ - smp_mb(); - hvc_driver = drv; - return 0; - -stop_thread: - kthread_stop(hvc_task); - hvc_task = NULL; -put_tty: - put_tty_driver(drv); -out: - return err; -} - -/* This isn't particularly necessary due to this being a console driver - * but it is nice to be thorough. - */ -static void __exit hvc_exit(void) -{ - if (hvc_driver) { - kthread_stop(hvc_task); - - tty_unregister_driver(hvc_driver); - /* return tty_struct instances allocated in hvc_init(). */ - put_tty_driver(hvc_driver); - unregister_console(&hvc_console); - } -} -module_exit(hvc_exit); diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h deleted file mode 100644 index 54381eba4e4a..000000000000 --- a/drivers/char/hvc_console.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * hvc_console.h - * Copyright (C) 2005 IBM Corporation - * - * Author(s): - * Ryan S. Arnold - * - * hvc_console header information: - * moved here from arch/powerpc/include/asm/hvconsole.h - * and drivers/char/hvc_console.c - * - * 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 HVC_CONSOLE_H -#define HVC_CONSOLE_H -#include -#include -#include - -/* - * This is the max number of console adapters that can/will be found as - * console devices on first stage console init. Any number beyond this range - * can't be used as a console device but is still a valid tty device. - */ -#define MAX_NR_HVC_CONSOLES 16 - -/* - * The Linux TTY code does not support dynamic addition of tty derived devices - * so we need to know how many tty devices we might need when space is allocated - * for the tty device. Since this driver supports hotplug of vty adapters we - * need to make sure we have enough allocated. - */ -#define HVC_ALLOC_TTY_ADAPTERS 8 - -struct hvc_struct { - spinlock_t lock; - int index; - struct tty_struct *tty; - int count; - int do_wakeup; - char *outbuf; - int outbuf_size; - int n_outbuf; - uint32_t vtermno; - const struct hv_ops *ops; - int irq_requested; - int data; - struct winsize ws; - struct work_struct tty_resize; - struct list_head next; - struct kref kref; /* ref count & hvc_struct lifetime */ -}; - -/* implemented by a low level driver */ -struct hv_ops { - int (*get_chars)(uint32_t vtermno, char *buf, int count); - int (*put_chars)(uint32_t vtermno, const char *buf, int count); - - /* Callbacks for notification. Called in open, close and hangup */ - int (*notifier_add)(struct hvc_struct *hp, int irq); - void (*notifier_del)(struct hvc_struct *hp, int irq); - void (*notifier_hangup)(struct hvc_struct *hp, int irq); -}; - -/* Register a vterm and a slot index for use as a console (console_init) */ -extern int hvc_instantiate(uint32_t vtermno, int index, - const struct hv_ops *ops); - -/* register a vterm for hvc tty operation (module_init or hotplug add) */ -extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data, - const struct hv_ops *ops, int outbuf_size); -/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ -extern int hvc_remove(struct hvc_struct *hp); - -/* data available */ -int hvc_poll(struct hvc_struct *hp); -void hvc_kick(void); - -/* Resize hvc tty terminal window */ -extern void __hvc_resize(struct hvc_struct *hp, struct winsize ws); - -static inline void hvc_resize(struct hvc_struct *hp, struct winsize ws) -{ - unsigned long flags; - - spin_lock_irqsave(&hp->lock, flags); - __hvc_resize(hp, ws); - spin_unlock_irqrestore(&hp->lock, flags); -} - -/* default notifier for irq based notification */ -extern int notifier_add_irq(struct hvc_struct *hp, int data); -extern void notifier_del_irq(struct hvc_struct *hp, int data); -extern void notifier_hangup_irq(struct hvc_struct *hp, int data); - - -#if defined(CONFIG_XMON) && defined(CONFIG_SMP) -#include -#else -static inline int cpus_are_in_xmon(void) -{ - return 0; -} -#endif - -#endif // HVC_CONSOLE_H diff --git a/drivers/char/hvc_dcc.c b/drivers/char/hvc_dcc.c deleted file mode 100644 index 6470f63deb4b..000000000000 --- a/drivers/char/hvc_dcc.c +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "hvc_console.h" - -/* DCC Status Bits */ -#define DCC_STATUS_RX (1 << 30) -#define DCC_STATUS_TX (1 << 29) - -static inline u32 __dcc_getstatus(void) -{ - u32 __ret; - - asm("mrc p14, 0, %0, c0, c1, 0 @ read comms ctrl reg" - : "=r" (__ret) : : "cc"); - - return __ret; -} - - -#if defined(CONFIG_CPU_V7) -static inline char __dcc_getchar(void) -{ - char __c; - - asm("get_wait: mrc p14, 0, pc, c0, c1, 0 \n\ - bne get_wait \n\ - mrc p14, 0, %0, c0, c5, 0 @ read comms data reg" - : "=r" (__c) : : "cc"); - - return __c; -} -#else -static inline char __dcc_getchar(void) -{ - char __c; - - asm("mrc p14, 0, %0, c0, c5, 0 @ read comms data reg" - : "=r" (__c)); - - return __c; -} -#endif - -#if defined(CONFIG_CPU_V7) -static inline void __dcc_putchar(char c) -{ - asm("put_wait: mrc p14, 0, pc, c0, c1, 0 \n\ - bcs put_wait \n\ - mcr p14, 0, %0, c0, c5, 0 " - : : "r" (c) : "cc"); -} -#else -static inline void __dcc_putchar(char c) -{ - asm("mcr p14, 0, %0, c0, c5, 0 @ write a char" - : /* no output register */ - : "r" (c)); -} -#endif - -static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) -{ - int i; - - for (i = 0; i < count; i++) { - while (__dcc_getstatus() & DCC_STATUS_TX) - cpu_relax(); - - __dcc_putchar((char)(buf[i] & 0xFF)); - } - - return count; -} - -static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count) -{ - int i; - - for (i = 0; i < count; ++i) { - int c = -1; - - if (__dcc_getstatus() & DCC_STATUS_RX) - c = __dcc_getchar(); - if (c < 0) - break; - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_dcc_get_put_ops = { - .get_chars = hvc_dcc_get_chars, - .put_chars = hvc_dcc_put_chars, -}; - -static int __init hvc_dcc_console_init(void) -{ - hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); - return 0; -} -console_initcall(hvc_dcc_console_init); - -static int __init hvc_dcc_init(void) -{ - hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); - return 0; -} -device_initcall(hvc_dcc_init); diff --git a/drivers/char/hvc_irq.c b/drivers/char/hvc_irq.c deleted file mode 100644 index 2623e177e8d6..000000000000 --- a/drivers/char/hvc_irq.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright IBM Corp. 2001,2008 - * - * This file contains the IRQ specific code for hvc_console - * - */ - -#include - -#include "hvc_console.h" - -static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance) -{ - /* if hvc_poll request a repoll, then kick the hvcd thread */ - if (hvc_poll(dev_instance)) - hvc_kick(); - return IRQ_HANDLED; -} - -/* - * For IRQ based systems these callbacks can be used - */ -int notifier_add_irq(struct hvc_struct *hp, int irq) -{ - int rc; - - if (!irq) { - hp->irq_requested = 0; - return 0; - } - rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, - "hvc_console", hp); - if (!rc) - hp->irq_requested = 1; - return rc; -} - -void notifier_del_irq(struct hvc_struct *hp, int irq) -{ - if (!hp->irq_requested) - return; - free_irq(irq, hp); - hp->irq_requested = 0; -} - -void notifier_hangup_irq(struct hvc_struct *hp, int irq) -{ - notifier_del_irq(hp, irq); -} diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c deleted file mode 100644 index 21c54955084e..000000000000 --- a/drivers/char/hvc_iseries.c +++ /dev/null @@ -1,598 +0,0 @@ -/* - * iSeries vio driver interface to hvc_console.c - * - * This code is based heavily on hvc_vio.c and viocons.c - * - * Copyright (C) 2006 Stephen Rothwell, IBM Corporation - * - * 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 -#include -#include -#include -#include - -#include "hvc_console.h" - -#define VTTY_PORTS 10 - -static DEFINE_SPINLOCK(consolelock); -static DEFINE_SPINLOCK(consoleloglock); - -static const char hvc_driver_name[] = "hvc_console"; - -#define IN_BUF_SIZE 200 - -/* - * Our port information. - */ -static struct port_info { - HvLpIndex lp; - u64 seq; /* sequence number of last HV send */ - u64 ack; /* last ack from HV */ - struct hvc_struct *hp; - int in_start; - int in_end; - unsigned char in_buf[IN_BUF_SIZE]; -} port_info[VTTY_PORTS] = { - [ 0 ... VTTY_PORTS - 1 ] = { - .lp = HvLpIndexInvalid - } -}; - -#define viochar_is_console(pi) ((pi) == &port_info[0]) - -static struct vio_device_id hvc_driver_table[] __devinitdata = { - {"serial", "IBM,iSeries-vty"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvc_driver_table); - -static void hvlog(char *fmt, ...) -{ - int i; - unsigned long flags; - va_list args; - static char buf[256]; - - spin_lock_irqsave(&consoleloglock, flags); - va_start(args, fmt); - i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); - va_end(args); - buf[i++] = '\r'; - HvCall_writeLogBuffer(buf, i); - spin_unlock_irqrestore(&consoleloglock, flags); -} - -/* - * Initialize the common fields in a charLpEvent - */ -static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp) -{ - struct HvLpEvent *hev = &viochar->event; - - memset(viochar, 0, sizeof(struct viocharlpevent)); - - hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK | - HV_LP_EVENT_INT; - hev->xType = HvLpEvent_Type_VirtualIo; - hev->xSubtype = viomajorsubtype_chario | viochardata; - hev->xSourceLp = HvLpConfig_getLpIndex(); - hev->xTargetLp = lp; - hev->xSizeMinus1 = sizeof(struct viocharlpevent); - hev->xSourceInstanceId = viopath_sourceinst(lp); - hev->xTargetInstanceId = viopath_targetinst(lp); -} - -static int get_chars(uint32_t vtermno, char *buf, int count) -{ - struct port_info *pi; - int n = 0; - unsigned long flags; - - if (vtermno >= VTTY_PORTS) - return -EINVAL; - if (count == 0) - return 0; - - pi = &port_info[vtermno]; - spin_lock_irqsave(&consolelock, flags); - - if (pi->in_end == 0) - goto done; - - n = pi->in_end - pi->in_start; - if (n > count) - n = count; - memcpy(buf, &pi->in_buf[pi->in_start], n); - pi->in_start += n; - if (pi->in_start == pi->in_end) { - pi->in_start = 0; - pi->in_end = 0; - } -done: - spin_unlock_irqrestore(&consolelock, flags); - return n; -} - -static int put_chars(uint32_t vtermno, const char *buf, int count) -{ - struct viocharlpevent *viochar; - struct port_info *pi; - HvLpEvent_Rc hvrc; - unsigned long flags; - int sent = 0; - - if (vtermno >= VTTY_PORTS) - return -EINVAL; - - pi = &port_info[vtermno]; - - spin_lock_irqsave(&consolelock, flags); - - if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) { - HvCall_writeLogBuffer(buf, count); - sent = count; - goto done; - } - - viochar = vio_get_event_buffer(viomajorsubtype_chario); - if (viochar == NULL) { - hvlog("\n\rviocons: Can't get viochar buffer."); - goto done; - } - - while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { - int len; - - len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count; - - if (viochar_is_console(pi)) - HvCall_writeLogBuffer(buf, len); - - init_data_event(viochar, pi->lp); - - viochar->len = len; - viochar->event.xCorrelationToken = pi->seq++; - viochar->event.xSizeMinus1 = - offsetof(struct viocharlpevent, data) + len; - - memcpy(viochar->data, buf, len); - - hvrc = HvCallEvent_signalLpEvent(&viochar->event); - if (hvrc) - hvlog("\n\rerror sending event! return code %d\n\r", - (int)hvrc); - sent += len; - count -= len; - buf += len; - } - - vio_free_event_buffer(viomajorsubtype_chario, viochar); -done: - spin_unlock_irqrestore(&consolelock, flags); - return sent; -} - -static const struct hv_ops hvc_get_put_ops = { - .get_chars = get_chars, - .put_chars = put_chars, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __devinit hvc_vio_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - struct hvc_struct *hp; - struct port_info *pi; - - /* probed with invalid parameters. */ - if (!vdev || !id) - return -EPERM; - - if (vdev->unit_address >= VTTY_PORTS) - return -ENODEV; - - pi = &port_info[vdev->unit_address]; - - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, - VIOCHAR_MAX_DATA); - if (IS_ERR(hp)) - return PTR_ERR(hp); - pi->hp = hp; - dev_set_drvdata(&vdev->dev, pi); - - return 0; -} - -static int __devexit hvc_vio_remove(struct vio_dev *vdev) -{ - struct port_info *pi = dev_get_drvdata(&vdev->dev); - struct hvc_struct *hp = pi->hp; - - return hvc_remove(hp); -} - -static struct vio_driver hvc_vio_driver = { - .id_table = hvc_driver_table, - .probe = hvc_vio_probe, - .remove = __devexit_p(hvc_vio_remove), - .driver = { - .name = hvc_driver_name, - .owner = THIS_MODULE, - } -}; - -static void hvc_open_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtual_device; - struct port_info *pi; - int reject = 0; - - if (hvlpevent_is_ack(event)) { - if (port >= VTTY_PORTS) - return; - - spin_lock_irqsave(&consolelock, flags); - - pi = &port_info[port]; - if (event->xRc == HvLpEvent_Rc_Good) { - pi->seq = pi->ack = 0; - /* - * This line allows connections from the primary - * partition but once one is connected from the - * primary partition nothing short of a reboot - * of linux will allow access from the hosting - * partition again without a required iSeries fix. - */ - pi->lp = event->xTargetLp; - } - - spin_unlock_irqrestore(&consolelock, flags); - if (event->xRc != HvLpEvent_Rc_Good) - printk(KERN_WARNING - "hvc: handle_open_event: event->xRc == (%d).\n", - event->xRc); - - if (event->xCorrelationToken != 0) { - atomic_t *aptr= (atomic_t *)event->xCorrelationToken; - atomic_set(aptr, 1); - } else - printk(KERN_WARNING - "hvc: weird...got open ack without atomic\n"); - return; - } - - /* This had better require an ack, otherwise complain */ - if (!hvlpevent_need_ack(event)) { - printk(KERN_WARNING "hvc: viocharopen without ack bit!\n"); - return; - } - - spin_lock_irqsave(&consolelock, flags); - - /* Make sure this is a good virtual tty */ - if (port >= VTTY_PORTS) { - event->xRc = HvLpEvent_Rc_SubtypeError; - cevent->subtype_result_code = viorc_openRejected; - /* - * Flag state here since we can't printk while holding - * the consolelock spinlock. - */ - reject = 1; - } else { - pi = &port_info[port]; - if ((pi->lp != HvLpIndexInvalid) && - (pi->lp != event->xSourceLp)) { - /* - * If this is tty is already connected to a different - * partition, fail. - */ - event->xRc = HvLpEvent_Rc_SubtypeError; - cevent->subtype_result_code = viorc_openRejected; - reject = 2; - } else { - pi->lp = event->xSourceLp; - event->xRc = HvLpEvent_Rc_Good; - cevent->subtype_result_code = viorc_good; - pi->seq = pi->ack = 0; - } - } - - spin_unlock_irqrestore(&consolelock, flags); - - if (reject == 1) - printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n"); - else if (reject == 2) - printk(KERN_WARNING "hvc: open rejected: console in exclusive " - "use by another partition.\n"); - - /* Return the acknowledgement */ - HvCallEvent_ackLpEvent(event); -} - -/* - * Handle a close charLpEvent. This should ONLY be an Interrupt because the - * virtual console should never actually issue a close event to the hypervisor - * because the virtual console never goes away. A close event coming from the - * hypervisor simply means that there are no client consoles connected to the - * virtual console. - */ -static void hvc_close_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtual_device; - - if (!hvlpevent_is_int(event)) { - printk(KERN_WARNING - "hvc: got unexpected close acknowledgement\n"); - return; - } - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING - "hvc: close message from invalid virtual device.\n"); - return; - } - - /* For closes, just mark the console partition invalid */ - spin_lock_irqsave(&consolelock, flags); - - if (port_info[port].lp == event->xSourceLp) - port_info[port].lp = HvLpIndexInvalid; - - spin_unlock_irqrestore(&consolelock, flags); -} - -static void hvc_data_event(struct HvLpEvent *event) -{ - unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - struct port_info *pi; - int n; - u8 port = cevent->virtual_device; - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING "hvc: data on invalid virtual device %d\n", - port); - return; - } - if (cevent->len == 0) - return; - - /* - * Change 05/01/2003 - Ryan Arnold: If a partition other than - * the current exclusive partition tries to send us data - * events then just drop them on the floor because we don't - * want his stinking data. He isn't authorized to receive - * data because he wasn't the first one to get the console, - * therefore he shouldn't be allowed to send data either. - * This will work without an iSeries fix. - */ - pi = &port_info[port]; - if (pi->lp != event->xSourceLp) - return; - - spin_lock_irqsave(&consolelock, flags); - - n = IN_BUF_SIZE - pi->in_end; - if (n > cevent->len) - n = cevent->len; - if (n > 0) { - memcpy(&pi->in_buf[pi->in_end], cevent->data, n); - pi->in_end += n; - } - spin_unlock_irqrestore(&consolelock, flags); - if (n == 0) - printk(KERN_WARNING "hvc: input buffer overflow\n"); -} - -static void hvc_ack_event(struct HvLpEvent *event) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - unsigned long flags; - u8 port = cevent->virtual_device; - - if (port >= VTTY_PORTS) { - printk(KERN_WARNING "hvc: data on invalid virtual device\n"); - return; - } - - spin_lock_irqsave(&consolelock, flags); - port_info[port].ack = event->xCorrelationToken; - spin_unlock_irqrestore(&consolelock, flags); -} - -static void hvc_config_event(struct HvLpEvent *event) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - - if (cevent->data[0] == 0x01) - printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n", - cevent->data[1], cevent->data[2], - cevent->data[3], cevent->data[4]); - else - printk(KERN_WARNING "hvc: unknown config event\n"); -} - -static void hvc_handle_event(struct HvLpEvent *event) -{ - int charminor; - - if (event == NULL) - return; - - charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; - switch (charminor) { - case viocharopen: - hvc_open_event(event); - break; - case viocharclose: - hvc_close_event(event); - break; - case viochardata: - hvc_data_event(event); - break; - case viocharack: - hvc_ack_event(event); - break; - case viocharconfig: - hvc_config_event(event); - break; - default: - if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - -static int __init send_open(HvLpIndex remoteLp, void *sem) -{ - return HvCallEvent_signalLpEventFast(remoteLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_chario | viocharopen, - HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(remoteLp), - viopath_targetinst(remoteLp), - (u64)(unsigned long)sem, VIOVERSION << 16, - 0, 0, 0, 0); -} - -static int __init hvc_vio_init(void) -{ - atomic_t wait_flag; - int rc; - - if (!firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - - /* +2 for fudge */ - rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), - viomajorsubtype_chario, VIOCHAR_WINDOW + 2); - if (rc) - printk(KERN_WARNING "hvc: error opening to primary %d\n", rc); - - if (viopath_hostLp == HvLpIndexInvalid) - vio_set_hostlp(); - - /* - * And if the primary is not the same as the hosting LP, open to the - * hosting lp - */ - if ((viopath_hostLp != HvLpIndexInvalid) && - (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { - printk(KERN_INFO "hvc: open path to hosting (%d)\n", - viopath_hostLp); - rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, - VIOCHAR_WINDOW + 2); /* +2 for fudge */ - if (rc) - printk(KERN_WARNING - "error opening to partition %d: %d\n", - viopath_hostLp, rc); - } - - if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0) - printk(KERN_WARNING - "hvc: error seting handler for console events!\n"); - - /* - * First, try to open the console to the hosting lp. - * Wait on a semaphore for the response. - */ - atomic_set(&wait_flag, 0); - if ((viopath_isactive(viopath_hostLp)) && - (send_open(viopath_hostLp, &wait_flag) == 0)) { - printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp); - while (atomic_read(&wait_flag) == 0) - mb(); - atomic_set(&wait_flag, 0); - } - - /* - * If we don't have an active console, try the primary - */ - if ((!viopath_isactive(port_info[0].lp)) && - (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && - (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) { - printk(KERN_INFO "hvc: opening console to primary partition\n"); - while (atomic_read(&wait_flag) == 0) - mb(); - } - - /* Register as a vio device to receive callbacks */ - rc = vio_register_driver(&hvc_vio_driver); - - return rc; -} -module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ - -static void __exit hvc_vio_exit(void) -{ - vio_unregister_driver(&hvc_vio_driver); -} -module_exit(hvc_vio_exit); - -/* the device tree order defines our numbering */ -static int __init hvc_find_vtys(void) -{ - struct device_node *vty; - int num_found = 0; - - for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; - vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; - - /* We have statically defined space for only a certain number - * of console adapters. - */ - if ((num_found >= MAX_NR_HVC_CONSOLES) || - (num_found >= VTTY_PORTS)) { - of_node_put(vty); - break; - } - - vtermno = of_get_property(vty, "reg", NULL); - if (!vtermno) - continue; - - if (!of_device_is_compatible(vty, "IBM,iSeries-vty")) - continue; - - if (num_found == 0) - add_preferred_console("hvc", 0, NULL); - hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); - ++num_found; - } - - return num_found; -} -console_initcall(hvc_find_vtys); diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c deleted file mode 100644 index c3425bb3a1f6..000000000000 --- a/drivers/char/hvc_iucv.c +++ /dev/null @@ -1,1337 +0,0 @@ -/* - * hvc_iucv.c - z/VM IUCV hypervisor console (HVC) device driver - * - * This HVC device driver provides terminal access using - * z/VM IUCV communication paths. - * - * Copyright IBM Corp. 2008, 2009 - * - * Author(s): Hendrik Brueckner - */ -#define KMSG_COMPONENT "hvc_iucv" -#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hvc_console.h" - - -/* General device driver settings */ -#define HVC_IUCV_MAGIC 0xc9e4c3e5 -#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS -#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4) - -/* IUCV TTY message */ -#define MSG_VERSION 0x02 /* Message version */ -#define MSG_TYPE_ERROR 0x01 /* Error message */ -#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */ -#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */ -#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */ -#define MSG_TYPE_DATA 0x10 /* Terminal data */ - -struct iucv_tty_msg { - u8 version; /* Message version */ - u8 type; /* Message type */ -#define MSG_MAX_DATALEN ((u16)(~0)) - u16 datalen; /* Payload length */ - u8 data[]; /* Payload buffer */ -} __attribute__((packed)); -#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) - -enum iucv_state_t { - IUCV_DISCONN = 0, - IUCV_CONNECTED = 1, - IUCV_SEVERED = 2, -}; - -enum tty_state_t { - TTY_CLOSED = 0, - TTY_OPENED = 1, -}; - -struct hvc_iucv_private { - struct hvc_struct *hvc; /* HVC struct reference */ - u8 srv_name[8]; /* IUCV service name (ebcdic) */ - unsigned char is_console; /* Linux console usage flag */ - enum iucv_state_t iucv_state; /* IUCV connection status */ - enum tty_state_t tty_state; /* TTY status */ - struct iucv_path *path; /* IUCV path pointer */ - spinlock_t lock; /* hvc_iucv_private lock */ -#define SNDBUF_SIZE (PAGE_SIZE) /* must be < MSG_MAX_DATALEN */ - void *sndbuf; /* send buffer */ - size_t sndbuf_len; /* length of send buffer */ -#define QUEUE_SNDBUF_DELAY (HZ / 25) - struct delayed_work sndbuf_work; /* work: send iucv msg(s) */ - wait_queue_head_t sndbuf_waitq; /* wait for send completion */ - struct list_head tty_outqueue; /* outgoing IUCV messages */ - struct list_head tty_inqueue; /* incoming IUCV messages */ - struct device *dev; /* device structure */ -}; - -struct iucv_tty_buffer { - struct list_head list; /* list pointer */ - struct iucv_message msg; /* store an IUCV message */ - size_t offset; /* data buffer offset */ - struct iucv_tty_msg *mbuf; /* buffer to store input/output data */ -}; - -/* IUCV callback handler */ -static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]); -static void hvc_iucv_path_severed(struct iucv_path *, u8[16]); -static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); -static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); - - -/* Kernel module parameter: use one terminal device as default */ -static unsigned long hvc_iucv_devices = 1; - -/* Array of allocated hvc iucv tty lines... */ -static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; -#define IUCV_HVC_CON_IDX (0) -/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */ -#define MAX_VMID_FILTER (500) -static size_t hvc_iucv_filter_size; -static void *hvc_iucv_filter; -static const char *hvc_iucv_filter_string; -static DEFINE_RWLOCK(hvc_iucv_filter_lock); - -/* Kmem cache and mempool for iucv_tty_buffer elements */ -static struct kmem_cache *hvc_iucv_buffer_cache; -static mempool_t *hvc_iucv_mempool; - -/* IUCV handler callback functions */ -static struct iucv_handler hvc_iucv_handler = { - .path_pending = hvc_iucv_path_pending, - .path_severed = hvc_iucv_path_severed, - .message_complete = hvc_iucv_msg_complete, - .message_pending = hvc_iucv_msg_pending, -}; - - -/** - * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance. - * @num: The HVC virtual terminal number (vtermno) - * - * This function returns the struct hvc_iucv_private instance that corresponds - * to the HVC virtual terminal number specified as parameter @num. - */ -struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) -{ - if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices)) - return NULL; - return hvc_iucv_table[num - HVC_IUCV_MAGIC]; -} - -/** - * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element. - * @size: Size of the internal buffer used to store data. - * @flags: Memory allocation flags passed to mempool. - * - * This function allocates a new struct iucv_tty_buffer element and, optionally, - * allocates an internal data buffer with the specified size @size. - * The internal data buffer is always allocated with GFP_DMA which is - * required for receiving and sending data with IUCV. - * Note: The total message size arises from the internal buffer size and the - * members of the iucv_tty_msg structure. - * The function returns NULL if memory allocation has failed. - */ -static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) -{ - struct iucv_tty_buffer *bufp; - - bufp = mempool_alloc(hvc_iucv_mempool, flags); - if (!bufp) - return NULL; - memset(bufp, 0, sizeof(*bufp)); - - if (size > 0) { - bufp->msg.length = MSG_SIZE(size); - bufp->mbuf = kmalloc(bufp->msg.length, flags | GFP_DMA); - if (!bufp->mbuf) { - mempool_free(bufp, hvc_iucv_mempool); - return NULL; - } - bufp->mbuf->version = MSG_VERSION; - bufp->mbuf->type = MSG_TYPE_DATA; - bufp->mbuf->datalen = (u16) size; - } - return bufp; -} - -/** - * destroy_tty_buffer() - destroy struct iucv_tty_buffer element. - * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL. - */ -static void destroy_tty_buffer(struct iucv_tty_buffer *bufp) -{ - kfree(bufp->mbuf); - mempool_free(bufp, hvc_iucv_mempool); -} - -/** - * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element. - * @list: List containing struct iucv_tty_buffer elements. - */ -static void destroy_tty_buffer_list(struct list_head *list) -{ - struct iucv_tty_buffer *ent, *next; - - list_for_each_entry_safe(ent, next, list, list) { - list_del(&ent->list); - destroy_tty_buffer(ent); - } -} - -/** - * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer. - * @priv: Pointer to struct hvc_iucv_private - * @buf: HVC buffer for writing received terminal data. - * @count: HVC buffer size. - * @has_more_data: Pointer to an int variable. - * - * The function picks up pending messages from the input queue and receives - * the message data that is then written to the specified buffer @buf. - * If the buffer size @count is less than the data message size, the - * message is kept on the input queue and @has_more_data is set to 1. - * If all message data has been written, the message is removed from - * the input queue. - * - * The function returns the number of bytes written to the terminal, zero if - * there are no pending data messages available or if there is no established - * IUCV path. - * If the IUCV path has been severed, then -EPIPE is returned to cause a - * hang up (that is issued by the HVC layer). - */ -static int hvc_iucv_write(struct hvc_iucv_private *priv, - char *buf, int count, int *has_more_data) -{ - struct iucv_tty_buffer *rb; - int written; - int rc; - - /* immediately return if there is no IUCV connection */ - if (priv->iucv_state == IUCV_DISCONN) - return 0; - - /* if the IUCV path has been severed, return -EPIPE to inform the - * HVC layer to hang up the tty device. */ - if (priv->iucv_state == IUCV_SEVERED) - return -EPIPE; - - /* check if there are pending messages */ - if (list_empty(&priv->tty_inqueue)) - return 0; - - /* receive an iucv message and flip data to the tty (ldisc) */ - rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list); - - written = 0; - if (!rb->mbuf) { /* message not yet received ... */ - /* allocate mem to store msg data; if no memory is available - * then leave the buffer on the list and re-try later */ - rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA); - if (!rb->mbuf) - return -ENOMEM; - - rc = __iucv_message_receive(priv->path, &rb->msg, 0, - rb->mbuf, rb->msg.length, NULL); - switch (rc) { - case 0: /* Successful */ - break; - case 2: /* No message found */ - case 9: /* Message purged */ - break; - default: - written = -EIO; - } - /* remove buffer if an error has occured or received data - * is not correct */ - if (rc || (rb->mbuf->version != MSG_VERSION) || - (rb->msg.length != MSG_SIZE(rb->mbuf->datalen))) - goto out_remove_buffer; - } - - switch (rb->mbuf->type) { - case MSG_TYPE_DATA: - written = min_t(int, rb->mbuf->datalen - rb->offset, count); - memcpy(buf, rb->mbuf->data + rb->offset, written); - if (written < (rb->mbuf->datalen - rb->offset)) { - rb->offset += written; - *has_more_data = 1; - goto out_written; - } - break; - - case MSG_TYPE_WINSIZE: - if (rb->mbuf->datalen != sizeof(struct winsize)) - break; - /* The caller must ensure that the hvc is locked, which - * is the case when called from hvc_iucv_get_chars() */ - __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); - break; - - case MSG_TYPE_ERROR: /* ignored ... */ - case MSG_TYPE_TERMENV: /* ignored ... */ - case MSG_TYPE_TERMIOS: /* ignored ... */ - break; - } - -out_remove_buffer: - list_del(&rb->list); - destroy_tty_buffer(rb); - *has_more_data = !list_empty(&priv->tty_inqueue); - -out_written: - return written; -} - -/** - * hvc_iucv_get_chars() - HVC get_chars operation. - * @vtermno: HVC virtual terminal number. - * @buf: Pointer to a buffer to store data - * @count: Size of buffer available for writing - * - * The HVC thread calls this method to read characters from the back-end. - * If an IUCV communication path has been established, pending IUCV messages - * are received and data is copied into buffer @buf up to @count bytes. - * - * Locking: The routine gets called under an irqsave() spinlock; and - * the routine locks the struct hvc_iucv_private->lock to call - * helper functions. - */ -static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) -{ - struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); - int written; - int has_more_data; - - if (count <= 0) - return 0; - - if (!priv) - return -ENODEV; - - spin_lock(&priv->lock); - has_more_data = 0; - written = hvc_iucv_write(priv, buf, count, &has_more_data); - spin_unlock(&priv->lock); - - /* if there are still messages on the queue... schedule another run */ - if (has_more_data) - hvc_kick(); - - return written; -} - -/** - * hvc_iucv_queue() - Buffer terminal data for sending. - * @priv: Pointer to struct hvc_iucv_private instance. - * @buf: Buffer containing data to send. - * @count: Size of buffer and amount of data to send. - * - * The function queues data for sending. To actually send the buffered data, - * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY). - * The function returns the number of data bytes that has been buffered. - * - * If the device is not connected, data is ignored and the function returns - * @count. - * If the buffer is full, the function returns 0. - * If an existing IUCV communicaton path has been severed, -EPIPE is returned - * (that can be passed to HVC layer to cause a tty hangup). - */ -static int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf, - int count) -{ - size_t len; - - if (priv->iucv_state == IUCV_DISCONN) - return count; /* ignore data */ - - if (priv->iucv_state == IUCV_SEVERED) - return -EPIPE; - - len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len); - if (!len) - return 0; - - memcpy(priv->sndbuf + priv->sndbuf_len, buf, len); - priv->sndbuf_len += len; - - if (priv->iucv_state == IUCV_CONNECTED) - schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY); - - return len; -} - -/** - * hvc_iucv_send() - Send an IUCV message containing terminal data. - * @priv: Pointer to struct hvc_iucv_private instance. - * - * If an IUCV communication path has been established, the buffered output data - * is sent via an IUCV message and the number of bytes sent is returned. - * Returns 0 if there is no established IUCV communication path or - * -EPIPE if an existing IUCV communicaton path has been severed. - */ -static int hvc_iucv_send(struct hvc_iucv_private *priv) -{ - struct iucv_tty_buffer *sb; - int rc, len; - - if (priv->iucv_state == IUCV_SEVERED) - return -EPIPE; - - if (priv->iucv_state == IUCV_DISCONN) - return -EIO; - - if (!priv->sndbuf_len) - return 0; - - /* allocate internal buffer to store msg data and also compute total - * message length */ - sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC); - if (!sb) - return -ENOMEM; - - memcpy(sb->mbuf->data, priv->sndbuf, priv->sndbuf_len); - sb->mbuf->datalen = (u16) priv->sndbuf_len; - sb->msg.length = MSG_SIZE(sb->mbuf->datalen); - - list_add_tail(&sb->list, &priv->tty_outqueue); - - rc = __iucv_message_send(priv->path, &sb->msg, 0, 0, - (void *) sb->mbuf, sb->msg.length); - if (rc) { - /* drop the message here; however we might want to handle - * 0x03 (msg limit reached) by trying again... */ - list_del(&sb->list); - destroy_tty_buffer(sb); - } - len = priv->sndbuf_len; - priv->sndbuf_len = 0; - - return len; -} - -/** - * hvc_iucv_sndbuf_work() - Send buffered data over IUCV - * @work: Work structure. - * - * This work queue function sends buffered output data over IUCV and, - * if not all buffered data could be sent, reschedules itself. - */ -static void hvc_iucv_sndbuf_work(struct work_struct *work) -{ - struct hvc_iucv_private *priv; - - priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work); - if (!priv) - return; - - spin_lock_bh(&priv->lock); - hvc_iucv_send(priv); - spin_unlock_bh(&priv->lock); -} - -/** - * hvc_iucv_put_chars() - HVC put_chars operation. - * @vtermno: HVC virtual terminal number. - * @buf: Pointer to an buffer to read data from - * @count: Size of buffer available for reading - * - * The HVC thread calls this method to write characters to the back-end. - * The function calls hvc_iucv_queue() to queue terminal data for sending. - * - * Locking: The method gets called under an irqsave() spinlock; and - * locks struct hvc_iucv_private->lock. - */ -static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) -{ - struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); - int queued; - - if (count <= 0) - return 0; - - if (!priv) - return -ENODEV; - - spin_lock(&priv->lock); - queued = hvc_iucv_queue(priv, buf, count); - spin_unlock(&priv->lock); - - return queued; -} - -/** - * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): the index of an struct - * hvc_iucv_private instance. - * - * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private - * instance that is derived from @id. Always returns 0. - * - * Locking: struct hvc_iucv_private->lock, spin_lock_bh - */ -static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id) -{ - struct hvc_iucv_private *priv; - - priv = hvc_iucv_get_private(id); - if (!priv) - return 0; - - spin_lock_bh(&priv->lock); - priv->tty_state = TTY_OPENED; - spin_unlock_bh(&priv->lock); - - return 0; -} - -/** - * hvc_iucv_cleanup() - Clean up and reset a z/VM IUCV HVC instance. - * @priv: Pointer to the struct hvc_iucv_private instance. - */ -static void hvc_iucv_cleanup(struct hvc_iucv_private *priv) -{ - destroy_tty_buffer_list(&priv->tty_outqueue); - destroy_tty_buffer_list(&priv->tty_inqueue); - - priv->tty_state = TTY_CLOSED; - priv->iucv_state = IUCV_DISCONN; - - priv->sndbuf_len = 0; -} - -/** - * tty_outqueue_empty() - Test if the tty outq is empty - * @priv: Pointer to struct hvc_iucv_private instance. - */ -static inline int tty_outqueue_empty(struct hvc_iucv_private *priv) -{ - int rc; - - spin_lock_bh(&priv->lock); - rc = list_empty(&priv->tty_outqueue); - spin_unlock_bh(&priv->lock); - - return rc; -} - -/** - * flush_sndbuf_sync() - Flush send buffer and wait for completion - * @priv: Pointer to struct hvc_iucv_private instance. - * - * The routine cancels a pending sndbuf work, calls hvc_iucv_send() - * to flush any buffered terminal output data and waits for completion. - */ -static void flush_sndbuf_sync(struct hvc_iucv_private *priv) -{ - int sync_wait; - - cancel_delayed_work_sync(&priv->sndbuf_work); - - spin_lock_bh(&priv->lock); - hvc_iucv_send(priv); /* force sending buffered data */ - sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */ - spin_unlock_bh(&priv->lock); - - if (sync_wait) - wait_event_timeout(priv->sndbuf_waitq, - tty_outqueue_empty(priv), HZ/10); -} - -/** - * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up - * @priv: Pointer to hvc_iucv_private structure - * - * This routine severs an existing IUCV communication path and hangs - * up the underlying HVC terminal device. - * The hang-up occurs only if an IUCV communication path is established; - * otherwise there is no need to hang up the terminal device. - * - * The IUCV HVC hang-up is separated into two steps: - * 1. After the IUCV path has been severed, the iucv_state is set to - * IUCV_SEVERED. - * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the - * IUCV_SEVERED state causes the tty hang-up in the HVC layer. - * - * If the tty has not yet been opened, clean up the hvc_iucv_private - * structure to allow re-connects. - * If the tty has been opened, let get_chars() return -EPIPE to signal - * the HVC layer to hang up the tty and, if so, wake up the HVC thread - * to call get_chars()... - * - * Special notes on hanging up a HVC terminal instantiated as console: - * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) - * 2. do_tty_hangup() calls tty->ops->close() for console_filp - * => no hangup notifier is called by HVC (default) - * 2. hvc_close() returns because of tty_hung_up_p(filp) - * => no delete notifier is called! - * Finally, the back-end is not being notified, thus, the tty session is - * kept active (TTY_OPEN) to be ready for re-connects. - * - * Locking: spin_lock(&priv->lock) w/o disabling bh - */ -static void hvc_iucv_hangup(struct hvc_iucv_private *priv) -{ - struct iucv_path *path; - - path = NULL; - spin_lock(&priv->lock); - if (priv->iucv_state == IUCV_CONNECTED) { - path = priv->path; - priv->path = NULL; - priv->iucv_state = IUCV_SEVERED; - if (priv->tty_state == TTY_CLOSED) - hvc_iucv_cleanup(priv); - else - /* console is special (see above) */ - if (priv->is_console) { - hvc_iucv_cleanup(priv); - priv->tty_state = TTY_OPENED; - } else - hvc_kick(); - } - spin_unlock(&priv->lock); - - /* finally sever path (outside of priv->lock due to lock ordering) */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } -} - -/** - * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): - * the index of an struct hvc_iucv_private instance. - * - * This routine notifies the HVC back-end that a tty hangup (carrier loss, - * virtual or otherwise) has occured. - * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup()) - * to keep an existing IUCV communication path established. - * (Background: vhangup() is called from user space (by getty or login) to - * disable writing to the tty by other applications). - * If the tty has been opened and an established IUCV path has been severed - * (we caused the tty hangup), the function calls hvc_iucv_cleanup(). - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) -{ - struct hvc_iucv_private *priv; - - priv = hvc_iucv_get_private(id); - if (!priv) - return; - - flush_sndbuf_sync(priv); - - spin_lock_bh(&priv->lock); - /* NOTE: If the hangup was scheduled by ourself (from the iucv - * path_servered callback [IUCV_SEVERED]), we have to clean up - * our structure and to set state to TTY_CLOSED. - * If the tty was hung up otherwise (e.g. vhangup()), then we - * ignore this hangup and keep an established IUCV path open... - * (...the reason is that we are not able to connect back to the - * client if we disconnect on hang up) */ - priv->tty_state = TTY_CLOSED; - - if (priv->iucv_state == IUCV_SEVERED) - hvc_iucv_cleanup(priv); - spin_unlock_bh(&priv->lock); -} - -/** - * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. - * @hp: Pointer to the HVC device (struct hvc_struct) - * @id: Additional data (originally passed to hvc_alloc): - * the index of an struct hvc_iucv_private instance. - * - * This routine notifies the HVC back-end that the last tty device fd has been - * closed. The function calls hvc_iucv_cleanup() to clean up the struct - * hvc_iucv_private instance. - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) -{ - struct hvc_iucv_private *priv; - struct iucv_path *path; - - priv = hvc_iucv_get_private(id); - if (!priv) - return; - - flush_sndbuf_sync(priv); - - spin_lock_bh(&priv->lock); - path = priv->path; /* save reference to IUCV path */ - priv->path = NULL; - hvc_iucv_cleanup(priv); - spin_unlock_bh(&priv->lock); - - /* sever IUCV path outside of priv->lock due to lock ordering of: - * priv->lock <--> iucv_table_lock */ - if (path) { - iucv_path_sever(path, NULL); - iucv_path_free(path); - } -} - -/** - * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID - * @ipvmid: Originating z/VM user ID (right padded with blanks) - * - * Returns 0 if the z/VM user ID @ipvmid is allowed to connection, otherwise - * non-zero. - */ -static int hvc_iucv_filter_connreq(u8 ipvmid[8]) -{ - size_t i; - - /* Note: default policy is ACCEPT if no filter is set */ - if (!hvc_iucv_filter_size) - return 0; - - for (i = 0; i < hvc_iucv_filter_size; i++) - if (0 == memcmp(ipvmid, hvc_iucv_filter + (8 * i), 8)) - return 0; - return 1; -} - -/** - * hvc_iucv_path_pending() - IUCV handler to process a connection request. - * @path: Pending path (struct iucv_path) - * @ipvmid: z/VM system identifier of originator - * @ipuser: User specified data for this path - * (AF_IUCV: port/service name and originator port) - * - * The function uses the @ipuser data to determine if the pending path belongs - * to a terminal managed by this device driver. - * If the path belongs to this driver, ensure that the terminal is not accessed - * multiple times (only one connection to a terminal is allowed). - * If the terminal is not yet connected, the pending path is accepted and is - * associated to the appropriate struct hvc_iucv_private instance. - * - * Returns 0 if @path belongs to a terminal managed by the this device driver; - * otherwise returns -ENODEV in order to dispatch this path to other handlers. - * - * Locking: struct hvc_iucv_private->lock - */ -static int hvc_iucv_path_pending(struct iucv_path *path, - u8 ipvmid[8], u8 ipuser[16]) -{ - struct hvc_iucv_private *priv; - u8 nuser_data[16]; - u8 vm_user_id[9]; - int i, rc; - - priv = NULL; - for (i = 0; i < hvc_iucv_devices; i++) - if (hvc_iucv_table[i] && - (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) { - priv = hvc_iucv_table[i]; - break; - } - if (!priv) - return -ENODEV; - - /* Enforce that ipvmid is allowed to connect to us */ - read_lock(&hvc_iucv_filter_lock); - rc = hvc_iucv_filter_connreq(ipvmid); - read_unlock(&hvc_iucv_filter_lock); - if (rc) { - iucv_path_sever(path, ipuser); - iucv_path_free(path); - memcpy(vm_user_id, ipvmid, 8); - vm_user_id[8] = 0; - pr_info("A connection request from z/VM user ID %s " - "was refused\n", vm_user_id); - return 0; - } - - spin_lock(&priv->lock); - - /* If the terminal is already connected or being severed, then sever - * this path to enforce that there is only ONE established communication - * path per terminal. */ - if (priv->iucv_state != IUCV_DISCONN) { - iucv_path_sever(path, ipuser); - iucv_path_free(path); - goto out_path_handled; - } - - /* accept path */ - memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */ - memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */ - path->msglim = 0xffff; /* IUCV MSGLIMIT */ - path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */ - rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv); - if (rc) { - iucv_path_sever(path, ipuser); - iucv_path_free(path); - goto out_path_handled; - } - priv->path = path; - priv->iucv_state = IUCV_CONNECTED; - - /* flush buffered output data... */ - schedule_delayed_work(&priv->sndbuf_work, 5); - -out_path_handled: - spin_unlock(&priv->lock); - return 0; -} - -/** - * hvc_iucv_path_severed() - IUCV handler to process a path sever. - * @path: Pending path (struct iucv_path) - * @ipuser: User specified data for this path - * (AF_IUCV: port/service name and originator port) - * - * This function calls the hvc_iucv_hangup() function for the - * respective IUCV HVC terminal. - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) -{ - struct hvc_iucv_private *priv = path->private; - - hvc_iucv_hangup(priv); -} - -/** - * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message. - * @path: Pending path (struct iucv_path) - * @msg: Pointer to the IUCV message - * - * The function puts an incoming message on the input queue for later - * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). - * If the tty has not yet been opened, the message is rejected. - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_msg_pending(struct iucv_path *path, - struct iucv_message *msg) -{ - struct hvc_iucv_private *priv = path->private; - struct iucv_tty_buffer *rb; - - /* reject messages that exceed max size of iucv_tty_msg->datalen */ - if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) { - iucv_message_reject(path, msg); - return; - } - - spin_lock(&priv->lock); - - /* reject messages if tty has not yet been opened */ - if (priv->tty_state == TTY_CLOSED) { - iucv_message_reject(path, msg); - goto unlock_return; - } - - /* allocate tty buffer to save iucv msg only */ - rb = alloc_tty_buffer(0, GFP_ATOMIC); - if (!rb) { - iucv_message_reject(path, msg); - goto unlock_return; /* -ENOMEM */ - } - rb->msg = *msg; - - list_add_tail(&rb->list, &priv->tty_inqueue); - - hvc_kick(); /* wake up hvc thread */ - -unlock_return: - spin_unlock(&priv->lock); -} - -/** - * hvc_iucv_msg_complete() - IUCV handler to process message completion - * @path: Pending path (struct iucv_path) - * @msg: Pointer to the IUCV message - * - * The function is called upon completion of message delivery to remove the - * message from the outqueue. Additional delivery information can be found - * msg->audit: rejected messages (0x040000 (IPADRJCT)), and - * purged messages (0x010000 (IPADPGNR)). - * - * Locking: struct hvc_iucv_private->lock - */ -static void hvc_iucv_msg_complete(struct iucv_path *path, - struct iucv_message *msg) -{ - struct hvc_iucv_private *priv = path->private; - struct iucv_tty_buffer *ent, *next; - LIST_HEAD(list_remove); - - spin_lock(&priv->lock); - list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list) - if (ent->msg.id == msg->id) { - list_move(&ent->list, &list_remove); - break; - } - wake_up(&priv->sndbuf_waitq); - spin_unlock(&priv->lock); - destroy_tty_buffer_list(&list_remove); -} - -/** - * hvc_iucv_pm_freeze() - Freeze PM callback - * @dev: IUVC HVC terminal device - * - * Sever an established IUCV communication path and - * trigger a hang-up of the underlying HVC terminal. - */ -static int hvc_iucv_pm_freeze(struct device *dev) -{ - struct hvc_iucv_private *priv = dev_get_drvdata(dev); - - local_bh_disable(); - hvc_iucv_hangup(priv); - local_bh_enable(); - - return 0; -} - -/** - * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback - * @dev: IUVC HVC terminal device - * - * Wake up the HVC thread to trigger hang-up and respective - * HVC back-end notifier invocations. - */ -static int hvc_iucv_pm_restore_thaw(struct device *dev) -{ - hvc_kick(); - return 0; -} - - -/* HVC operations */ -static const struct hv_ops hvc_iucv_ops = { - .get_chars = hvc_iucv_get_chars, - .put_chars = hvc_iucv_put_chars, - .notifier_add = hvc_iucv_notifier_add, - .notifier_del = hvc_iucv_notifier_del, - .notifier_hangup = hvc_iucv_notifier_hangup, -}; - -/* Suspend / resume device operations */ -static const struct dev_pm_ops hvc_iucv_pm_ops = { - .freeze = hvc_iucv_pm_freeze, - .thaw = hvc_iucv_pm_restore_thaw, - .restore = hvc_iucv_pm_restore_thaw, -}; - -/* IUCV HVC device driver */ -static struct device_driver hvc_iucv_driver = { - .name = KMSG_COMPONENT, - .bus = &iucv_bus, - .pm = &hvc_iucv_pm_ops, -}; - -/** - * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance - * @id: hvc_iucv_table index - * @is_console: Flag if the instance is used as Linux console - * - * This function allocates a new hvc_iucv_private structure and stores - * the instance in hvc_iucv_table at index @id. - * Returns 0 on success; otherwise non-zero. - */ -static int __init hvc_iucv_alloc(int id, unsigned int is_console) -{ - struct hvc_iucv_private *priv; - char name[9]; - int rc; - - priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->lock); - INIT_LIST_HEAD(&priv->tty_outqueue); - INIT_LIST_HEAD(&priv->tty_inqueue); - INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work); - init_waitqueue_head(&priv->sndbuf_waitq); - - priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL); - if (!priv->sndbuf) { - kfree(priv); - return -ENOMEM; - } - - /* set console flag */ - priv->is_console = is_console; - - /* allocate hvc device */ - priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ - HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); - if (IS_ERR(priv->hvc)) { - rc = PTR_ERR(priv->hvc); - goto out_error_hvc; - } - - /* notify HVC thread instead of using polling */ - priv->hvc->irq_requested = 1; - - /* setup iucv related information */ - snprintf(name, 9, "lnxhvc%-2d", id); - memcpy(priv->srv_name, name, 8); - ASCEBC(priv->srv_name, 8); - - /* create and setup device */ - priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); - if (!priv->dev) { - rc = -ENOMEM; - goto out_error_dev; - } - dev_set_name(priv->dev, "hvc_iucv%d", id); - dev_set_drvdata(priv->dev, priv); - priv->dev->bus = &iucv_bus; - priv->dev->parent = iucv_root; - priv->dev->driver = &hvc_iucv_driver; - priv->dev->release = (void (*)(struct device *)) kfree; - rc = device_register(priv->dev); - if (rc) { - put_device(priv->dev); - goto out_error_dev; - } - - hvc_iucv_table[id] = priv; - return 0; - -out_error_dev: - hvc_remove(priv->hvc); -out_error_hvc: - free_page((unsigned long) priv->sndbuf); - kfree(priv); - - return rc; -} - -/** - * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances - */ -static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) -{ - hvc_remove(priv->hvc); - device_unregister(priv->dev); - free_page((unsigned long) priv->sndbuf); - kfree(priv); -} - -/** - * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID - * @filter: String containing a comma-separated list of z/VM user IDs - */ -static const char *hvc_iucv_parse_filter(const char *filter, char *dest) -{ - const char *nextdelim, *residual; - size_t len; - - nextdelim = strchr(filter, ','); - if (nextdelim) { - len = nextdelim - filter; - residual = nextdelim + 1; - } else { - len = strlen(filter); - residual = filter + len; - } - - if (len == 0) - return ERR_PTR(-EINVAL); - - /* check for '\n' (if called from sysfs) */ - if (filter[len - 1] == '\n') - len--; - - if (len > 8) - return ERR_PTR(-EINVAL); - - /* pad with blanks and save upper case version of user ID */ - memset(dest, ' ', 8); - while (len--) - dest[len] = toupper(filter[len]); - return residual; -} - -/** - * hvc_iucv_setup_filter() - Set up z/VM user ID filter - * @filter: String consisting of a comma-separated list of z/VM user IDs - * - * The function parses the @filter string and creates an array containing - * the list of z/VM user ID filter entries. - * Return code 0 means success, -EINVAL if the filter is syntactically - * incorrect, -ENOMEM if there was not enough memory to allocate the - * filter list array, or -ENOSPC if too many z/VM user IDs have been specified. - */ -static int hvc_iucv_setup_filter(const char *val) -{ - const char *residual; - int err; - size_t size, count; - void *array, *old_filter; - - count = strlen(val); - if (count == 0 || (count == 1 && val[0] == '\n')) { - size = 0; - array = NULL; - goto out_replace_filter; /* clear filter */ - } - - /* count user IDs in order to allocate sufficient memory */ - size = 1; - residual = val; - while ((residual = strchr(residual, ',')) != NULL) { - residual++; - size++; - } - - /* check if the specified list exceeds the filter limit */ - if (size > MAX_VMID_FILTER) - return -ENOSPC; - - array = kzalloc(size * 8, GFP_KERNEL); - if (!array) - return -ENOMEM; - - count = size; - residual = val; - while (*residual && count) { - residual = hvc_iucv_parse_filter(residual, - array + ((size - count) * 8)); - if (IS_ERR(residual)) { - err = PTR_ERR(residual); - kfree(array); - goto out_err; - } - count--; - } - -out_replace_filter: - write_lock_bh(&hvc_iucv_filter_lock); - old_filter = hvc_iucv_filter; - hvc_iucv_filter_size = size; - hvc_iucv_filter = array; - write_unlock_bh(&hvc_iucv_filter_lock); - kfree(old_filter); - - err = 0; -out_err: - return err; -} - -/** - * param_set_vmidfilter() - Set z/VM user ID filter parameter - * @val: String consisting of a comma-separated list of z/VM user IDs - * @kp: Kernel parameter pointing to hvc_iucv_filter array - * - * The function sets up the z/VM user ID filter specified as comma-separated - * list of user IDs in @val. - * Note: If it is called early in the boot process, @val is stored and - * parsed later in hvc_iucv_init(). - */ -static int param_set_vmidfilter(const char *val, const struct kernel_param *kp) -{ - int rc; - - if (!MACHINE_IS_VM || !hvc_iucv_devices) - return -ENODEV; - - if (!val) - return -EINVAL; - - rc = 0; - if (slab_is_available()) - rc = hvc_iucv_setup_filter(val); - else - hvc_iucv_filter_string = val; /* defer... */ - return rc; -} - -/** - * param_get_vmidfilter() - Get z/VM user ID filter - * @buffer: Buffer to store z/VM user ID filter, - * (buffer size assumption PAGE_SIZE) - * @kp: Kernel parameter pointing to the hvc_iucv_filter array - * - * The function stores the filter as a comma-separated list of z/VM user IDs - * in @buffer. Typically, sysfs routines call this function for attr show. - */ -static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) -{ - int rc; - size_t index, len; - void *start, *end; - - if (!MACHINE_IS_VM || !hvc_iucv_devices) - return -ENODEV; - - rc = 0; - read_lock_bh(&hvc_iucv_filter_lock); - for (index = 0; index < hvc_iucv_filter_size; index++) { - start = hvc_iucv_filter + (8 * index); - end = memchr(start, ' ', 8); - len = (end) ? end - start : 8; - memcpy(buffer + rc, start, len); - rc += len; - buffer[rc++] = ','; - } - read_unlock_bh(&hvc_iucv_filter_lock); - if (rc) - buffer[--rc] = '\0'; /* replace last comma and update rc */ - return rc; -} - -#define param_check_vmidfilter(name, p) __param_check(name, p, void) - -static struct kernel_param_ops param_ops_vmidfilter = { - .set = param_set_vmidfilter, - .get = param_get_vmidfilter, -}; - -/** - * hvc_iucv_init() - z/VM IUCV HVC device driver initialization - */ -static int __init hvc_iucv_init(void) -{ - int rc; - unsigned int i; - - if (!hvc_iucv_devices) - return -ENODEV; - - if (!MACHINE_IS_VM) { - pr_notice("The z/VM IUCV HVC device driver cannot " - "be used without z/VM\n"); - rc = -ENODEV; - goto out_error; - } - - if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) { - pr_err("%lu is not a valid value for the hvc_iucv= " - "kernel parameter\n", hvc_iucv_devices); - rc = -EINVAL; - goto out_error; - } - - /* register IUCV HVC device driver */ - rc = driver_register(&hvc_iucv_driver); - if (rc) - goto out_error; - - /* parse hvc_iucv_allow string and create z/VM user ID filter list */ - if (hvc_iucv_filter_string) { - rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); - switch (rc) { - case 0: - break; - case -ENOMEM: - pr_err("Allocating memory failed with " - "reason code=%d\n", 3); - goto out_error; - case -EINVAL: - pr_err("hvc_iucv_allow= does not specify a valid " - "z/VM user ID list\n"); - goto out_error; - case -ENOSPC: - pr_err("hvc_iucv_allow= specifies too many " - "z/VM user IDs\n"); - goto out_error; - default: - goto out_error; - } - } - - hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, - sizeof(struct iucv_tty_buffer), - 0, 0, NULL); - if (!hvc_iucv_buffer_cache) { - pr_err("Allocating memory failed with reason code=%d\n", 1); - rc = -ENOMEM; - goto out_error; - } - - hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, - hvc_iucv_buffer_cache); - if (!hvc_iucv_mempool) { - pr_err("Allocating memory failed with reason code=%d\n", 2); - kmem_cache_destroy(hvc_iucv_buffer_cache); - rc = -ENOMEM; - goto out_error; - } - - /* register the first terminal device as console - * (must be done before allocating hvc terminal devices) */ - rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops); - if (rc) { - pr_err("Registering HVC terminal device as " - "Linux console failed\n"); - goto out_error_memory; - } - - /* allocate hvc_iucv_private structs */ - for (i = 0; i < hvc_iucv_devices; i++) { - rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0); - if (rc) { - pr_err("Creating a new HVC terminal device " - "failed with error code=%d\n", rc); - goto out_error_hvc; - } - } - - /* register IUCV callback handler */ - rc = iucv_register(&hvc_iucv_handler, 0); - if (rc) { - pr_err("Registering IUCV handlers failed with error code=%d\n", - rc); - goto out_error_hvc; - } - - return 0; - -out_error_hvc: - for (i = 0; i < hvc_iucv_devices; i++) - if (hvc_iucv_table[i]) - hvc_iucv_destroy(hvc_iucv_table[i]); -out_error_memory: - mempool_destroy(hvc_iucv_mempool); - kmem_cache_destroy(hvc_iucv_buffer_cache); -out_error: - if (hvc_iucv_filter) - kfree(hvc_iucv_filter); - hvc_iucv_devices = 0; /* ensure that we do not provide any device */ - return rc; -} - -/** - * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter - * @val: Parameter value (numeric) - */ -static int __init hvc_iucv_config(char *val) -{ - return strict_strtoul(val, 10, &hvc_iucv_devices); -} - - -device_initcall(hvc_iucv_init); -__setup("hvc_iucv=", hvc_iucv_config); -core_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640); diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c deleted file mode 100644 index 61c4a61558d9..000000000000 --- a/drivers/char/hvc_rtas.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * IBM RTAS driver interface to hvc_console.c - * - * (C) Copyright IBM Corporation 2001-2005 - * (C) Copyright Red Hat, Inc. 2005 - * - * Author(s): Maximino Augilar - * : Ryan S. Arnold - * : Utz Bacher - * : David Woodhouse - * - * inspired by drivers/char/hvc_console.c - * written by Anton Blanchard and Paul Mackerras - * - * 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 "hvc_console.h" - -#define hvc_rtas_cookie 0x67781e15 -struct hvc_struct *hvc_rtas_dev; - -static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE; -static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE; - -static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, - int count) -{ - int i; - - for (i = 0; i < count; i++) { - if (rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[i])) - break; - } - - return i; -} - -static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) -{ - int i, c; - - for (i = 0; i < count; i++) { - if (rtas_call(rtascons_get_char_token, 0, 2, &c)) - break; - - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_rtas_get_put_ops = { - .get_chars = hvc_rtas_read_console, - .put_chars = hvc_rtas_write_console, -}; - -static int __init hvc_rtas_init(void) -{ - struct hvc_struct *hp; - - if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) - rtascons_put_char_token = rtas_token("put-term-char"); - if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) - rtascons_get_char_token = rtas_token("get-term-char"); - if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - BUG_ON(hvc_rtas_dev); - - /* Allocate an hvc_struct for the console device we instantiated - * earlier. Save off hp so that we can return it on exit */ - hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - - hvc_rtas_dev = hp; - - return 0; -} -module_init(hvc_rtas_init); - -/* This will tear down the tty portion of the driver */ -static void __exit hvc_rtas_exit(void) -{ - /* Really the fun isn't over until the worker thread breaks down and - * the tty cleans up */ - if (hvc_rtas_dev) - hvc_remove(hvc_rtas_dev); -} -module_exit(hvc_rtas_exit); - -/* This will happen prior to module init. There is no tty at this time? */ -static int __init hvc_rtas_console_init(void) -{ - rtascons_put_char_token = rtas_token("put-term-char"); - if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - rtascons_get_char_token = rtas_token("get-term-char"); - if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) - return -EIO; - - hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops); - add_preferred_console("hvc", 0, NULL); - - return 0; -} -console_initcall(hvc_rtas_console_init); diff --git a/drivers/char/hvc_tile.c b/drivers/char/hvc_tile.c deleted file mode 100644 index 7a84a0595477..000000000000 --- a/drivers/char/hvc_tile.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010 Tilera Corporation. All Rights Reserved. - * - * 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, version 2. - * - * 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, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for - * more details. - * - * Tilera TILE Processor hypervisor console - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "hvc_console.h" - -static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count) -{ - return hv_console_write((HV_VirtAddr)buf, count); -} - -static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) -{ - int i, c; - - for (i = 0; i < count; ++i) { - c = hv_console_read_if_ready(); - if (c < 0) - break; - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_tile_get_put_ops = { - .get_chars = hvc_tile_get_chars, - .put_chars = hvc_tile_put_chars, -}; - -static int __init hvc_tile_console_init(void) -{ - extern void disable_early_printk(void); - hvc_instantiate(0, 0, &hvc_tile_get_put_ops); - add_preferred_console("hvc", 0, NULL); - disable_early_printk(); - return 0; -} -console_initcall(hvc_tile_console_init); - -static int __init hvc_tile_init(void) -{ - struct hvc_struct *s; - s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); - return IS_ERR(s) ? PTR_ERR(s) : 0; -} -device_initcall(hvc_tile_init); diff --git a/drivers/char/hvc_udbg.c b/drivers/char/hvc_udbg.c deleted file mode 100644 index b0957e61a7be..000000000000 --- a/drivers/char/hvc_udbg.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * udbg interface to hvc_console.c - * - * (C) Copyright David Gibson, IBM Corporation 2008. - * - * 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 "hvc_console.h" - -struct hvc_struct *hvc_udbg_dev; - -static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) -{ - int i; - - for (i = 0; i < count; i++) - udbg_putc(buf[i]); - - return i; -} - -static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) -{ - int i, c; - - if (!udbg_getc_poll) - return 0; - - for (i = 0; i < count; i++) { - if ((c = udbg_getc_poll()) == -1) - break; - buf[i] = c; - } - - return i; -} - -static const struct hv_ops hvc_udbg_ops = { - .get_chars = hvc_udbg_get, - .put_chars = hvc_udbg_put, -}; - -static int __init hvc_udbg_init(void) -{ - struct hvc_struct *hp; - - BUG_ON(hvc_udbg_dev); - - hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16); - if (IS_ERR(hp)) - return PTR_ERR(hp); - - hvc_udbg_dev = hp; - - return 0; -} -module_init(hvc_udbg_init); - -static void __exit hvc_udbg_exit(void) -{ - if (hvc_udbg_dev) - hvc_remove(hvc_udbg_dev); -} -module_exit(hvc_udbg_exit); - -static int __init hvc_udbg_console_init(void) -{ - hvc_instantiate(0, 0, &hvc_udbg_ops); - add_preferred_console("hvc", 0, NULL); - - return 0; -} -console_initcall(hvc_udbg_console_init); diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c deleted file mode 100644 index 5e2f52b33327..000000000000 --- a/drivers/char/hvc_vio.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * vio driver interface to hvc_console.c - * - * This code was moved here to allow the remaing code to be reused as a - * generic polling mode with semi-reliable transport driver core to the - * console and tty subsystems. - * - * - * Copyright (C) 2001 Anton Blanchard , IBM - * Copyright (C) 2001 Paul Mackerras , IBM - * Copyright (C) 2004 Benjamin Herrenschmidt , IBM Corp. - * Copyright (C) 2004 IBM Corporation - * - * Additional Author(s): - * Ryan S. Arnold - * - * 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 "hvc_console.h" - -static const char hvc_driver_name[] = "hvc_console"; - -static struct vio_device_id hvc_driver_table[] __devinitdata = { - {"serial", "hvterm1"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvc_driver_table); - -static int filtered_get_chars(uint32_t vtermno, char *buf, int count) -{ - unsigned long got; - int i; - - /* - * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion - * so we play safe and avoid the situation where got > count which could - * overload the flip buffer. - */ - if (count < SIZE_VIO_GET_CHARS) - return -EAGAIN; - - got = hvc_get_chars(vtermno, buf, count); - - /* - * Work around a HV bug where it gives us a null - * after every \r. -- paulus - */ - for (i = 1; i < got; ++i) { - if (buf[i] == 0 && buf[i-1] == '\r') { - --got; - if (i < got) - memmove(&buf[i], &buf[i+1], - got - i); - } - } - return got; -} - -static const struct hv_ops hvc_get_put_ops = { - .get_chars = filtered_get_chars, - .put_chars = hvc_put_chars, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __devinit hvc_vio_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - struct hvc_struct *hp; - - /* probed with invalid parameters. */ - if (!vdev || !id) - return -EPERM; - - hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, - MAX_VIO_PUT_CHARS); - if (IS_ERR(hp)) - return PTR_ERR(hp); - dev_set_drvdata(&vdev->dev, hp); - - return 0; -} - -static int __devexit hvc_vio_remove(struct vio_dev *vdev) -{ - struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); - - return hvc_remove(hp); -} - -static struct vio_driver hvc_vio_driver = { - .id_table = hvc_driver_table, - .probe = hvc_vio_probe, - .remove = __devexit_p(hvc_vio_remove), - .driver = { - .name = hvc_driver_name, - .owner = THIS_MODULE, - } -}; - -static int __init hvc_vio_init(void) -{ - int rc; - - if (firmware_has_feature(FW_FEATURE_ISERIES)) - return -EIO; - - /* Register as a vio device to receive callbacks */ - rc = vio_register_driver(&hvc_vio_driver); - - return rc; -} -module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ - -static void __exit hvc_vio_exit(void) -{ - vio_unregister_driver(&hvc_vio_driver); -} -module_exit(hvc_vio_exit); - -/* the device tree order defines our numbering */ -static int hvc_find_vtys(void) -{ - struct device_node *vty; - int num_found = 0; - - for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; - vty = of_find_node_by_name(vty, "vty")) { - const uint32_t *vtermno; - - /* We have statically defined space for only a certain number - * of console adapters. - */ - if (num_found >= MAX_NR_HVC_CONSOLES) { - of_node_put(vty); - break; - } - - vtermno = of_get_property(vty, "reg", NULL); - if (!vtermno) - continue; - - if (of_device_is_compatible(vty, "hvterm1")) { - hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); - ++num_found; - } - } - - return num_found; -} -console_initcall(hvc_find_vtys); diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c deleted file mode 100644 index 3740e327f180..000000000000 --- a/drivers/char/hvc_xen.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * xen console driver interface to hvc_console.c - * - * (c) 2007 Gerd Hoffmann - * - * 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 -#include - -#include "hvc_console.h" - -#define HVC_COOKIE 0x58656e /* "Xen" in hex */ - -static struct hvc_struct *hvc; -static int xencons_irq; - -/* ------------------------------------------------------------------ */ - -static unsigned long console_pfn = ~0ul; - -static inline struct xencons_interface *xencons_interface(void) -{ - if (console_pfn == ~0ul) - return mfn_to_virt(xen_start_info->console.domU.mfn); - else - return __va(console_pfn << PAGE_SHIFT); -} - -static inline void notify_daemon(void) -{ - /* Use evtchn: this is called early, before irq is set up. */ - notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); -} - -static int __write_console(const char *data, int len) -{ - struct xencons_interface *intf = xencons_interface(); - XENCONS_RING_IDX cons, prod; - int sent = 0; - - cons = intf->out_cons; - prod = intf->out_prod; - mb(); /* update queue values before going on */ - BUG_ON((prod - cons) > sizeof(intf->out)); - - while ((sent < len) && ((prod - cons) < sizeof(intf->out))) - intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; - - wmb(); /* write ring before updating pointer */ - intf->out_prod = prod; - - if (sent) - notify_daemon(); - return sent; -} - -static int domU_write_console(uint32_t vtermno, const char *data, int len) -{ - int ret = len; - - /* - * Make sure the whole buffer is emitted, polling if - * necessary. We don't ever want to rely on the hvc daemon - * because the most interesting console output is when the - * kernel is crippled. - */ - while (len) { - int sent = __write_console(data, len); - - data += sent; - len -= sent; - - if (unlikely(len)) - HYPERVISOR_sched_op(SCHEDOP_yield, NULL); - } - - return ret; -} - -static int domU_read_console(uint32_t vtermno, char *buf, int len) -{ - struct xencons_interface *intf = xencons_interface(); - XENCONS_RING_IDX cons, prod; - int recv = 0; - - cons = intf->in_cons; - prod = intf->in_prod; - mb(); /* get pointers before reading ring */ - BUG_ON((prod - cons) > sizeof(intf->in)); - - while (cons != prod && recv < len) - buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; - - mb(); /* read ring before consuming */ - intf->in_cons = cons; - - notify_daemon(); - return recv; -} - -static struct hv_ops domU_hvc_ops = { - .get_chars = domU_read_console, - .put_chars = domU_write_console, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int dom0_read_console(uint32_t vtermno, char *buf, int len) -{ - return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); -} - -/* - * Either for a dom0 to write to the system console, or a domU with a - * debug version of Xen - */ -static int dom0_write_console(uint32_t vtermno, const char *str, int len) -{ - int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); - if (rc < 0) - return 0; - - return len; -} - -static struct hv_ops dom0_hvc_ops = { - .get_chars = dom0_read_console, - .put_chars = dom0_write_console, - .notifier_add = notifier_add_irq, - .notifier_del = notifier_del_irq, - .notifier_hangup = notifier_hangup_irq, -}; - -static int __init xen_hvc_init(void) -{ - struct hvc_struct *hp; - struct hv_ops *ops; - - if (!xen_pv_domain()) - return -ENODEV; - - if (xen_initial_domain()) { - ops = &dom0_hvc_ops; - xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); - } else { - if (!xen_start_info->console.domU.evtchn) - return -ENODEV; - - ops = &domU_hvc_ops; - xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); - } - if (xencons_irq < 0) - xencons_irq = 0; /* NO_IRQ */ - - hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); - if (IS_ERR(hp)) - return PTR_ERR(hp); - - hvc = hp; - - console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); - - return 0; -} - -void xen_console_resume(void) -{ - if (xencons_irq) - rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); -} - -static void __exit xen_hvc_fini(void) -{ - if (hvc) - hvc_remove(hvc); -} - -static int xen_cons_init(void) -{ - struct hv_ops *ops; - - if (!xen_pv_domain()) - return 0; - - if (xen_initial_domain()) - ops = &dom0_hvc_ops; - else - ops = &domU_hvc_ops; - - hvc_instantiate(HVC_COOKIE, 0, ops); - return 0; -} - -module_init(xen_hvc_init); -module_exit(xen_hvc_fini); -console_initcall(xen_cons_init); - -#ifdef CONFIG_EARLY_PRINTK -static void xenboot_write_console(struct console *console, const char *string, - unsigned len) -{ - unsigned int linelen, off = 0; - const char *pos; - - dom0_write_console(0, string, len); - - if (xen_initial_domain()) - return; - - domU_write_console(0, "(early) ", 8); - while (off < len && NULL != (pos = strchr(string+off, '\n'))) { - linelen = pos-string+off; - if (off + linelen > len) - break; - domU_write_console(0, string+off, linelen); - domU_write_console(0, "\r\n", 2); - off += linelen + 1; - } - if (off < len) - domU_write_console(0, string+off, len-off); -} - -struct console xenboot_console = { - .name = "xenboot", - .write = xenboot_write_console, - .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, -}; -#endif /* CONFIG_EARLY_PRINTK */ - -void xen_raw_console_write(const char *str) -{ - dom0_write_console(0, str, strlen(str)); -} - -void xen_raw_printk(const char *fmt, ...) -{ - static char buf[512]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - xen_raw_console_write(buf); -} diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c deleted file mode 100644 index bedc6c1b6fa5..000000000000 --- a/drivers/char/hvcs.c +++ /dev/null @@ -1,1604 +0,0 @@ -/* - * IBM eServer Hypervisor Virtual Console Server Device Driver - * Copyright (C) 2003, 2004 IBM Corp. - * Ryan S. Arnold (rsa@us.ibm.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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 - * - * Author(s) : Ryan S. Arnold - * - * This is the device driver for the IBM Hypervisor Virtual Console Server, - * "hvcs". The IBM hvcs provides a tty driver interface to allow Linux - * user space applications access to the system consoles of logically - * partitioned operating systems, e.g. Linux, running on the same partitioned - * Power5 ppc64 system. Physical hardware consoles per partition are not - * practical on this hardware so system consoles are accessed by this driver - * using inter-partition firmware interfaces to virtual terminal devices. - * - * A vty is known to the HMC as a "virtual serial server adapter". It is a - * virtual terminal device that is created by firmware upon partition creation - * to act as a partitioned OS's console device. - * - * Firmware dynamically (via hotplug) exposes vty-servers to a running ppc64 - * Linux system upon their creation by the HMC or their exposure during boot. - * The non-user interactive backend of this driver is implemented as a vio - * device driver so that it can receive notification of vty-server lifetimes - * after it registers with the vio bus to handle vty-server probe and remove - * callbacks. - * - * Many vty-servers can be configured to connect to one vty, but a vty can - * only be actively connected to by a single vty-server, in any manner, at one - * time. If the HMC is currently hosting the console for a target Linux - * partition; attempts to open the tty device to the partition's console using - * the hvcs on any partition will return -EBUSY with every open attempt until - * the HMC frees the connection between its vty-server and the desired - * partition's vty device. Conversely, a vty-server may only be connected to - * a single vty at one time even though it may have several configured vty - * partner possibilities. - * - * Firmware does not provide notification of vty partner changes to this - * driver. This means that an HMC Super Admin may add or remove partner vtys - * from a vty-server's partner list but the changes will not be signaled to - * the vty-server. Firmware only notifies the driver when a vty-server is - * added or removed from the system. To compensate for this deficiency, this - * driver implements a sysfs update attribute which provides a method for - * rescanning partner information upon a user's request. - * - * Each vty-server, prior to being exposed to this driver is reference counted - * using the 2.6 Linux kernel kref construct. - * - * For direction on installation and usage of this driver please reference - * Documentation/powerpc/hvcs.txt. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * 1.3.0 -> 1.3.1 In hvcs_open memset(..,0x00,..) instead of memset(..,0x3F,00). - * Removed braces around single statements following conditionals. Removed '= - * 0' after static int declarations since these default to zero. Removed - * list_for_each_safe() and replaced with list_for_each_entry() in - * hvcs_get_by_index(). The 'safe' version is un-needed now that the driver is - * using spinlocks. Changed spin_lock_irqsave() to spin_lock() when locking - * hvcs_structs_lock and hvcs_pi_lock since these are not touched in an int - * handler. Initialized hvcs_structs_lock and hvcs_pi_lock to - * SPIN_LOCK_UNLOCKED at declaration time rather than in hvcs_module_init(). - * Added spin_lock around list_del() in destroy_hvcs_struct() to protect the - * list traversals from a deletion. Removed '= NULL' from pointer declaration - * statements since they are initialized NULL by default. Removed wmb() - * instances from hvcs_try_write(). They probably aren't needed with locking in - * place. Added check and cleanup for hvcs_pi_buff = kmalloc() in - * hvcs_module_init(). Exposed hvcs_struct.index via a sysfs attribute so that - * the coupling between /dev/hvcs* and a vty-server can be automatically - * determined. Moved kobject_put() in hvcs_open outside of the - * spin_unlock_irqrestore(). - * - * 1.3.1 -> 1.3.2 Changed method for determining hvcs_struct->index and had it - * align with how the tty layer always assigns the lowest index available. This - * change resulted in a list of ints that denotes which indexes are available. - * Device additions and removals use the new hvcs_get_index() and - * hvcs_return_index() helper functions. The list is created with - * hvsc_alloc_index_list() and it is destroyed with hvcs_free_index_list(). - * Without these fixes hotplug vty-server adapter support goes crazy with this - * driver if the user removes a vty-server adapter. Moved free_irq() outside of - * the hvcs_final_close() function in order to get it out of the spinlock. - * Rearranged hvcs_close(). Cleaned up some printks and did some housekeeping - * on the changelog. Removed local CLC_LENGTH and used HVCS_CLC_LENGTH from - * arch/powerepc/include/asm/hvcserver.h - * - * 1.3.2 -> 1.3.3 Replaced yield() in hvcs_close() with tty_wait_until_sent() to - * prevent possible lockup with realtime scheduling as similarily pointed out by - * akpm in hvc_console. Changed resulted in the removal of hvcs_final_close() - * to reorder cleanup operations and prevent discarding of pending data during - * an hvcs_close(). Removed spinlock protection of hvcs_struct data members in - * hvcs_write_room() and hvcs_chars_in_buffer() because they aren't needed. - */ - -#define HVCS_DRIVER_VERSION "1.3.3" - -MODULE_AUTHOR("Ryan S. Arnold "); -MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(HVCS_DRIVER_VERSION); - -/* - * Wait this long per iteration while trying to push buffered data to the - * hypervisor before allowing the tty to complete a close operation. - */ -#define HVCS_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ - -/* - * Since the Linux TTY code does not currently (2-04-2004) support dynamic - * addition of tty derived devices and we shouldn't allocate thousands of - * tty_device pointers when the number of vty-server & vty partner connections - * will most often be much lower than this, we'll arbitrarily allocate - * HVCS_DEFAULT_SERVER_ADAPTERS tty_structs and cdev's by default when we - * register the tty_driver. This can be overridden using an insmod parameter. - */ -#define HVCS_DEFAULT_SERVER_ADAPTERS 64 - -/* - * The user can't insmod with more than HVCS_MAX_SERVER_ADAPTERS hvcs device - * nodes as a sanity check. Theoretically there can be over 1 Billion - * vty-server & vty partner connections. - */ -#define HVCS_MAX_SERVER_ADAPTERS 1024 - -/* - * We let Linux assign us a major number and we start the minors at zero. There - * is no intuitive mapping between minor number and the target vty-server - * adapter except that each new vty-server adapter is always assigned to the - * smallest minor number available. - */ -#define HVCS_MINOR_START 0 - -/* - * The hcall interface involves putting 8 chars into each of two registers. - * We load up those 2 registers (in arch/powerpc/platforms/pseries/hvconsole.c) - * by casting char[16] to long[2]. It would work without __ALIGNED__, but a - * little (tiny) bit slower because an unaligned load is slower than aligned - * load. - */ -#define __ALIGNED__ __attribute__((__aligned__(8))) - -/* - * How much data can firmware send with each hvc_put_chars()? Maybe this - * should be moved into an architecture specific area. - */ -#define HVCS_BUFF_LEN 16 - -/* - * This is the maximum amount of data we'll let the user send us (hvcs_write) at - * once in a chunk as a sanity check. - */ -#define HVCS_MAX_FROM_USER 4096 - -/* - * Be careful when adding flags to this line discipline. Don't add anything - * that will cause echoing or we'll go into recursive loop echoing chars back - * and forth with the console drivers. - */ -static struct ktermios hvcs_tty_termios = { - .c_iflag = IGNBRK | IGNPAR, - .c_oflag = OPOST, - .c_cflag = B38400 | CS8 | CREAD | HUPCL, - .c_cc = INIT_C_CC, - .c_ispeed = 38400, - .c_ospeed = 38400 -}; - -/* - * This value is used to take the place of a command line parameter when the - * module is inserted. It starts as -1 and stays as such if the user doesn't - * specify a module insmod parameter. If they DO specify one then it is set to - * the value of the integer passed in. - */ -static int hvcs_parm_num_devs = -1; -module_param(hvcs_parm_num_devs, int, 0); - -static const char hvcs_driver_name[] = "hvcs"; -static const char hvcs_device_node[] = "hvcs"; -static const char hvcs_driver_string[] - = "IBM hvcs (Hypervisor Virtual Console Server) Driver"; - -/* Status of partner info rescan triggered via sysfs. */ -static int hvcs_rescan_status; - -static struct tty_driver *hvcs_tty_driver; - -/* - * In order to be somewhat sane this driver always associates the hvcs_struct - * index element with the numerically equal tty->index. This means that a - * hotplugged vty-server adapter will always map to the lowest index valued - * device node. If vty-servers were hotplug removed from the system and then - * new ones added the new vty-server may have the largest slot number of all - * the vty-server adapters in the partition but it may have the lowest dev node - * index of all the adapters due to the hole left by the hotplug removed - * adapter. There are a set of functions provided to get the lowest index for - * a new device as well as return the index to the list. This list is allocated - * with a number of elements equal to the number of device nodes requested when - * the module was inserted. - */ -static int *hvcs_index_list; - -/* - * How large is the list? This is kept for traversal since the list is - * dynamically created. - */ -static int hvcs_index_count; - -/* - * Used by the khvcsd to pick up I/O operations when the kernel_thread is - * already awake but potentially shifted to TASK_INTERRUPTIBLE state. - */ -static int hvcs_kicked; - -/* - * Use by the kthread construct for task operations like waking the sleeping - * thread and stopping the kthread. - */ -static struct task_struct *hvcs_task; - -/* - * We allocate this for the use of all of the hvcs_structs when they fetch - * partner info. - */ -static unsigned long *hvcs_pi_buff; - -/* Only allow one hvcs_struct to use the hvcs_pi_buff at a time. */ -static DEFINE_SPINLOCK(hvcs_pi_lock); - -/* One vty-server per hvcs_struct */ -struct hvcs_struct { - spinlock_t lock; - - /* - * This index identifies this hvcs device as the complement to a - * specific tty index. - */ - unsigned int index; - - struct tty_struct *tty; - int open_count; - - /* - * Used to tell the driver kernel_thread what operations need to take - * place upon this hvcs_struct instance. - */ - int todo_mask; - - /* - * This buffer is required so that when hvcs_write_room() reports that - * it can send HVCS_BUFF_LEN characters that it will buffer the full - * HVCS_BUFF_LEN characters if need be. This is essential for opost - * writes since they do not do high level buffering and expect to be - * able to send what the driver commits to sending buffering - * [e.g. tab to space conversions in n_tty.c opost()]. - */ - char buffer[HVCS_BUFF_LEN]; - int chars_in_buffer; - - /* - * Any variable below the kref is valid before a tty is connected and - * stays valid after the tty is disconnected. These shouldn't be - * whacked until the koject refcount reaches zero though some entries - * may be changed via sysfs initiatives. - */ - struct kref kref; /* ref count & hvcs_struct lifetime */ - int connected; /* is the vty-server currently connected to a vty? */ - uint32_t p_unit_address; /* partner unit address */ - uint32_t p_partition_ID; /* partner partition ID */ - char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */ - struct list_head next; /* list management */ - struct vio_dev *vdev; -}; - -/* Required to back map a kref to its containing object */ -#define from_kref(k) container_of(k, struct hvcs_struct, kref) - -static LIST_HEAD(hvcs_structs); -static DEFINE_SPINLOCK(hvcs_structs_lock); - -static void hvcs_unthrottle(struct tty_struct *tty); -static void hvcs_throttle(struct tty_struct *tty); -static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance); - -static int hvcs_write(struct tty_struct *tty, - const unsigned char *buf, int count); -static int hvcs_write_room(struct tty_struct *tty); -static int hvcs_chars_in_buffer(struct tty_struct *tty); - -static int hvcs_has_pi(struct hvcs_struct *hvcsd); -static void hvcs_set_pi(struct hvcs_partner_info *pi, - struct hvcs_struct *hvcsd); -static int hvcs_get_pi(struct hvcs_struct *hvcsd); -static int hvcs_rescan_devices_list(void); - -static int hvcs_partner_connect(struct hvcs_struct *hvcsd); -static void hvcs_partner_free(struct hvcs_struct *hvcsd); - -static int hvcs_enable_device(struct hvcs_struct *hvcsd, - uint32_t unit_address, unsigned int irq, struct vio_dev *dev); - -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, - 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); - -#define HVCS_SCHED_READ 0x00000001 -#define HVCS_QUICK_READ 0x00000002 -#define HVCS_TRY_WRITE 0x00000004 -#define HVCS_READ_MASK (HVCS_SCHED_READ | HVCS_QUICK_READ) - -static inline struct hvcs_struct *from_vio_dev(struct vio_dev *viod) -{ - return dev_get_drvdata(&viod->dev); -} -/* The sysfs interface for the driver and devices */ - -static ssize_t hvcs_partner_vtys_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%X\n", hvcsd->p_unit_address); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} -static DEVICE_ATTR(partner_vtys, S_IRUGO, hvcs_partner_vtys_show, NULL); - -static ssize_t hvcs_partner_clcs_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} -static DEVICE_ATTR(partner_clcs, S_IRUGO, hvcs_partner_clcs_show, NULL); - -static ssize_t hvcs_current_vty_store(struct device *dev, struct device_attribute *attr, const char * buf, - size_t count) -{ - /* - * Don't need this feature at the present time because firmware doesn't - * yet support multiple partners. - */ - printk(KERN_INFO "HVCS: Denied current_vty change: -EPERM.\n"); - return -EPERM; -} - -static ssize_t hvcs_current_vty_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} - -static DEVICE_ATTR(current_vty, - S_IRUGO | S_IWUSR, hvcs_current_vty_show, hvcs_current_vty_store); - -static ssize_t hvcs_vterm_state_store(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - - /* writing a '0' to this sysfs entry will result in the disconnect. */ - if (simple_strtol(buf, NULL, 0) != 0) - return -EINVAL; - - spin_lock_irqsave(&hvcsd->lock, flags); - - if (hvcsd->open_count > 0) { - spin_unlock_irqrestore(&hvcsd->lock, flags); - printk(KERN_INFO "HVCS: vterm state unchanged. " - "The hvcs device node is still in use.\n"); - return -EPERM; - } - - if (hvcsd->connected == 0) { - spin_unlock_irqrestore(&hvcsd->lock, flags); - printk(KERN_INFO "HVCS: vterm state unchanged. The" - " vty-server is not connected to a vty.\n"); - return -EPERM; - } - - hvcs_partner_free(hvcsd); - printk(KERN_INFO "HVCS: Closed vty-server@%X and" - " partner vty@%X:%d connection.\n", - hvcsd->vdev->unit_address, - hvcsd->p_unit_address, - (uint32_t)hvcsd->p_partition_ID); - - spin_unlock_irqrestore(&hvcsd->lock, flags); - return count; -} - -static ssize_t hvcs_vterm_state_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%d\n", hvcsd->connected); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} -static DEVICE_ATTR(vterm_state, S_IRUGO | S_IWUSR, - hvcs_vterm_state_show, hvcs_vterm_state_store); - -static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct vio_dev *viod = to_vio_dev(dev); - struct hvcs_struct *hvcsd = from_vio_dev(viod); - unsigned long flags; - int retval; - - spin_lock_irqsave(&hvcsd->lock, flags); - retval = sprintf(buf, "%d\n", hvcsd->index); - spin_unlock_irqrestore(&hvcsd->lock, flags); - return retval; -} - -static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); - -static struct attribute *hvcs_attrs[] = { - &dev_attr_partner_vtys.attr, - &dev_attr_partner_clcs.attr, - &dev_attr_current_vty.attr, - &dev_attr_vterm_state.attr, - &dev_attr_index.attr, - NULL, -}; - -static struct attribute_group hvcs_attr_group = { - .attrs = hvcs_attrs, -}; - -static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf) -{ - /* A 1 means it is updating, a 0 means it is done updating */ - return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status); -} - -static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf, - size_t count) -{ - if ((simple_strtol(buf, NULL, 0) != 1) - && (hvcs_rescan_status != 0)) - return -EINVAL; - - hvcs_rescan_status = 1; - printk(KERN_INFO "HVCS: rescanning partner info for all" - " vty-servers.\n"); - hvcs_rescan_devices_list(); - hvcs_rescan_status = 0; - return count; -} - -static DRIVER_ATTR(rescan, - S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store); - -static void hvcs_kick(void) -{ - hvcs_kicked = 1; - wmb(); - wake_up_process(hvcs_task); -} - -static void hvcs_unthrottle(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&hvcsd->lock, flags); - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); - hvcs_kick(); -} - -static void hvcs_throttle(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&hvcsd->lock, flags); - vio_disable_interrupts(hvcsd->vdev); - spin_unlock_irqrestore(&hvcsd->lock, flags); -} - -/* - * If the device is being removed we don't have to worry about this interrupt - * handler taking any further interrupts because they are disabled which means - * the hvcs_struct will always be valid in this handler. - */ -static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance) -{ - struct hvcs_struct *hvcsd = dev_instance; - - spin_lock(&hvcsd->lock); - vio_disable_interrupts(hvcsd->vdev); - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock(&hvcsd->lock); - hvcs_kick(); - - return IRQ_HANDLED; -} - -/* This function must be called with the hvcsd->lock held */ -static void hvcs_try_write(struct hvcs_struct *hvcsd) -{ - uint32_t unit_address = hvcsd->vdev->unit_address; - struct tty_struct *tty = hvcsd->tty; - int sent; - - if (hvcsd->todo_mask & HVCS_TRY_WRITE) { - /* won't send partial writes */ - sent = hvc_put_chars(unit_address, - &hvcsd->buffer[0], - hvcsd->chars_in_buffer ); - if (sent > 0) { - hvcsd->chars_in_buffer = 0; - /* wmb(); */ - hvcsd->todo_mask &= ~(HVCS_TRY_WRITE); - /* wmb(); */ - - /* - * We are still obligated to deliver the data to the - * hypervisor even if the tty has been closed because - * we commited to delivering it. But don't try to wake - * a non-existent tty. - */ - if (tty) { - tty_wakeup(tty); - } - } - } -} - -static int hvcs_io(struct hvcs_struct *hvcsd) -{ - uint32_t unit_address; - struct tty_struct *tty; - char buf[HVCS_BUFF_LEN] __ALIGNED__; - unsigned long flags; - int got = 0; - - spin_lock_irqsave(&hvcsd->lock, flags); - - unit_address = hvcsd->vdev->unit_address; - tty = hvcsd->tty; - - hvcs_try_write(hvcsd); - - if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) { - hvcsd->todo_mask &= ~(HVCS_READ_MASK); - goto bail; - } else if (!(hvcsd->todo_mask & (HVCS_READ_MASK))) - goto bail; - - /* remove the read masks */ - hvcsd->todo_mask &= ~(HVCS_READ_MASK); - - if (tty_buffer_request_room(tty, HVCS_BUFF_LEN) >= HVCS_BUFF_LEN) { - got = hvc_get_chars(unit_address, - &buf[0], - HVCS_BUFF_LEN); - tty_insert_flip_string(tty, buf, got); - } - - /* Give the TTY time to process the data we just sent. */ - if (got) - hvcsd->todo_mask |= HVCS_QUICK_READ; - - spin_unlock_irqrestore(&hvcsd->lock, flags); - /* This is synch because tty->low_latency == 1 */ - if(got) - tty_flip_buffer_push(tty); - - if (!got) { - /* Do this _after_ the flip_buffer_push */ - spin_lock_irqsave(&hvcsd->lock, flags); - vio_enable_interrupts(hvcsd->vdev); - spin_unlock_irqrestore(&hvcsd->lock, flags); - } - - return hvcsd->todo_mask; - - bail: - spin_unlock_irqrestore(&hvcsd->lock, flags); - return hvcsd->todo_mask; -} - -static int khvcsd(void *unused) -{ - struct hvcs_struct *hvcsd; - int hvcs_todo_mask; - - __set_current_state(TASK_RUNNING); - - do { - hvcs_todo_mask = 0; - hvcs_kicked = 0; - wmb(); - - spin_lock(&hvcs_structs_lock); - list_for_each_entry(hvcsd, &hvcs_structs, next) { - hvcs_todo_mask |= hvcs_io(hvcsd); - } - spin_unlock(&hvcs_structs_lock); - - /* - * If any of the hvcs adapters want to try a write or quick read - * don't schedule(), yield a smidgen then execute the hvcs_io - * thread again for those that want the write. - */ - if (hvcs_todo_mask & (HVCS_TRY_WRITE | HVCS_QUICK_READ)) { - yield(); - continue; - } - - set_current_state(TASK_INTERRUPTIBLE); - if (!hvcs_kicked) - schedule(); - __set_current_state(TASK_RUNNING); - } while (!kthread_should_stop()); - - return 0; -} - -static struct vio_device_id hvcs_driver_table[] __devinitdata= { - {"serial-server", "hvterm2"}, - { "", "" } -}; -MODULE_DEVICE_TABLE(vio, hvcs_driver_table); - -static void hvcs_return_index(int index) -{ - /* Paranoia check */ - if (!hvcs_index_list) - return; - if (index < 0 || index >= hvcs_index_count) - return; - if (hvcs_index_list[index] == -1) - return; - else - hvcs_index_list[index] = -1; -} - -/* callback when the kref ref count reaches zero */ -static void destroy_hvcs_struct(struct kref *kref) -{ - struct hvcs_struct *hvcsd = from_kref(kref); - struct vio_dev *vdev; - unsigned long flags; - - spin_lock(&hvcs_structs_lock); - spin_lock_irqsave(&hvcsd->lock, flags); - - /* the list_del poisons the pointers */ - list_del(&(hvcsd->next)); - - if (hvcsd->connected == 1) { - hvcs_partner_free(hvcsd); - printk(KERN_INFO "HVCS: Closed vty-server@%X and" - " partner vty@%X:%d connection.\n", - hvcsd->vdev->unit_address, - hvcsd->p_unit_address, - (uint32_t)hvcsd->p_partition_ID); - } - printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n", - hvcsd->vdev->unit_address); - - vdev = hvcsd->vdev; - hvcsd->vdev = NULL; - - hvcsd->p_unit_address = 0; - hvcsd->p_partition_ID = 0; - hvcs_return_index(hvcsd->index); - memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); - - spin_unlock_irqrestore(&hvcsd->lock, flags); - spin_unlock(&hvcs_structs_lock); - - sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group); - - kfree(hvcsd); -} - -static int hvcs_get_index(void) -{ - int i; - /* Paranoia check */ - if (!hvcs_index_list) { - printk(KERN_ERR "HVCS: hvcs_index_list NOT valid!.\n"); - return -EFAULT; - } - /* Find the numerically lowest first free index. */ - for(i = 0; i < hvcs_index_count; i++) { - if (hvcs_index_list[i] == -1) { - hvcs_index_list[i] = 0; - return i; - } - } - return -1; -} - -static int __devinit hvcs_probe( - struct vio_dev *dev, - const struct vio_device_id *id) -{ - struct hvcs_struct *hvcsd; - int index; - int retval; - - if (!dev || !id) { - printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); - return -EPERM; - } - - /* early to avoid cleanup on failure */ - index = hvcs_get_index(); - if (index < 0) { - return -EFAULT; - } - - hvcsd = kzalloc(sizeof(*hvcsd), GFP_KERNEL); - if (!hvcsd) - return -ENODEV; - - - spin_lock_init(&hvcsd->lock); - /* Automatically incs the refcount the first time */ - kref_init(&hvcsd->kref); - - hvcsd->vdev = dev; - dev_set_drvdata(&dev->dev, hvcsd); - - hvcsd->index = index; - - /* hvcsd->index = ++hvcs_struct_count; */ - hvcsd->chars_in_buffer = 0; - hvcsd->todo_mask = 0; - hvcsd->connected = 0; - - /* - * This will populate the hvcs_struct's partner info fields for the - * first time. - */ - if (hvcs_get_pi(hvcsd)) { - printk(KERN_ERR "HVCS: Failed to fetch partner" - " info for vty-server@%X on device probe.\n", - hvcsd->vdev->unit_address); - } - - /* - * If a user app opens a tty that corresponds to this vty-server before - * the hvcs_struct has been added to the devices list then the user app - * will get -ENODEV. - */ - spin_lock(&hvcs_structs_lock); - list_add_tail(&(hvcsd->next), &hvcs_structs); - spin_unlock(&hvcs_structs_lock); - - retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group); - if (retval) { - printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n", - hvcsd->vdev->unit_address); - return retval; - } - - printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); - - /* - * DON'T enable interrupts here because there is no user to receive the - * data. - */ - return 0; -} - -static int __devexit hvcs_remove(struct vio_dev *dev) -{ - struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); - unsigned long flags; - struct tty_struct *tty; - - if (!hvcsd) - return -ENODEV; - - /* By this time the vty-server won't be getting any more interrupts */ - - spin_lock_irqsave(&hvcsd->lock, flags); - - tty = hvcsd->tty; - - spin_unlock_irqrestore(&hvcsd->lock, flags); - - /* - * Let the last holder of this object cause it to be removed, which - * would probably be tty_hangup below. - */ - kref_put(&hvcsd->kref, destroy_hvcs_struct); - - /* - * The hangup is a scheduled function which will auto chain call - * hvcs_hangup. The tty should always be valid at this time unless a - * simultaneous tty close already cleaned up the hvcs_struct. - */ - if (tty) - tty_hangup(tty); - - printk(KERN_INFO "HVCS: vty-server@%X removed from the" - " vio bus.\n", dev->unit_address); - return 0; -}; - -static struct vio_driver hvcs_vio_driver = { - .id_table = hvcs_driver_table, - .probe = hvcs_probe, - .remove = __devexit_p(hvcs_remove), - .driver = { - .name = hvcs_driver_name, - .owner = THIS_MODULE, - } -}; - -/* Only called from hvcs_get_pi please */ -static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd) -{ - int clclength; - - hvcsd->p_unit_address = pi->unit_address; - hvcsd->p_partition_ID = pi->partition_ID; - clclength = strlen(&pi->location_code[0]); - if (clclength > HVCS_CLC_LENGTH) - clclength = HVCS_CLC_LENGTH; - - /* copy the null-term char too */ - strncpy(&hvcsd->p_location_code[0], - &pi->location_code[0], clclength + 1); -} - -/* - * Traverse the list and add the partner info that is found to the hvcs_struct - * struct entry. NOTE: At this time I know that partner info will return a - * single entry but in the future there may be multiple partner info entries per - * vty-server and you'll want to zero out that list and reset it. If for some - * reason you have an old version of this driver but there IS more than one - * partner info then hvcsd->p_* will hold the last partner info data from the - * firmware query. A good way to update this code would be to replace the three - * partner info fields in hvcs_struct with a list of hvcs_partner_info - * instances. - * - * This function must be called with the hvcsd->lock held. - */ -static int hvcs_get_pi(struct hvcs_struct *hvcsd) -{ - struct hvcs_partner_info *pi; - uint32_t unit_address = hvcsd->vdev->unit_address; - struct list_head head; - int retval; - - spin_lock(&hvcs_pi_lock); - if (!hvcs_pi_buff) { - spin_unlock(&hvcs_pi_lock); - return -EFAULT; - } - retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff); - spin_unlock(&hvcs_pi_lock); - if (retval) { - printk(KERN_ERR "HVCS: Failed to fetch partner" - " info for vty-server@%x.\n", unit_address); - return retval; - } - - /* nixes the values if the partner vty went away */ - hvcsd->p_unit_address = 0; - hvcsd->p_partition_ID = 0; - - list_for_each_entry(pi, &head, node) - hvcs_set_pi(pi, hvcsd); - - hvcs_free_partner_info(&head); - return 0; -} - -/* - * This function is executed by the driver "rescan" sysfs entry. It shouldn't - * be executed elsewhere, in order to prevent deadlock issues. - */ -static int hvcs_rescan_devices_list(void) -{ - struct hvcs_struct *hvcsd; - unsigned long flags; - - spin_lock(&hvcs_structs_lock); - - list_for_each_entry(hvcsd, &hvcs_structs, next) { - spin_lock_irqsave(&hvcsd->lock, flags); - hvcs_get_pi(hvcsd); - spin_unlock_irqrestore(&hvcsd->lock, flags); - } - - spin_unlock(&hvcs_structs_lock); - - return 0; -} - -/* - * Farm this off into its own function because it could be more complex once - * multiple partners support is added. This function should be called with - * the hvcsd->lock held. - */ -static int hvcs_has_pi(struct hvcs_struct *hvcsd) -{ - if ((!hvcsd->p_unit_address) || (!hvcsd->p_partition_ID)) - return 0; - return 1; -} - -/* - * NOTE: It is possible that the super admin removed a partner vty and then - * added a different vty as the new partner. - * - * This function must be called with the hvcsd->lock held. - */ -static int hvcs_partner_connect(struct hvcs_struct *hvcsd) -{ - int retval; - unsigned int unit_address = hvcsd->vdev->unit_address; - - /* - * If there wasn't any pi when the device was added it doesn't meant - * there isn't any now. This driver isn't notified when a new partner - * vty is added to a vty-server so we discover changes on our own. - * Please see comments in hvcs_register_connection() for justification - * of this bizarre code. - */ - retval = hvcs_register_connection(unit_address, - hvcsd->p_partition_ID, - hvcsd->p_unit_address); - if (!retval) { - hvcsd->connected = 1; - return 0; - } else if (retval != -EINVAL) - return retval; - - /* - * As per the spec re-get the pi and try again if -EINVAL after the - * first connection attempt. - */ - if (hvcs_get_pi(hvcsd)) - return -ENOMEM; - - if (!hvcs_has_pi(hvcsd)) - return -ENODEV; - - retval = hvcs_register_connection(unit_address, - hvcsd->p_partition_ID, - hvcsd->p_unit_address); - if (retval != -EINVAL) { - hvcsd->connected = 1; - return retval; - } - - /* - * EBUSY is the most likely scenario though the vty could have been - * removed or there really could be an hcall error due to the parameter - * data but thanks to ambiguous firmware return codes we can't really - * tell. - */ - printk(KERN_INFO "HVCS: vty-server or partner" - " vty is busy. Try again later.\n"); - return -EBUSY; -} - -/* This function must be called with the hvcsd->lock held */ -static void hvcs_partner_free(struct hvcs_struct *hvcsd) -{ - int retval; - do { - retval = hvcs_free_connection(hvcsd->vdev->unit_address); - } while (retval == -EBUSY); - hvcsd->connected = 0; -} - -/* This helper function must be called WITHOUT the hvcsd->lock held */ -static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, - unsigned int irq, struct vio_dev *vdev) -{ - unsigned long flags; - int rc; - - /* - * It is possible that the vty-server was removed between the time that - * the conn was registered and now. - */ - if (!(rc = request_irq(irq, &hvcs_handle_interrupt, - IRQF_DISABLED, "ibmhvcs", hvcsd))) { - /* - * It is possible the vty-server was removed after the irq was - * requested but before we have time to enable interrupts. - */ - if (vio_enable_interrupts(vdev) == H_SUCCESS) - return 0; - else { - printk(KERN_ERR "HVCS: int enable failed for" - " vty-server@%X.\n", unit_address); - free_irq(irq, hvcsd); - } - } else - printk(KERN_ERR "HVCS: irq req failed for" - " vty-server@%X.\n", unit_address); - - spin_lock_irqsave(&hvcsd->lock, flags); - hvcs_partner_free(hvcsd); - spin_unlock_irqrestore(&hvcsd->lock, flags); - - return rc; - -} - -/* - * This always increments the kref ref count if the call is successful. - * Please remember to dec when you are done with the instance. - * - * NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when - * calling this function or you will get deadlock. - */ -static struct hvcs_struct *hvcs_get_by_index(int index) -{ - struct hvcs_struct *hvcsd = NULL; - unsigned long flags; - - spin_lock(&hvcs_structs_lock); - /* We can immediately discard OOB requests */ - if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) { - list_for_each_entry(hvcsd, &hvcs_structs, next) { - spin_lock_irqsave(&hvcsd->lock, flags); - if (hvcsd->index == index) { - kref_get(&hvcsd->kref); - spin_unlock_irqrestore(&hvcsd->lock, flags); - spin_unlock(&hvcs_structs_lock); - return hvcsd; - } - spin_unlock_irqrestore(&hvcsd->lock, flags); - } - hvcsd = NULL; - } - - spin_unlock(&hvcs_structs_lock); - return hvcsd; -} - -/* - * This is invoked via the tty_open interface when a user app connects to the - * /dev node. - */ -static int hvcs_open(struct tty_struct *tty, struct file *filp) -{ - struct hvcs_struct *hvcsd; - int rc, retval = 0; - unsigned long flags; - unsigned int irq; - struct vio_dev *vdev; - unsigned long unit_address; - - if (tty->driver_data) - goto fast_open; - - /* - * Is there a vty-server that shares the same index? - * This function increments the kref index. - */ - if (!(hvcsd = hvcs_get_by_index(tty->index))) { - printk(KERN_WARNING "HVCS: open failed, no device associated" - " with tty->index %d.\n", tty->index); - return -ENODEV; - } - - spin_lock_irqsave(&hvcsd->lock, flags); - - if (hvcsd->connected == 0) - if ((retval = hvcs_partner_connect(hvcsd))) - goto error_release; - - hvcsd->open_count = 1; - hvcsd->tty = tty; - tty->driver_data = hvcsd; - - memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); - - /* - * Save these in the spinlock for the enable operations that need them - * outside of the spinlock. - */ - irq = hvcsd->vdev->irq; - vdev = hvcsd->vdev; - unit_address = hvcsd->vdev->unit_address; - - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); - - /* - * This must be done outside of the spinlock because it requests irqs - * and will grab the spinlock and free the connection if it fails. - */ - if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { - kref_put(&hvcsd->kref, destroy_hvcs_struct); - printk(KERN_WARNING "HVCS: enable device failed.\n"); - return rc; - } - - goto open_success; - -fast_open: - hvcsd = tty->driver_data; - - spin_lock_irqsave(&hvcsd->lock, flags); - kref_get(&hvcsd->kref); - hvcsd->open_count++; - hvcsd->todo_mask |= HVCS_SCHED_READ; - spin_unlock_irqrestore(&hvcsd->lock, flags); - -open_success: - hvcs_kick(); - - printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n", - hvcsd->vdev->unit_address ); - - return 0; - -error_release: - spin_unlock_irqrestore(&hvcsd->lock, flags); - kref_put(&hvcsd->kref, destroy_hvcs_struct); - - printk(KERN_WARNING "HVCS: partner connect failed.\n"); - return retval; -} - -static void hvcs_close(struct tty_struct *tty, struct file *filp) -{ - struct hvcs_struct *hvcsd; - unsigned long flags; - int irq = NO_IRQ; - - /* - * Is someone trying to close the file associated with this device after - * we have hung up? If so tty->driver_data wouldn't be valid. - */ - if (tty_hung_up_p(filp)) - return; - - /* - * No driver_data means that this close was probably issued after a - * failed hvcs_open by the tty layer's release_dev() api and we can just - * exit cleanly. - */ - if (!tty->driver_data) - return; - - hvcsd = tty->driver_data; - - spin_lock_irqsave(&hvcsd->lock, flags); - if (--hvcsd->open_count == 0) { - - vio_disable_interrupts(hvcsd->vdev); - - /* - * NULL this early so that the kernel_thread doesn't try to - * execute any operations on the TTY even though it is obligated - * to deliver any pending I/O to the hypervisor. - */ - hvcsd->tty = NULL; - - irq = hvcsd->vdev->irq; - spin_unlock_irqrestore(&hvcsd->lock, flags); - - tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); - - /* - * This line is important because it tells hvcs_open that this - * device needs to be re-configured the next time hvcs_open is - * called. - */ - tty->driver_data = NULL; - - free_irq(irq, hvcsd); - kref_put(&hvcsd->kref, destroy_hvcs_struct); - return; - } else if (hvcsd->open_count < 0) { - printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" - " is missmanaged.\n", - hvcsd->vdev->unit_address, hvcsd->open_count); - } - - spin_unlock_irqrestore(&hvcsd->lock, flags); - kref_put(&hvcsd->kref, destroy_hvcs_struct); -} - -static void hvcs_hangup(struct tty_struct * tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned long flags; - int temp_open_count; - int irq = NO_IRQ; - - spin_lock_irqsave(&hvcsd->lock, flags); - /* Preserve this so that we know how many kref refs to put */ - temp_open_count = hvcsd->open_count; - - /* - * Don't kref put inside the spinlock because the destruction - * callback may use the spinlock and it may get called before the - * spinlock has been released. - */ - vio_disable_interrupts(hvcsd->vdev); - - hvcsd->todo_mask = 0; - - /* I don't think the tty needs the hvcs_struct pointer after a hangup */ - hvcsd->tty->driver_data = NULL; - hvcsd->tty = NULL; - - hvcsd->open_count = 0; - - /* This will drop any buffered data on the floor which is OK in a hangup - * scenario. */ - memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); - hvcsd->chars_in_buffer = 0; - - irq = hvcsd->vdev->irq; - - spin_unlock_irqrestore(&hvcsd->lock, flags); - - free_irq(irq, hvcsd); - - /* - * We need to kref_put() for every open_count we have since the - * tty_hangup() function doesn't invoke a close per open connection on a - * non-console device. - */ - while(temp_open_count) { - --temp_open_count; - /* - * The final put will trigger destruction of the hvcs_struct. - * NOTE: If this hangup was signaled from user space then the - * final put will never happen. - */ - kref_put(&hvcsd->kref, destroy_hvcs_struct); - } -} - -/* - * NOTE: This is almost always from_user since user level apps interact with the - * /dev nodes. I'm trusting that if hvcs_write gets called and interrupted by - * hvcs_remove (which removes the target device and executes tty_hangup()) that - * tty_hangup will allow hvcs_write time to complete execution before it - * terminates our device. - */ -static int hvcs_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - unsigned int unit_address; - const unsigned char *charbuf; - unsigned long flags; - int total_sent = 0; - int tosend = 0; - int result = 0; - - /* - * If they don't check the return code off of their open they may - * attempt this even if there is no connected device. - */ - if (!hvcsd) - return -ENODEV; - - /* Reasonable size to prevent user level flooding */ - if (count > HVCS_MAX_FROM_USER) { - printk(KERN_WARNING "HVCS write: count being truncated to" - " HVCS_MAX_FROM_USER.\n"); - count = HVCS_MAX_FROM_USER; - } - - charbuf = buf; - - spin_lock_irqsave(&hvcsd->lock, flags); - - /* - * Somehow an open succedded but the device was removed or the - * connection terminated between the vty-server and partner vty during - * the middle of a write operation? This is a crummy place to do this - * but we want to keep it all in the spinlock. - */ - if (hvcsd->open_count <= 0) { - spin_unlock_irqrestore(&hvcsd->lock, flags); - return -ENODEV; - } - - unit_address = hvcsd->vdev->unit_address; - - while (count > 0) { - tosend = min(count, (HVCS_BUFF_LEN - hvcsd->chars_in_buffer)); - /* - * No more space, this probably means that the last call to - * hvcs_write() didn't succeed and the buffer was filled up. - */ - if (!tosend) - break; - - memcpy(&hvcsd->buffer[hvcsd->chars_in_buffer], - &charbuf[total_sent], - tosend); - - hvcsd->chars_in_buffer += tosend; - - result = 0; - - /* - * If this is true then we don't want to try writing to the - * hypervisor because that is the kernel_threads job now. We'll - * just add to the buffer. - */ - if (!(hvcsd->todo_mask & HVCS_TRY_WRITE)) - /* won't send partial writes */ - result = hvc_put_chars(unit_address, - &hvcsd->buffer[0], - hvcsd->chars_in_buffer); - - /* - * Since we know we have enough room in hvcsd->buffer for - * tosend we record that it was sent regardless of whether the - * hypervisor actually took it because we have it buffered. - */ - total_sent+=tosend; - count-=tosend; - if (result == 0) { - hvcsd->todo_mask |= HVCS_TRY_WRITE; - hvcs_kick(); - break; - } - - hvcsd->chars_in_buffer = 0; - /* - * Test after the chars_in_buffer reset otherwise this could - * deadlock our writes if hvc_put_chars fails. - */ - if (result < 0) - break; - } - - spin_unlock_irqrestore(&hvcsd->lock, flags); - - if (result == -1) - return -EIO; - else - return total_sent; -} - -/* - * This is really asking how much can we guarentee that we can send or that we - * absolutely WILL BUFFER if we can't send it. This driver MUST honor the - * return value, hence the reason for hvcs_struct buffering. - */ -static int hvcs_write_room(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - - if (!hvcsd || hvcsd->open_count <= 0) - return 0; - - return HVCS_BUFF_LEN - hvcsd->chars_in_buffer; -} - -static int hvcs_chars_in_buffer(struct tty_struct *tty) -{ - struct hvcs_struct *hvcsd = tty->driver_data; - - return hvcsd->chars_in_buffer; -} - -static const struct tty_operations hvcs_ops = { - .open = hvcs_open, - .close = hvcs_close, - .hangup = hvcs_hangup, - .write = hvcs_write, - .write_room = hvcs_write_room, - .chars_in_buffer = hvcs_chars_in_buffer, - .unthrottle = hvcs_unthrottle, - .throttle = hvcs_throttle, -}; - -static int hvcs_alloc_index_list(int n) -{ - int i; - - hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL); - if (!hvcs_index_list) - return -ENOMEM; - hvcs_index_count = n; - for (i = 0; i < hvcs_index_count; i++) - hvcs_index_list[i] = -1; - return 0; -} - -static void hvcs_free_index_list(void) -{ - /* Paranoia check to be thorough. */ - kfree(hvcs_index_list); - hvcs_index_list = NULL; - hvcs_index_count = 0; -} - -static int __init hvcs_module_init(void) -{ - int rc; - int num_ttys_to_alloc; - - printk(KERN_INFO "Initializing %s\n", hvcs_driver_string); - - /* Has the user specified an overload with an insmod param? */ - if (hvcs_parm_num_devs <= 0 || - (hvcs_parm_num_devs > HVCS_MAX_SERVER_ADAPTERS)) { - num_ttys_to_alloc = HVCS_DEFAULT_SERVER_ADAPTERS; - } else - num_ttys_to_alloc = hvcs_parm_num_devs; - - hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc); - if (!hvcs_tty_driver) - return -ENOMEM; - - if (hvcs_alloc_index_list(num_ttys_to_alloc)) { - rc = -ENOMEM; - goto index_fail; - } - - hvcs_tty_driver->owner = THIS_MODULE; - - hvcs_tty_driver->driver_name = hvcs_driver_name; - hvcs_tty_driver->name = hvcs_device_node; - - /* - * We'll let the system assign us a major number, indicated by leaving - * it blank. - */ - - hvcs_tty_driver->minor_start = HVCS_MINOR_START; - hvcs_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; - - /* - * We role our own so that we DONT ECHO. We can't echo because the - * device we are connecting to already echoes by default and this would - * throw us into a horrible recursive echo-echo-echo loop. - */ - hvcs_tty_driver->init_termios = hvcs_tty_termios; - hvcs_tty_driver->flags = TTY_DRIVER_REAL_RAW; - - tty_set_operations(hvcs_tty_driver, &hvcs_ops); - - /* - * The following call will result in sysfs entries that denote the - * dynamically assigned major and minor numbers for our devices. - */ - if (tty_register_driver(hvcs_tty_driver)) { - printk(KERN_ERR "HVCS: registration as a tty driver failed.\n"); - rc = -EIO; - goto register_fail; - } - - hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!hvcs_pi_buff) { - rc = -ENOMEM; - goto buff_alloc_fail; - } - - hvcs_task = kthread_run(khvcsd, NULL, "khvcsd"); - if (IS_ERR(hvcs_task)) { - printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n"); - rc = -EIO; - goto kthread_fail; - } - - rc = vio_register_driver(&hvcs_vio_driver); - if (rc) { - printk(KERN_ERR "HVCS: can't register vio driver\n"); - goto vio_fail; - } - - /* - * This needs to be done AFTER the vio_register_driver() call or else - * the kobjects won't be initialized properly. - */ - rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); - if (rc) { - printk(KERN_ERR "HVCS: sysfs attr create failed\n"); - goto attr_fail; - } - - printk(KERN_INFO "HVCS: driver module inserted.\n"); - - return 0; - -attr_fail: - vio_unregister_driver(&hvcs_vio_driver); -vio_fail: - kthread_stop(hvcs_task); -kthread_fail: - kfree(hvcs_pi_buff); -buff_alloc_fail: - tty_unregister_driver(hvcs_tty_driver); -register_fail: - hvcs_free_index_list(); -index_fail: - put_tty_driver(hvcs_tty_driver); - hvcs_tty_driver = NULL; - return rc; -} - -static void __exit hvcs_module_exit(void) -{ - /* - * This driver receives hvcs_remove callbacks for each device upon - * module removal. - */ - - /* - * This synchronous operation will wake the khvcsd kthread if it is - * asleep and will return when khvcsd has terminated. - */ - kthread_stop(hvcs_task); - - spin_lock(&hvcs_pi_lock); - kfree(hvcs_pi_buff); - hvcs_pi_buff = NULL; - spin_unlock(&hvcs_pi_lock); - - driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan); - - vio_unregister_driver(&hvcs_vio_driver); - - tty_unregister_driver(hvcs_tty_driver); - - hvcs_free_index_list(); - - put_tty_driver(hvcs_tty_driver); - - printk(KERN_INFO "HVCS: driver module removed.\n"); -} - -module_init(hvcs_module_init); -module_exit(hvcs_module_exit); diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c deleted file mode 100644 index 67a75a502c01..000000000000 --- a/drivers/char/hvsi.c +++ /dev/null @@ -1,1314 +0,0 @@ -/* - * Copyright (C) 2004 Hollis Blanchard , IBM - * - * 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 - */ - -/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS - * and the service processor on IBM pSeries servers. On these servers, there - * are no serial ports under the OS's control, and sometimes there is no other - * console available either. However, the service processor has two standard - * serial ports, so this over-complicated protocol allows the OS to control - * those ports by proxy. - * - * Besides data, the procotol supports the reading/writing of the serial - * port's DTR line, and the reading of the CD line. This is to allow the OS to - * control a modem attached to the service processor's serial port. Note that - * the OS cannot change the speed of the port through this protocol. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define HVSI_MAJOR 229 -#define HVSI_MINOR 128 -#define MAX_NR_HVSI_CONSOLES 4 - -#define HVSI_TIMEOUT (5*HZ) -#define HVSI_VERSION 1 -#define HVSI_MAX_PACKET 256 -#define HVSI_MAX_READ 16 -#define HVSI_MAX_OUTGOING_DATA 12 -#define N_OUTBUF 12 - -/* - * we pass data via two 8-byte registers, so we would like our char arrays - * properly aligned for those loads. - */ -#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) - -struct hvsi_struct { - struct delayed_work writer; - struct work_struct handshaker; - wait_queue_head_t emptyq; /* woken when outbuf is emptied */ - wait_queue_head_t stateq; /* woken when HVSI state changes */ - spinlock_t lock; - int index; - struct tty_struct *tty; - int count; - uint8_t throttle_buf[128]; - uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ - /* inbuf is for packet reassembly. leave a little room for leftovers. */ - uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; - uint8_t *inbuf_end; - int n_throttle; - int n_outbuf; - uint32_t vtermno; - uint32_t virq; - atomic_t seqno; /* HVSI packet sequence number */ - uint16_t mctrl; - uint8_t state; /* HVSI protocol state */ - uint8_t flags; -#ifdef CONFIG_MAGIC_SYSRQ - uint8_t sysrq; -#endif /* CONFIG_MAGIC_SYSRQ */ -}; -static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; - -static struct tty_driver *hvsi_driver; -static int hvsi_count; -static int (*hvsi_wait)(struct hvsi_struct *hp, int state); - -enum HVSI_PROTOCOL_STATE { - HVSI_CLOSED, - HVSI_WAIT_FOR_VER_RESPONSE, - HVSI_WAIT_FOR_VER_QUERY, - HVSI_OPEN, - HVSI_WAIT_FOR_MCTRL_RESPONSE, - HVSI_FSP_DIED, -}; -#define HVSI_CONSOLE 0x1 - -#define VS_DATA_PACKET_HEADER 0xff -#define VS_CONTROL_PACKET_HEADER 0xfe -#define VS_QUERY_PACKET_HEADER 0xfd -#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc - -/* control verbs */ -#define VSV_SET_MODEM_CTL 1 /* to service processor only */ -#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ -#define VSV_CLOSE_PROTOCOL 3 - -/* query verbs */ -#define VSV_SEND_VERSION_NUMBER 1 -#define VSV_SEND_MODEM_CTL_STATUS 2 - -/* yes, these masks are not consecutive. */ -#define HVSI_TSDTR 0x01 -#define HVSI_TSCD 0x20 - -struct hvsi_header { - uint8_t type; - uint8_t len; - uint16_t seqno; -} __attribute__((packed)); - -struct hvsi_data { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint8_t data[HVSI_MAX_OUTGOING_DATA]; -} __attribute__((packed)); - -struct hvsi_control { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; - /* optional depending on verb: */ - uint32_t word; - uint32_t mask; -} __attribute__((packed)); - -struct hvsi_query { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; -} __attribute__((packed)); - -struct hvsi_query_response { - uint8_t type; - uint8_t len; - uint16_t seqno; - uint16_t verb; - uint16_t query_seqno; - union { - uint8_t version; - uint32_t mctrl_word; - } u; -} __attribute__((packed)); - - - -static inline int is_console(struct hvsi_struct *hp) -{ - return hp->flags & HVSI_CONSOLE; -} - -static inline int is_open(struct hvsi_struct *hp) -{ - /* if we're waiting for an mctrl then we're already open */ - return (hp->state == HVSI_OPEN) - || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); -} - -static inline void print_state(struct hvsi_struct *hp) -{ -#ifdef DEBUG - static const char *state_names[] = { - "HVSI_CLOSED", - "HVSI_WAIT_FOR_VER_RESPONSE", - "HVSI_WAIT_FOR_VER_QUERY", - "HVSI_OPEN", - "HVSI_WAIT_FOR_MCTRL_RESPONSE", - "HVSI_FSP_DIED", - }; - const char *name = (hp->state < ARRAY_SIZE(state_names)) - ? state_names[hp->state] : "UNKNOWN"; - - pr_debug("hvsi%i: state = %s\n", hp->index, name); -#endif /* DEBUG */ -} - -static inline void __set_state(struct hvsi_struct *hp, int state) -{ - hp->state = state; - print_state(hp); - wake_up_all(&hp->stateq); -} - -static inline void set_state(struct hvsi_struct *hp, int state) -{ - unsigned long flags; - - spin_lock_irqsave(&hp->lock, flags); - __set_state(hp, state); - spin_unlock_irqrestore(&hp->lock, flags); -} - -static inline int len_packet(const uint8_t *packet) -{ - return (int)((struct hvsi_header *)packet)->len; -} - -static inline int is_header(const uint8_t *packet) -{ - struct hvsi_header *header = (struct hvsi_header *)packet; - return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; -} - -static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) -{ - if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) - return 0; /* don't even have the packet header */ - - if (hp->inbuf_end < (packet + len_packet(packet))) - return 0; /* don't have the rest of the packet */ - - return 1; -} - -/* shift remaining bytes in packetbuf down */ -static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) -{ - int remaining = (int)(hp->inbuf_end - read_to); - - pr_debug("%s: %i chars remain\n", __func__, remaining); - - if (read_to != hp->inbuf) - memmove(hp->inbuf, read_to, remaining); - - hp->inbuf_end = hp->inbuf + remaining; -} - -#ifdef DEBUG -#define dbg_dump_packet(packet) dump_packet(packet) -#define dbg_dump_hex(data, len) dump_hex(data, len) -#else -#define dbg_dump_packet(packet) do { } while (0) -#define dbg_dump_hex(data, len) do { } while (0) -#endif - -static void dump_hex(const uint8_t *data, int len) -{ - int i; - - printk(" "); - for (i=0; i < len; i++) - printk("%.2x", data[i]); - - printk("\n "); - for (i=0; i < len; i++) { - if (isprint(data[i])) - printk("%c", data[i]); - else - printk("."); - } - printk("\n"); -} - -static void dump_packet(uint8_t *packet) -{ - struct hvsi_header *header = (struct hvsi_header *)packet; - - printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, - header->seqno); - - dump_hex(packet, header->len); -} - -static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) -{ - unsigned long got; - - got = hvc_get_chars(hp->vtermno, buf, count); - - return got; -} - -static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, - struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) -{ - struct hvsi_control *header = (struct hvsi_control *)packet; - - switch (header->verb) { - case VSV_MODEM_CTL_UPDATE: - if ((header->word & HVSI_TSCD) == 0) { - /* CD went away; no more connection */ - pr_debug("hvsi%i: CD dropped\n", hp->index); - hp->mctrl &= TIOCM_CD; - /* If userland hasn't done an open(2) yet, hp->tty is NULL. */ - if (hp->tty && !(hp->tty->flags & CLOCAL)) - *to_hangup = hp->tty; - } - break; - case VSV_CLOSE_PROTOCOL: - pr_debug("hvsi%i: service processor came back\n", hp->index); - if (hp->state != HVSI_CLOSED) { - *to_handshake = hp; - } - break; - default: - printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", - hp->index); - dump_packet(packet); - break; - } -} - -static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) -{ - struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; - - switch (hp->state) { - case HVSI_WAIT_FOR_VER_RESPONSE: - __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); - break; - case HVSI_WAIT_FOR_MCTRL_RESPONSE: - hp->mctrl = 0; - if (resp->u.mctrl_word & HVSI_TSDTR) - hp->mctrl |= TIOCM_DTR; - if (resp->u.mctrl_word & HVSI_TSCD) - hp->mctrl |= TIOCM_CD; - __set_state(hp, HVSI_OPEN); - break; - default: - printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); - dump_packet(packet); - break; - } -} - -/* respond to service processor's version query */ -static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) -{ - struct hvsi_query_response packet __ALIGNED__; - int wrote; - - packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; - packet.len = sizeof(struct hvsi_query_response); - packet.seqno = atomic_inc_return(&hp->seqno); - packet.verb = VSV_SEND_VERSION_NUMBER; - packet.u.version = HVSI_VERSION; - packet.query_seqno = query_seqno+1; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { - printk(KERN_ERR "hvsi%i: couldn't send query response!\n", - hp->index); - return -EIO; - } - - return 0; -} - -static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) -{ - struct hvsi_query *query = (struct hvsi_query *)packet; - - switch (hp->state) { - case HVSI_WAIT_FOR_VER_QUERY: - hvsi_version_respond(hp, query->seqno); - __set_state(hp, HVSI_OPEN); - break; - default: - printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); - dump_packet(packet); - break; - } -} - -static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) -{ - int i; - - for (i=0; i < len; i++) { - char c = buf[i]; -#ifdef CONFIG_MAGIC_SYSRQ - if (c == '\0') { - hp->sysrq = 1; - continue; - } else if (hp->sysrq) { - handle_sysrq(c); - hp->sysrq = 0; - continue; - } -#endif /* CONFIG_MAGIC_SYSRQ */ - tty_insert_flip_char(hp->tty, c, 0); - } -} - -/* - * We could get 252 bytes of data at once here. But the tty layer only - * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow - * it. Accordingly we won't send more than 128 bytes at a time to the flip - * buffer, which will give the tty buffer a chance to throttle us. Should the - * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be - * revisited. - */ -#define TTY_THRESHOLD_THROTTLE 128 -static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, - const uint8_t *packet) -{ - const struct hvsi_header *header = (const struct hvsi_header *)packet; - const uint8_t *data = packet + sizeof(struct hvsi_header); - int datalen = header->len - sizeof(struct hvsi_header); - int overflow = datalen - TTY_THRESHOLD_THROTTLE; - - pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); - - if (datalen == 0) - return NULL; - - if (overflow > 0) { - pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); - datalen = TTY_THRESHOLD_THROTTLE; - } - - hvsi_insert_chars(hp, data, datalen); - - if (overflow > 0) { - /* - * we still have more data to deliver, so we need to save off the - * overflow and send it later - */ - pr_debug("%s: deferring overflow\n", __func__); - memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); - hp->n_throttle = overflow; - } - - return hp->tty; -} - -/* - * Returns true/false indicating data successfully read from hypervisor. - * Used both to get packets for tty connections and to advance the state - * machine during console handshaking (in which case tty = NULL and we ignore - * incoming data). - */ -static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, - struct tty_struct **hangup, struct hvsi_struct **handshake) -{ - uint8_t *packet = hp->inbuf; - int chunklen; - - *flip = NULL; - *hangup = NULL; - *handshake = NULL; - - chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); - if (chunklen == 0) { - pr_debug("%s: 0-length read\n", __func__); - return 0; - } - - pr_debug("%s: got %i bytes\n", __func__, chunklen); - dbg_dump_hex(hp->inbuf_end, chunklen); - - hp->inbuf_end += chunklen; - - /* handle all completed packets */ - while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { - struct hvsi_header *header = (struct hvsi_header *)packet; - - if (!is_header(packet)) { - printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); - /* skip bytes until we find a header or run out of data */ - while ((packet < hp->inbuf_end) && (!is_header(packet))) - packet++; - continue; - } - - pr_debug("%s: handling %i-byte packet\n", __func__, - len_packet(packet)); - dbg_dump_packet(packet); - - switch (header->type) { - case VS_DATA_PACKET_HEADER: - if (!is_open(hp)) - break; - if (hp->tty == NULL) - break; /* no tty buffer to put data in */ - *flip = hvsi_recv_data(hp, packet); - break; - case VS_CONTROL_PACKET_HEADER: - hvsi_recv_control(hp, packet, hangup, handshake); - break; - case VS_QUERY_RESPONSE_PACKET_HEADER: - hvsi_recv_response(hp, packet); - break; - case VS_QUERY_PACKET_HEADER: - hvsi_recv_query(hp, packet); - break; - default: - printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", - hp->index, header->type); - dump_packet(packet); - break; - } - - packet += len_packet(packet); - - if (*hangup || *handshake) { - pr_debug("%s: hangup or handshake\n", __func__); - /* - * we need to send the hangup now before receiving any more data. - * If we get "data, hangup, data", we can't deliver the second - * data before the hangup. - */ - break; - } - } - - compact_inbuf(hp, packet); - - return 1; -} - -static void hvsi_send_overflow(struct hvsi_struct *hp) -{ - pr_debug("%s: delivering %i bytes overflow\n", __func__, - hp->n_throttle); - - hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); - hp->n_throttle = 0; -} - -/* - * must get all pending data because we only get an irq on empty->non-empty - * transition - */ -static irqreturn_t hvsi_interrupt(int irq, void *arg) -{ - struct hvsi_struct *hp = (struct hvsi_struct *)arg; - struct tty_struct *flip; - struct tty_struct *hangup; - struct hvsi_struct *handshake; - unsigned long flags; - int again = 1; - - pr_debug("%s\n", __func__); - - while (again) { - spin_lock_irqsave(&hp->lock, flags); - again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); - spin_unlock_irqrestore(&hp->lock, flags); - - /* - * we have to call tty_flip_buffer_push() and tty_hangup() outside our - * spinlock. But we also have to keep going until we've read all the - * available data. - */ - - if (flip) { - /* there was data put in the tty flip buffer */ - tty_flip_buffer_push(flip); - flip = NULL; - } - - if (hangup) { - tty_hangup(hangup); - } - - if (handshake) { - pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); - schedule_work(&handshake->handshaker); - } - } - - spin_lock_irqsave(&hp->lock, flags); - if (hp->tty && hp->n_throttle - && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { - /* we weren't hung up and we weren't throttled, so we can deliver the - * rest now */ - flip = hp->tty; - hvsi_send_overflow(hp); - } - spin_unlock_irqrestore(&hp->lock, flags); - - if (flip) { - tty_flip_buffer_push(flip); - } - - return IRQ_HANDLED; -} - -/* for boot console, before the irq handler is running */ -static int __init poll_for_state(struct hvsi_struct *hp, int state) -{ - unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; - - for (;;) { - hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ - - if (hp->state == state) - return 0; - - mdelay(5); - if (time_after(jiffies, end_jiffies)) - return -EIO; - } -} - -/* wait for irq handler to change our state */ -static int wait_for_state(struct hvsi_struct *hp, int state) -{ - int ret = 0; - - if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) - ret = -EIO; - - return ret; -} - -static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) -{ - struct hvsi_query packet __ALIGNED__; - int wrote; - - packet.type = VS_QUERY_PACKET_HEADER; - packet.len = sizeof(struct hvsi_query); - packet.seqno = atomic_inc_return(&hp->seqno); - packet.verb = verb; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { - printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, - wrote); - return -EIO; - } - - return 0; -} - -static int hvsi_get_mctrl(struct hvsi_struct *hp) -{ - int ret; - - set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); - hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); - - ret = hvsi_wait(hp, HVSI_OPEN); - if (ret < 0) { - printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); - set_state(hp, HVSI_OPEN); - return ret; - } - - pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); - - return 0; -} - -/* note that we can only set DTR */ -static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) -{ - struct hvsi_control packet __ALIGNED__; - int wrote; - - packet.type = VS_CONTROL_PACKET_HEADER, - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = sizeof(struct hvsi_control); - packet.verb = VSV_SET_MODEM_CTL; - packet.mask = HVSI_TSDTR; - - if (mctrl & TIOCM_DTR) - packet.word = HVSI_TSDTR; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (wrote != packet.len) { - printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); - return -EIO; - } - - return 0; -} - -static void hvsi_drain_input(struct hvsi_struct *hp) -{ - uint8_t buf[HVSI_MAX_READ] __ALIGNED__; - unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; - - while (time_before(end_jiffies, jiffies)) - if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) - break; -} - -static int hvsi_handshake(struct hvsi_struct *hp) -{ - int ret; - - /* - * We could have a CLOSE or other data waiting for us before we even try - * to open; try to throw it all away so we don't get confused. (CLOSE - * is the first message sent up the pipe when the FSP comes online. We - * need to distinguish between "it came up a while ago and we're the first - * user" and "it was just reset before it saw our handshake packet".) - */ - hvsi_drain_input(hp); - - set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); - ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); - if (ret < 0) { - printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); - return ret; - } - - ret = hvsi_wait(hp, HVSI_OPEN); - if (ret < 0) - return ret; - - return 0; -} - -static void hvsi_handshaker(struct work_struct *work) -{ - struct hvsi_struct *hp = - container_of(work, struct hvsi_struct, handshaker); - - if (hvsi_handshake(hp) >= 0) - return; - - printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); - if (is_console(hp)) { - /* - * ttys will re-attempt the handshake via hvsi_open, but - * the console will not. - */ - printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); - } -} - -static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) -{ - struct hvsi_data packet __ALIGNED__; - int ret; - - BUG_ON(count > HVSI_MAX_OUTGOING_DATA); - - packet.type = VS_DATA_PACKET_HEADER; - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = count + sizeof(struct hvsi_header); - memcpy(&packet.data, buf, count); - - ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); - if (ret == packet.len) { - /* return the number of chars written, not the packet length */ - return count; - } - return ret; /* return any errors */ -} - -static void hvsi_close_protocol(struct hvsi_struct *hp) -{ - struct hvsi_control packet __ALIGNED__; - - packet.type = VS_CONTROL_PACKET_HEADER; - packet.seqno = atomic_inc_return(&hp->seqno); - packet.len = 6; - packet.verb = VSV_CLOSE_PROTOCOL; - - pr_debug("%s: sending %i bytes\n", __func__, packet.len); - dbg_dump_hex((uint8_t*)&packet, packet.len); - - hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); -} - -static int hvsi_open(struct tty_struct *tty, struct file *filp) -{ - struct hvsi_struct *hp; - unsigned long flags; - int line = tty->index; - int ret; - - pr_debug("%s\n", __func__); - - if (line < 0 || line >= hvsi_count) - return -ENODEV; - hp = &hvsi_ports[line]; - - tty->driver_data = hp; - - mb(); - if (hp->state == HVSI_FSP_DIED) - return -EIO; - - spin_lock_irqsave(&hp->lock, flags); - hp->tty = tty; - hp->count++; - atomic_set(&hp->seqno, 0); - h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); - spin_unlock_irqrestore(&hp->lock, flags); - - if (is_console(hp)) - return 0; /* this has already been handshaked as the console */ - - ret = hvsi_handshake(hp); - if (ret < 0) { - printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); - return ret; - } - - ret = hvsi_get_mctrl(hp); - if (ret < 0) { - printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); - return ret; - } - - ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); - if (ret < 0) { - printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); - return ret; - } - - return 0; -} - -/* wait for hvsi_write_worker to empty hp->outbuf */ -static void hvsi_flush_output(struct hvsi_struct *hp) -{ - wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); - - /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ - cancel_delayed_work_sync(&hp->writer); - flush_work_sync(&hp->handshaker); - - /* - * it's also possible that our timeout expired and hvsi_write_worker - * didn't manage to push outbuf. poof. - */ - hp->n_outbuf = 0; -} - -static void hvsi_close(struct tty_struct *tty, struct file *filp) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - - pr_debug("%s\n", __func__); - - if (tty_hung_up_p(filp)) - return; - - spin_lock_irqsave(&hp->lock, flags); - - if (--hp->count == 0) { - hp->tty = NULL; - hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ - - /* only close down connection if it is not the console */ - if (!is_console(hp)) { - h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ - __set_state(hp, HVSI_CLOSED); - /* - * any data delivered to the tty layer after this will be - * discarded (except for XON/XOFF) - */ - tty->closing = 1; - - spin_unlock_irqrestore(&hp->lock, flags); - - /* let any existing irq handlers finish. no more will start. */ - synchronize_irq(hp->virq); - - /* hvsi_write_worker will re-schedule until outbuf is empty. */ - hvsi_flush_output(hp); - - /* tell FSP to stop sending data */ - hvsi_close_protocol(hp); - - /* - * drain anything FSP is still in the middle of sending, and let - * hvsi_handshake drain the rest on the next open. - */ - hvsi_drain_input(hp); - - spin_lock_irqsave(&hp->lock, flags); - } - } else if (hp->count < 0) - printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", - hp - hvsi_ports, hp->count); - - spin_unlock_irqrestore(&hp->lock, flags); -} - -static void hvsi_hangup(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - - pr_debug("%s\n", __func__); - - spin_lock_irqsave(&hp->lock, flags); - - hp->count = 0; - hp->n_outbuf = 0; - hp->tty = NULL; - - spin_unlock_irqrestore(&hp->lock, flags); -} - -/* called with hp->lock held */ -static void hvsi_push(struct hvsi_struct *hp) -{ - int n; - - if (hp->n_outbuf <= 0) - return; - - n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); - if (n > 0) { - /* success */ - pr_debug("%s: wrote %i chars\n", __func__, n); - hp->n_outbuf = 0; - } else if (n == -EIO) { - __set_state(hp, HVSI_FSP_DIED); - printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); - } -} - -/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ -static void hvsi_write_worker(struct work_struct *work) -{ - struct hvsi_struct *hp = - container_of(work, struct hvsi_struct, writer.work); - unsigned long flags; -#ifdef DEBUG - static long start_j = 0; - - if (start_j == 0) - start_j = jiffies; -#endif /* DEBUG */ - - spin_lock_irqsave(&hp->lock, flags); - - pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); - - if (!is_open(hp)) { - /* - * We could have a non-open connection if the service processor died - * while we were busily scheduling ourselves. In that case, it could - * be minutes before the service processor comes back, so only try - * again once a second. - */ - schedule_delayed_work(&hp->writer, HZ); - goto out; - } - - hvsi_push(hp); - if (hp->n_outbuf > 0) - schedule_delayed_work(&hp->writer, 10); - else { -#ifdef DEBUG - pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, - jiffies - start_j); - start_j = 0; -#endif /* DEBUG */ - wake_up_all(&hp->emptyq); - tty_wakeup(hp->tty); - } - -out: - spin_unlock_irqrestore(&hp->lock, flags); -} - -static int hvsi_write_room(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - - return N_OUTBUF - hp->n_outbuf; -} - -static int hvsi_chars_in_buffer(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - - return hp->n_outbuf; -} - -static int hvsi_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct hvsi_struct *hp = tty->driver_data; - const char *source = buf; - unsigned long flags; - int total = 0; - int origcount = count; - - spin_lock_irqsave(&hp->lock, flags); - - pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); - - if (!is_open(hp)) { - /* we're either closing or not yet open; don't accept data */ - pr_debug("%s: not open\n", __func__); - goto out; - } - - /* - * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf - * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls - * will see there is no room in outbuf and return. - */ - while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { - int chunksize = min(count, hvsi_write_room(hp->tty)); - - BUG_ON(hp->n_outbuf < 0); - memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); - hp->n_outbuf += chunksize; - - total += chunksize; - source += chunksize; - count -= chunksize; - hvsi_push(hp); - } - - if (hp->n_outbuf > 0) { - /* - * we weren't able to write it all to the hypervisor. - * schedule another push attempt. - */ - schedule_delayed_work(&hp->writer, 10); - } - -out: - spin_unlock_irqrestore(&hp->lock, flags); - - if (total != origcount) - pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, - total); - - return total; -} - -/* - * I have never seen throttle or unthrottle called, so this little throttle - * buffering scheme may or may not work. - */ -static void hvsi_throttle(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - - pr_debug("%s\n", __func__); - - h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); -} - -static void hvsi_unthrottle(struct tty_struct *tty) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - int shouldflip = 0; - - pr_debug("%s\n", __func__); - - spin_lock_irqsave(&hp->lock, flags); - if (hp->n_throttle) { - hvsi_send_overflow(hp); - shouldflip = 1; - } - spin_unlock_irqrestore(&hp->lock, flags); - - if (shouldflip) - tty_flip_buffer_push(hp->tty); - - h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); -} - -static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct hvsi_struct *hp = tty->driver_data; - - hvsi_get_mctrl(hp); - return hp->mctrl; -} - -static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct hvsi_struct *hp = tty->driver_data; - unsigned long flags; - uint16_t new_mctrl; - - /* we can only alter DTR */ - clear &= TIOCM_DTR; - set &= TIOCM_DTR; - - spin_lock_irqsave(&hp->lock, flags); - - new_mctrl = (hp->mctrl & ~clear) | set; - - if (hp->mctrl != new_mctrl) { - hvsi_set_mctrl(hp, new_mctrl); - hp->mctrl = new_mctrl; - } - spin_unlock_irqrestore(&hp->lock, flags); - - return 0; -} - - -static const struct tty_operations hvsi_ops = { - .open = hvsi_open, - .close = hvsi_close, - .write = hvsi_write, - .hangup = hvsi_hangup, - .write_room = hvsi_write_room, - .chars_in_buffer = hvsi_chars_in_buffer, - .throttle = hvsi_throttle, - .unthrottle = hvsi_unthrottle, - .tiocmget = hvsi_tiocmget, - .tiocmset = hvsi_tiocmset, -}; - -static int __init hvsi_init(void) -{ - int i; - - hvsi_driver = alloc_tty_driver(hvsi_count); - if (!hvsi_driver) - return -ENOMEM; - - hvsi_driver->owner = THIS_MODULE; - hvsi_driver->driver_name = "hvsi"; - hvsi_driver->name = "hvsi"; - hvsi_driver->major = HVSI_MAJOR; - hvsi_driver->minor_start = HVSI_MINOR; - hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; - hvsi_driver->init_termios = tty_std_termios; - hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; - hvsi_driver->init_termios.c_ispeed = 9600; - hvsi_driver->init_termios.c_ospeed = 9600; - hvsi_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(hvsi_driver, &hvsi_ops); - - for (i=0; i < hvsi_count; i++) { - struct hvsi_struct *hp = &hvsi_ports[i]; - int ret = 1; - - ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); - if (ret) - printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", - hp->virq, ret); - } - hvsi_wait = wait_for_state; /* irqs active now */ - - if (tty_register_driver(hvsi_driver)) - panic("Couldn't register hvsi console driver\n"); - - printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); - - return 0; -} -device_initcall(hvsi_init); - -/***** console (not tty) code: *****/ - -static void hvsi_console_print(struct console *console, const char *buf, - unsigned int count) -{ - struct hvsi_struct *hp = &hvsi_ports[console->index]; - char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; - unsigned int i = 0, n = 0; - int ret, donecr = 0; - - mb(); - if (!is_open(hp)) - return; - - /* - * ugh, we have to translate LF -> CRLF ourselves, in place. - * copied from hvc_console.c: - */ - while (count > 0 || i > 0) { - if (count > 0 && i < sizeof(c)) { - if (buf[n] == '\n' && !donecr) { - c[i++] = '\r'; - donecr = 1; - } else { - c[i++] = buf[n++]; - donecr = 0; - --count; - } - } else { - ret = hvsi_put_chars(hp, c, i); - if (ret < 0) - i = 0; - i -= ret; - } - } -} - -static struct tty_driver *hvsi_console_device(struct console *console, - int *index) -{ - *index = console->index; - return hvsi_driver; -} - -static int __init hvsi_console_setup(struct console *console, char *options) -{ - struct hvsi_struct *hp; - int ret; - - if (console->index < 0 || console->index >= hvsi_count) - return -1; - hp = &hvsi_ports[console->index]; - - /* give the FSP a chance to change the baud rate when we re-open */ - hvsi_close_protocol(hp); - - ret = hvsi_handshake(hp); - if (ret < 0) - return ret; - - ret = hvsi_get_mctrl(hp); - if (ret < 0) - return ret; - - ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); - if (ret < 0) - return ret; - - hp->flags |= HVSI_CONSOLE; - - return 0; -} - -static struct console hvsi_console = { - .name = "hvsi", - .write = hvsi_console_print, - .device = hvsi_console_device, - .setup = hvsi_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -static int __init hvsi_console_init(void) -{ - struct device_node *vty; - - hvsi_wait = poll_for_state; /* no irqs yet; must poll */ - - /* search device tree for vty nodes */ - for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); - vty != NULL; - vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { - struct hvsi_struct *hp; - const uint32_t *vtermno, *irq; - - vtermno = of_get_property(vty, "reg", NULL); - irq = of_get_property(vty, "interrupts", NULL); - if (!vtermno || !irq) - continue; - - if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { - of_node_put(vty); - break; - } - - hp = &hvsi_ports[hvsi_count]; - INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); - INIT_WORK(&hp->handshaker, hvsi_handshaker); - init_waitqueue_head(&hp->emptyq); - init_waitqueue_head(&hp->stateq); - spin_lock_init(&hp->lock); - hp->index = hvsi_count; - hp->inbuf_end = hp->inbuf; - hp->state = HVSI_CLOSED; - hp->vtermno = *vtermno; - hp->virq = irq_create_mapping(NULL, irq[0]); - if (hp->virq == NO_IRQ) { - printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", - __func__, irq[0]); - continue; - } - - hvsi_count++; - } - - if (hvsi_count) - register_console(&hvsi_console); - return 0; -} -console_initcall(hvsi_console_init); diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c deleted file mode 100644 index 896a2ced1d27..000000000000 --- a/drivers/char/virtio_console.c +++ /dev/null @@ -1,1838 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation - * Copyright (C) 2009, 2010 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 -#include -#include -#include -#include -#include -#include "hvc_console.h" - -/* - * This is a global struct for storing common data for all the devices - * this driver handles. - * - * Mainly, it has a linked list for all the consoles in one place so - * that callbacks from hvc for get_chars(), put_chars() work properly - * across multiple devices and multiple ports per device. - */ -struct ports_driver_data { - /* Used for registering chardevs */ - struct class *class; - - /* Used for exporting per-port information to debugfs */ - struct dentry *debugfs_dir; - - /* List of all the devices we're handling */ - struct list_head portdevs; - - /* Number of devices this driver is handling */ - unsigned int index; - - /* - * This is used to keep track of the number of hvc consoles - * spawned by this driver. This number is given as the first - * argument to hvc_alloc(). To correctly map an initial - * console spawned via hvc_instantiate to the console being - * hooked up via hvc_alloc, we need to pass the same vtermno. - * - * We also just assume the first console being initialised was - * the first one that got used as the initial console. - */ - unsigned int next_vtermno; - - /* All the console devices handled by this driver */ - struct list_head consoles; -}; -static struct ports_driver_data pdrvdata; - -DEFINE_SPINLOCK(pdrvdata_lock); - -/* This struct holds information that's relevant only for console ports */ -struct console { - /* We'll place all consoles in a list in the pdrvdata struct */ - struct list_head list; - - /* The hvc device associated with this console port */ - struct hvc_struct *hvc; - - /* The size of the console */ - struct winsize ws; - - /* - * This number identifies the number that we used to register - * with hvc in hvc_instantiate() and hvc_alloc(); this is the - * number passed on by the hvc callbacks to us to - * differentiate between the other console ports handled by - * this driver - */ - u32 vtermno; -}; - -struct port_buffer { - char *buf; - - /* size of the buffer in *buf above */ - size_t size; - - /* used length of the buffer */ - size_t len; - /* offset in the buf from which to consume data */ - size_t offset; -}; - -/* - * This is a per-device struct that stores data common to all the - * ports for that device (vdev->priv). - */ -struct ports_device { - /* Next portdev in the list, head is in the pdrvdata struct */ - struct list_head list; - - /* - * Workqueue handlers where we process deferred work after - * notification - */ - struct work_struct control_work; - - struct list_head ports; - - /* To protect the list of ports */ - spinlock_t ports_lock; - - /* To protect the vq operations for the control channel */ - spinlock_t cvq_lock; - - /* The current config space is stored here */ - struct virtio_console_config config; - - /* The virtio device we're associated with */ - struct virtio_device *vdev; - - /* - * A couple of virtqueues for the control channel: one for - * guest->host transfers, one for host->guest transfers - */ - struct virtqueue *c_ivq, *c_ovq; - - /* Array of per-port IO virtqueues */ - struct virtqueue **in_vqs, **out_vqs; - - /* Used for numbering devices for sysfs and debugfs */ - unsigned int drv_index; - - /* Major number for this device. Ports will be created as minors. */ - int chr_major; -}; - -/* This struct holds the per-port data */ -struct port { - /* Next port in the list, head is in the ports_device */ - struct list_head list; - - /* Pointer to the parent virtio_console device */ - struct ports_device *portdev; - - /* The current buffer from which data has to be fed to readers */ - struct port_buffer *inbuf; - - /* - * To protect the operations on the in_vq associated with this - * port. Has to be a spinlock because it can be called from - * interrupt context (get_char()). - */ - spinlock_t inbuf_lock; - - /* Protect the operations on the out_vq. */ - spinlock_t outvq_lock; - - /* The IO vqs for this port */ - struct virtqueue *in_vq, *out_vq; - - /* File in the debugfs directory that exposes this port's information */ - struct dentry *debugfs_file; - - /* - * The entries in this struct will be valid if this port is - * hooked up to an hvc console - */ - struct console cons; - - /* Each port associates with a separate char device */ - struct cdev *cdev; - struct device *dev; - - /* Reference-counting to handle port hot-unplugs and file operations */ - struct kref kref; - - /* A waitqueue for poll() or blocking read operations */ - wait_queue_head_t waitqueue; - - /* The 'name' of the port that we expose via sysfs properties */ - char *name; - - /* We can notify apps of host connect / disconnect events via SIGIO */ - struct fasync_struct *async_queue; - - /* The 'id' to identify the port with the Host */ - u32 id; - - bool outvq_full; - - /* Is the host device open */ - bool host_connected; - - /* We should allow only one process to open a port */ - bool guest_connected; -}; - -/* This is the very early arch-specified put chars function. */ -static int (*early_put_chars)(u32, const char *, int); - -static struct port *find_port_by_vtermno(u32 vtermno) -{ - struct port *port; - struct console *cons; - unsigned long flags; - - spin_lock_irqsave(&pdrvdata_lock, flags); - list_for_each_entry(cons, &pdrvdata.consoles, list) { - if (cons->vtermno == vtermno) { - port = container_of(cons, struct port, cons); - goto out; - } - } - port = NULL; -out: - spin_unlock_irqrestore(&pdrvdata_lock, flags); - return port; -} - -static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, - dev_t dev) -{ - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&portdev->ports_lock, flags); - list_for_each_entry(port, &portdev->ports, list) - if (port->cdev->dev == dev) - goto out; - port = NULL; -out: - spin_unlock_irqrestore(&portdev->ports_lock, flags); - - return port; -} - -static struct port *find_port_by_devt(dev_t dev) -{ - struct ports_device *portdev; - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&pdrvdata_lock, flags); - list_for_each_entry(portdev, &pdrvdata.portdevs, list) { - port = find_port_by_devt_in_portdev(portdev, dev); - if (port) - goto out; - } - port = NULL; -out: - spin_unlock_irqrestore(&pdrvdata_lock, flags); - return port; -} - -static struct port *find_port_by_id(struct ports_device *portdev, u32 id) -{ - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&portdev->ports_lock, flags); - list_for_each_entry(port, &portdev->ports, list) - if (port->id == id) - goto out; - port = NULL; -out: - spin_unlock_irqrestore(&portdev->ports_lock, flags); - - return port; -} - -static struct port *find_port_by_vq(struct ports_device *portdev, - struct virtqueue *vq) -{ - struct port *port; - unsigned long flags; - - spin_lock_irqsave(&portdev->ports_lock, flags); - list_for_each_entry(port, &portdev->ports, list) - if (port->in_vq == vq || port->out_vq == vq) - goto out; - port = NULL; -out: - spin_unlock_irqrestore(&portdev->ports_lock, flags); - return port; -} - -static bool is_console_port(struct port *port) -{ - if (port->cons.hvc) - return true; - return false; -} - -static inline bool use_multiport(struct ports_device *portdev) -{ - /* - * This condition can be true when put_chars is called from - * early_init - */ - if (!portdev->vdev) - return 0; - return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); -} - -static void free_buf(struct port_buffer *buf) -{ - kfree(buf->buf); - kfree(buf); -} - -static struct port_buffer *alloc_buf(size_t buf_size) -{ - struct port_buffer *buf; - - buf = kmalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - goto fail; - buf->buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf->buf) - goto free_buf; - buf->len = 0; - buf->offset = 0; - buf->size = buf_size; - return buf; - -free_buf: - kfree(buf); -fail: - return NULL; -} - -/* Callers should take appropriate locks */ -static void *get_inbuf(struct port *port) -{ - struct port_buffer *buf; - struct virtqueue *vq; - unsigned int len; - - vq = port->in_vq; - buf = virtqueue_get_buf(vq, &len); - if (buf) { - buf->len = len; - buf->offset = 0; - } - return buf; -} - -/* - * Create a scatter-gather list representing our input buffer and put - * it in the queue. - * - * Callers should take appropriate locks. - */ -static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) -{ - struct scatterlist sg[1]; - int ret; - - sg_init_one(sg, buf->buf, buf->size); - - ret = virtqueue_add_buf(vq, sg, 0, 1, buf); - virtqueue_kick(vq); - return ret; -} - -/* Discard any unread data this port has. Callers lockers. */ -static void discard_port_data(struct port *port) -{ - struct port_buffer *buf; - struct virtqueue *vq; - unsigned int len; - int ret; - - vq = port->in_vq; - if (port->inbuf) - buf = port->inbuf; - else - buf = virtqueue_get_buf(vq, &len); - - ret = 0; - while (buf) { - if (add_inbuf(vq, buf) < 0) { - ret++; - free_buf(buf); - } - buf = virtqueue_get_buf(vq, &len); - } - port->inbuf = NULL; - if (ret) - dev_warn(port->dev, "Errors adding %d buffers back to vq\n", - ret); -} - -static bool port_has_data(struct port *port) -{ - unsigned long flags; - bool ret; - - spin_lock_irqsave(&port->inbuf_lock, flags); - if (port->inbuf) { - ret = true; - goto out; - } - port->inbuf = get_inbuf(port); - if (port->inbuf) { - ret = true; - goto out; - } - ret = false; -out: - spin_unlock_irqrestore(&port->inbuf_lock, flags); - return ret; -} - -static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, - unsigned int event, unsigned int value) -{ - struct scatterlist sg[1]; - struct virtio_console_control cpkt; - struct virtqueue *vq; - unsigned int len; - - if (!use_multiport(portdev)) - return 0; - - cpkt.id = port_id; - cpkt.event = event; - cpkt.value = value; - - vq = portdev->c_ovq; - - sg_init_one(sg, &cpkt, sizeof(cpkt)); - if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { - virtqueue_kick(vq); - while (!virtqueue_get_buf(vq, &len)) - cpu_relax(); - } - return 0; -} - -static ssize_t send_control_msg(struct port *port, unsigned int event, - unsigned int value) -{ - /* Did the port get unplugged before userspace closed it? */ - if (port->portdev) - return __send_control_msg(port->portdev, port->id, event, value); - return 0; -} - -/* Callers must take the port->outvq_lock */ -static void reclaim_consumed_buffers(struct port *port) -{ - void *buf; - unsigned int len; - - while ((buf = virtqueue_get_buf(port->out_vq, &len))) { - kfree(buf); - port->outvq_full = false; - } -} - -static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, - bool nonblock) -{ - struct scatterlist sg[1]; - struct virtqueue *out_vq; - ssize_t ret; - unsigned long flags; - unsigned int len; - - out_vq = port->out_vq; - - spin_lock_irqsave(&port->outvq_lock, flags); - - reclaim_consumed_buffers(port); - - sg_init_one(sg, in_buf, in_count); - ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); - - /* Tell Host to go! */ - virtqueue_kick(out_vq); - - if (ret < 0) { - in_count = 0; - goto done; - } - - if (ret == 0) - port->outvq_full = true; - - if (nonblock) - goto done; - - /* - * Wait till the host acknowledges it pushed out the data we - * sent. This is done for data from the hvc_console; the tty - * operations are performed with spinlocks held so we can't - * sleep here. An alternative would be to copy the data to a - * buffer and relax the spinning requirement. The downside is - * we need to kmalloc a GFP_ATOMIC buffer each time the - * console driver writes something out. - */ - while (!virtqueue_get_buf(out_vq, &len)) - cpu_relax(); -done: - spin_unlock_irqrestore(&port->outvq_lock, flags); - /* - * We're expected to return the amount of data we wrote -- all - * of it - */ - return in_count; -} - -/* - * Give out the data that's requested from the buffer that we have - * queued up. - */ -static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, - bool to_user) -{ - struct port_buffer *buf; - unsigned long flags; - - if (!out_count || !port_has_data(port)) - return 0; - - buf = port->inbuf; - out_count = min(out_count, buf->len - buf->offset); - - if (to_user) { - ssize_t ret; - - ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count); - if (ret) - return -EFAULT; - } else { - memcpy(out_buf, buf->buf + buf->offset, out_count); - } - - buf->offset += out_count; - - if (buf->offset == buf->len) { - /* - * We're done using all the data in this buffer. - * Re-queue so that the Host can send us more data. - */ - spin_lock_irqsave(&port->inbuf_lock, flags); - port->inbuf = NULL; - - if (add_inbuf(port->in_vq, buf) < 0) - dev_warn(port->dev, "failed add_buf\n"); - - spin_unlock_irqrestore(&port->inbuf_lock, flags); - } - /* Return the number of bytes actually copied */ - return out_count; -} - -/* The condition that must be true for polling to end */ -static bool will_read_block(struct port *port) -{ - if (!port->guest_connected) { - /* Port got hot-unplugged. Let's exit. */ - return false; - } - return !port_has_data(port) && port->host_connected; -} - -static bool will_write_block(struct port *port) -{ - bool ret; - - if (!port->guest_connected) { - /* Port got hot-unplugged. Let's exit. */ - return false; - } - if (!port->host_connected) - return true; - - spin_lock_irq(&port->outvq_lock); - /* - * Check if the Host has consumed any buffers since we last - * sent data (this is only applicable for nonblocking ports). - */ - reclaim_consumed_buffers(port); - ret = port->outvq_full; - spin_unlock_irq(&port->outvq_lock); - - return ret; -} - -static ssize_t port_fops_read(struct file *filp, char __user *ubuf, - size_t count, loff_t *offp) -{ - struct port *port; - ssize_t ret; - - port = filp->private_data; - - if (!port_has_data(port)) { - /* - * If nothing's connected on the host just return 0 in - * case of list_empty; this tells the userspace app - * that there's no connection - */ - if (!port->host_connected) - return 0; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - - ret = wait_event_interruptible(port->waitqueue, - !will_read_block(port)); - if (ret < 0) - return ret; - } - /* Port got hot-unplugged. */ - if (!port->guest_connected) - return -ENODEV; - /* - * We could've received a disconnection message while we were - * waiting for more data. - * - * This check is not clubbed in the if() statement above as we - * might receive some data as well as the host could get - * disconnected after we got woken up from our wait. So we - * really want to give off whatever data we have and only then - * check for host_connected. - */ - if (!port_has_data(port) && !port->host_connected) - return 0; - - return fill_readbuf(port, ubuf, count, true); -} - -static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, - size_t count, loff_t *offp) -{ - struct port *port; - char *buf; - ssize_t ret; - bool nonblock; - - /* Userspace could be out to fool us */ - if (!count) - return 0; - - port = filp->private_data; - - nonblock = filp->f_flags & O_NONBLOCK; - - if (will_write_block(port)) { - if (nonblock) - return -EAGAIN; - - ret = wait_event_interruptible(port->waitqueue, - !will_write_block(port)); - if (ret < 0) - return ret; - } - /* Port got hot-unplugged. */ - if (!port->guest_connected) - return -ENODEV; - - count = min((size_t)(32 * 1024), count); - - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = copy_from_user(buf, ubuf, count); - if (ret) { - ret = -EFAULT; - goto free_buf; - } - - /* - * We now ask send_buf() to not spin for generic ports -- we - * can re-use the same code path that non-blocking file - * descriptors take for blocking file descriptors since the - * wait is already done and we're certain the write will go - * through to the host. - */ - nonblock = true; - ret = send_buf(port, buf, count, nonblock); - - if (nonblock && ret > 0) - goto out; - -free_buf: - kfree(buf); -out: - return ret; -} - -static unsigned int port_fops_poll(struct file *filp, poll_table *wait) -{ - struct port *port; - unsigned int ret; - - port = filp->private_data; - poll_wait(filp, &port->waitqueue, wait); - - if (!port->guest_connected) { - /* Port got unplugged */ - return POLLHUP; - } - ret = 0; - if (!will_read_block(port)) - ret |= POLLIN | POLLRDNORM; - if (!will_write_block(port)) - ret |= POLLOUT; - if (!port->host_connected) - ret |= POLLHUP; - - return ret; -} - -static void remove_port(struct kref *kref); - -static int port_fops_release(struct inode *inode, struct file *filp) -{ - struct port *port; - - port = filp->private_data; - - /* Notify host of port being closed */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); - - spin_lock_irq(&port->inbuf_lock); - port->guest_connected = false; - - discard_port_data(port); - - spin_unlock_irq(&port->inbuf_lock); - - spin_lock_irq(&port->outvq_lock); - reclaim_consumed_buffers(port); - spin_unlock_irq(&port->outvq_lock); - - /* - * Locks aren't necessary here as a port can't be opened after - * unplug, and if a port isn't unplugged, a kref would already - * exist for the port. Plus, taking ports_lock here would - * create a dependency on other locks taken by functions - * inside remove_port if we're the last holder of the port, - * creating many problems. - */ - kref_put(&port->kref, remove_port); - - return 0; -} - -static int port_fops_open(struct inode *inode, struct file *filp) -{ - struct cdev *cdev = inode->i_cdev; - struct port *port; - int ret; - - port = find_port_by_devt(cdev->dev); - filp->private_data = port; - - /* Prevent against a port getting hot-unplugged at the same time */ - spin_lock_irq(&port->portdev->ports_lock); - kref_get(&port->kref); - spin_unlock_irq(&port->portdev->ports_lock); - - /* - * Don't allow opening of console port devices -- that's done - * via /dev/hvc - */ - if (is_console_port(port)) { - ret = -ENXIO; - goto out; - } - - /* Allow only one process to open a particular port at a time */ - spin_lock_irq(&port->inbuf_lock); - if (port->guest_connected) { - spin_unlock_irq(&port->inbuf_lock); - ret = -EMFILE; - goto out; - } - - port->guest_connected = true; - spin_unlock_irq(&port->inbuf_lock); - - spin_lock_irq(&port->outvq_lock); - /* - * There might be a chance that we missed reclaiming a few - * buffers in the window of the port getting previously closed - * and opening now. - */ - reclaim_consumed_buffers(port); - spin_unlock_irq(&port->outvq_lock); - - nonseekable_open(inode, filp); - - /* Notify host of port being opened */ - send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); - - return 0; -out: - kref_put(&port->kref, remove_port); - return ret; -} - -static int port_fops_fasync(int fd, struct file *filp, int mode) -{ - struct port *port; - - port = filp->private_data; - return fasync_helper(fd, filp, mode, &port->async_queue); -} - -/* - * The file operations that we support: programs in the guest can open - * a console device, read from it, write to it, poll for data and - * close it. The devices are at - * /dev/vportp - */ -static const struct file_operations port_fops = { - .owner = THIS_MODULE, - .open = port_fops_open, - .read = port_fops_read, - .write = port_fops_write, - .poll = port_fops_poll, - .release = port_fops_release, - .fasync = port_fops_fasync, - .llseek = no_llseek, -}; - -/* - * The put_chars() callback is pretty straightforward. - * - * We turn the characters into a scatter-gather list, add it to the - * output queue and then kick the Host. Then we sit here waiting for - * it to finish: inefficient in theory, but in practice - * implementations will do it immediately (lguest's Launcher does). - */ -static int put_chars(u32 vtermno, const char *buf, int count) -{ - struct port *port; - - if (unlikely(early_put_chars)) - return early_put_chars(vtermno, buf, count); - - port = find_port_by_vtermno(vtermno); - if (!port) - return -EPIPE; - - return send_buf(port, (void *)buf, count, false); -} - -/* - * get_chars() is the callback from the hvc_console infrastructure - * when an interrupt is received. - * - * We call out to fill_readbuf that gets us the required data from the - * buffers that are queued up. - */ -static int get_chars(u32 vtermno, char *buf, int count) -{ - struct port *port; - - /* If we've not set up the port yet, we have no input to give. */ - if (unlikely(early_put_chars)) - return 0; - - port = find_port_by_vtermno(vtermno); - if (!port) - return -EPIPE; - - /* If we don't have an input queue yet, we can't get input. */ - BUG_ON(!port->in_vq); - - return fill_readbuf(port, buf, count, false); -} - -static void resize_console(struct port *port) -{ - struct virtio_device *vdev; - - /* The port could have been hot-unplugged */ - if (!port || !is_console_port(port)) - return; - - vdev = port->portdev->vdev; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) - hvc_resize(port->cons.hvc, port->cons.ws); -} - -/* We set the configuration at this point, since we now have a tty */ -static int notifier_add_vio(struct hvc_struct *hp, int data) -{ - struct port *port; - - port = find_port_by_vtermno(hp->vtermno); - if (!port) - return -EINVAL; - - hp->irq_requested = 1; - resize_console(port); - - return 0; -} - -static void notifier_del_vio(struct hvc_struct *hp, int data) -{ - hp->irq_requested = 0; -} - -/* The operations for console ports. */ -static const struct hv_ops hv_ops = { - .get_chars = get_chars, - .put_chars = put_chars, - .notifier_add = notifier_add_vio, - .notifier_del = notifier_del_vio, - .notifier_hangup = notifier_del_vio, -}; - -/* - * Console drivers are initialized very early so boot messages can go - * out, so we do things slightly differently from the generic virtio - * initialization of the net and block drivers. - * - * At this stage, the console is output-only. It's too early to set - * up a virtqueue, so we let the drivers do some boutique early-output - * thing. - */ -int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) -{ - early_put_chars = put_chars; - return hvc_instantiate(0, 0, &hv_ops); -} - -int init_port_console(struct port *port) -{ - int ret; - - /* - * The Host's telling us this port is a console port. Hook it - * up with an hvc console. - * - * To set up and manage our virtual console, we call - * hvc_alloc(). - * - * The first argument of hvc_alloc() is the virtual console - * number. The second argument is the parameter for the - * notification mechanism (like irq number). We currently - * leave this as zero, virtqueues have implicit notifications. - * - * The third argument is a "struct hv_ops" containing the - * put_chars() get_chars(), notifier_add() and notifier_del() - * pointers. The final argument is the output buffer size: we - * can do any size, so we put PAGE_SIZE here. - */ - port->cons.vtermno = pdrvdata.next_vtermno; - - port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); - if (IS_ERR(port->cons.hvc)) { - ret = PTR_ERR(port->cons.hvc); - dev_err(port->dev, - "error %d allocating hvc for port\n", ret); - port->cons.hvc = NULL; - return ret; - } - spin_lock_irq(&pdrvdata_lock); - pdrvdata.next_vtermno++; - list_add_tail(&port->cons.list, &pdrvdata.consoles); - spin_unlock_irq(&pdrvdata_lock); - port->guest_connected = true; - - /* - * Start using the new console output if this is the first - * console to come up. - */ - if (early_put_chars) - early_put_chars = NULL; - - /* Notify host of port being opened */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); - - return 0; -} - -static ssize_t show_port_name(struct device *dev, - struct device_attribute *attr, char *buffer) -{ - struct port *port; - - port = dev_get_drvdata(dev); - - return sprintf(buffer, "%s\n", port->name); -} - -static DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL); - -static struct attribute *port_sysfs_entries[] = { - &dev_attr_name.attr, - NULL -}; - -static struct attribute_group port_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = port_sysfs_entries, -}; - -static int debugfs_open(struct inode *inode, struct file *filp) -{ - filp->private_data = inode->i_private; - return 0; -} - -static ssize_t debugfs_read(struct file *filp, char __user *ubuf, - size_t count, loff_t *offp) -{ - struct port *port; - char *buf; - ssize_t ret, out_offset, out_count; - - out_count = 1024; - buf = kmalloc(out_count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - port = filp->private_data; - out_offset = 0; - out_offset += snprintf(buf + out_offset, out_count, - "name: %s\n", port->name ? port->name : ""); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "guest_connected: %d\n", port->guest_connected); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "host_connected: %d\n", port->host_connected); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "outvq_full: %d\n", port->outvq_full); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "is_console: %s\n", - is_console_port(port) ? "yes" : "no"); - out_offset += snprintf(buf + out_offset, out_count - out_offset, - "console_vtermno: %u\n", port->cons.vtermno); - - ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); - kfree(buf); - return ret; -} - -static const struct file_operations port_debugfs_ops = { - .owner = THIS_MODULE, - .open = debugfs_open, - .read = debugfs_read, -}; - -static void set_console_size(struct port *port, u16 rows, u16 cols) -{ - if (!port || !is_console_port(port)) - return; - - port->cons.ws.ws_row = rows; - port->cons.ws.ws_col = cols; -} - -static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) -{ - struct port_buffer *buf; - unsigned int nr_added_bufs; - int ret; - - nr_added_bufs = 0; - do { - buf = alloc_buf(PAGE_SIZE); - if (!buf) - break; - - spin_lock_irq(lock); - ret = add_inbuf(vq, buf); - if (ret < 0) { - spin_unlock_irq(lock); - free_buf(buf); - break; - } - nr_added_bufs++; - spin_unlock_irq(lock); - } while (ret > 0); - - return nr_added_bufs; -} - -static void send_sigio_to_port(struct port *port) -{ - if (port->async_queue && port->guest_connected) - kill_fasync(&port->async_queue, SIGIO, POLL_OUT); -} - -static int add_port(struct ports_device *portdev, u32 id) -{ - char debugfs_name[16]; - struct port *port; - struct port_buffer *buf; - dev_t devt; - unsigned int nr_added_bufs; - int err; - - port = kmalloc(sizeof(*port), GFP_KERNEL); - if (!port) { - err = -ENOMEM; - goto fail; - } - kref_init(&port->kref); - - port->portdev = portdev; - port->id = id; - - port->name = NULL; - port->inbuf = NULL; - port->cons.hvc = NULL; - port->async_queue = NULL; - - port->cons.ws.ws_row = port->cons.ws.ws_col = 0; - - port->host_connected = port->guest_connected = false; - - port->outvq_full = false; - - port->in_vq = portdev->in_vqs[port->id]; - port->out_vq = portdev->out_vqs[port->id]; - - port->cdev = cdev_alloc(); - if (!port->cdev) { - dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n"); - err = -ENOMEM; - goto free_port; - } - port->cdev->ops = &port_fops; - - devt = MKDEV(portdev->chr_major, id); - err = cdev_add(port->cdev, devt, 1); - if (err < 0) { - dev_err(&port->portdev->vdev->dev, - "Error %d adding cdev for port %u\n", err, id); - goto free_cdev; - } - port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, - devt, port, "vport%up%u", - port->portdev->drv_index, id); - if (IS_ERR(port->dev)) { - err = PTR_ERR(port->dev); - dev_err(&port->portdev->vdev->dev, - "Error %d creating device for port %u\n", - err, id); - goto free_cdev; - } - - spin_lock_init(&port->inbuf_lock); - spin_lock_init(&port->outvq_lock); - init_waitqueue_head(&port->waitqueue); - - /* Fill the in_vq with buffers so the host can send us data. */ - nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); - if (!nr_added_bufs) { - dev_err(port->dev, "Error allocating inbufs\n"); - err = -ENOMEM; - goto free_device; - } - - /* - * If we're not using multiport support, this has to be a console port - */ - if (!use_multiport(port->portdev)) { - err = init_port_console(port); - if (err) - goto free_inbufs; - } - - spin_lock_irq(&portdev->ports_lock); - list_add_tail(&port->list, &port->portdev->ports); - spin_unlock_irq(&portdev->ports_lock); - - /* - * Tell the Host we're set so that it can send us various - * configuration parameters for this port (eg, port name, - * caching, whether this is a console port, etc.) - */ - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - - if (pdrvdata.debugfs_dir) { - /* - * Finally, create the debugfs file that we can use to - * inspect a port's state at any time - */ - sprintf(debugfs_name, "vport%up%u", - port->portdev->drv_index, id); - port->debugfs_file = debugfs_create_file(debugfs_name, 0444, - pdrvdata.debugfs_dir, - port, - &port_debugfs_ops); - } - return 0; - -free_inbufs: - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf); -free_device: - device_destroy(pdrvdata.class, port->dev->devt); -free_cdev: - cdev_del(port->cdev); -free_port: - kfree(port); -fail: - /* The host might want to notify management sw about port add failure */ - __send_control_msg(portdev, id, VIRTIO_CONSOLE_PORT_READY, 0); - return err; -} - -/* No users remain, remove all port-specific data. */ -static void remove_port(struct kref *kref) -{ - struct port *port; - - port = container_of(kref, struct port, kref); - - sysfs_remove_group(&port->dev->kobj, &port_attribute_group); - device_destroy(pdrvdata.class, port->dev->devt); - cdev_del(port->cdev); - - kfree(port->name); - - debugfs_remove(port->debugfs_file); - - kfree(port); -} - -/* - * Port got unplugged. Remove port from portdev's list and drop the - * kref reference. If no userspace has this port opened, it will - * result in immediate removal the port. - */ -static void unplug_port(struct port *port) -{ - struct port_buffer *buf; - - spin_lock_irq(&port->portdev->ports_lock); - list_del(&port->list); - spin_unlock_irq(&port->portdev->ports_lock); - - if (port->guest_connected) { - port->guest_connected = false; - port->host_connected = false; - wake_up_interruptible(&port->waitqueue); - - /* Let the app know the port is going down. */ - send_sigio_to_port(port); - } - - if (is_console_port(port)) { - spin_lock_irq(&pdrvdata_lock); - list_del(&port->cons.list); - spin_unlock_irq(&pdrvdata_lock); -#if 0 - /* - * hvc_remove() not called as removing one hvc port - * results in other hvc ports getting frozen. - * - * Once this is resolved in hvc, this functionality - * will be enabled. Till that is done, the -EPIPE - * return from get_chars() above will help - * hvc_console.c to clean up on ports we remove here. - */ - hvc_remove(port->cons.hvc); -#endif - } - - /* Remove unused data this port might have received. */ - discard_port_data(port); - - reclaim_consumed_buffers(port); - - /* Remove buffers we queued up for the Host to send us data in. */ - while ((buf = virtqueue_detach_unused_buf(port->in_vq))) - free_buf(buf); - - /* - * We should just assume the device itself has gone off -- - * else a close on an open port later will try to send out a - * control message. - */ - port->portdev = NULL; - - /* - * Locks around here are not necessary - a port can't be - * opened after we removed the port struct from ports_list - * above. - */ - kref_put(&port->kref, remove_port); -} - -/* Any private messages that the Host and Guest want to share */ -static void handle_control_message(struct ports_device *portdev, - struct port_buffer *buf) -{ - struct virtio_console_control *cpkt; - struct port *port; - size_t name_size; - int err; - - cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); - - port = find_port_by_id(portdev, cpkt->id); - if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { - /* No valid header at start of buffer. Drop it. */ - dev_dbg(&portdev->vdev->dev, - "Invalid index %u in control packet\n", cpkt->id); - return; - } - - switch (cpkt->event) { - case VIRTIO_CONSOLE_PORT_ADD: - if (port) { - dev_dbg(&portdev->vdev->dev, - "Port %u already added\n", port->id); - send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); - break; - } - if (cpkt->id >= portdev->config.max_nr_ports) { - dev_warn(&portdev->vdev->dev, - "Request for adding port with out-of-bound id %u, max. supported id: %u\n", - cpkt->id, portdev->config.max_nr_ports - 1); - break; - } - add_port(portdev, cpkt->id); - break; - case VIRTIO_CONSOLE_PORT_REMOVE: - unplug_port(port); - break; - case VIRTIO_CONSOLE_CONSOLE_PORT: - if (!cpkt->value) - break; - if (is_console_port(port)) - break; - - init_port_console(port); - /* - * Could remove the port here in case init fails - but - * have to notify the host first. - */ - break; - case VIRTIO_CONSOLE_RESIZE: { - struct { - __u16 rows; - __u16 cols; - } size; - - if (!is_console_port(port)) - break; - - memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), - sizeof(size)); - set_console_size(port, size.rows, size.cols); - - port->cons.hvc->irq_requested = 1; - resize_console(port); - break; - } - case VIRTIO_CONSOLE_PORT_OPEN: - port->host_connected = cpkt->value; - wake_up_interruptible(&port->waitqueue); - /* - * If the host port got closed and the host had any - * unconsumed buffers, we'll be able to reclaim them - * now. - */ - spin_lock_irq(&port->outvq_lock); - reclaim_consumed_buffers(port); - spin_unlock_irq(&port->outvq_lock); - - /* - * If the guest is connected, it'll be interested in - * knowing the host connection state changed. - */ - send_sigio_to_port(port); - break; - case VIRTIO_CONSOLE_PORT_NAME: - /* - * Skip the size of the header and the cpkt to get the size - * of the name that was sent - */ - name_size = buf->len - buf->offset - sizeof(*cpkt) + 1; - - port->name = kmalloc(name_size, GFP_KERNEL); - if (!port->name) { - dev_err(port->dev, - "Not enough space to store port name\n"); - break; - } - strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), - name_size - 1); - port->name[name_size - 1] = 0; - - /* - * Since we only have one sysfs attribute, 'name', - * create it only if we have a name for the port. - */ - err = sysfs_create_group(&port->dev->kobj, - &port_attribute_group); - if (err) { - dev_err(port->dev, - "Error %d creating sysfs device attributes\n", - err); - } else { - /* - * Generate a udev event so that appropriate - * symlinks can be created based on udev - * rules. - */ - kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); - } - break; - } -} - -static void control_work_handler(struct work_struct *work) -{ - struct ports_device *portdev; - struct virtqueue *vq; - struct port_buffer *buf; - unsigned int len; - - portdev = container_of(work, struct ports_device, control_work); - vq = portdev->c_ivq; - - spin_lock(&portdev->cvq_lock); - while ((buf = virtqueue_get_buf(vq, &len))) { - spin_unlock(&portdev->cvq_lock); - - buf->len = len; - buf->offset = 0; - - handle_control_message(portdev, buf); - - spin_lock(&portdev->cvq_lock); - if (add_inbuf(portdev->c_ivq, buf) < 0) { - dev_warn(&portdev->vdev->dev, - "Error adding buffer to queue\n"); - free_buf(buf); - } - } - spin_unlock(&portdev->cvq_lock); -} - -static void in_intr(struct virtqueue *vq) -{ - struct port *port; - unsigned long flags; - - port = find_port_by_vq(vq->vdev->priv, vq); - if (!port) - return; - - spin_lock_irqsave(&port->inbuf_lock, flags); - if (!port->inbuf) - port->inbuf = get_inbuf(port); - - /* - * Don't queue up data when port is closed. This condition - * can be reached when a console port is not yet connected (no - * tty is spawned) and the host sends out data to console - * ports. For generic serial ports, the host won't - * (shouldn't) send data till the guest is connected. - */ - if (!port->guest_connected) - discard_port_data(port); - - spin_unlock_irqrestore(&port->inbuf_lock, flags); - - wake_up_interruptible(&port->waitqueue); - - /* Send a SIGIO indicating new data in case the process asked for it */ - send_sigio_to_port(port); - - if (is_console_port(port) && hvc_poll(port->cons.hvc)) - hvc_kick(); -} - -static void control_intr(struct virtqueue *vq) -{ - struct ports_device *portdev; - - portdev = vq->vdev->priv; - schedule_work(&portdev->control_work); -} - -static void config_intr(struct virtio_device *vdev) -{ - struct ports_device *portdev; - - portdev = vdev->priv; - - if (!use_multiport(portdev)) { - struct port *port; - u16 rows, cols; - - vdev->config->get(vdev, - offsetof(struct virtio_console_config, cols), - &cols, sizeof(u16)); - vdev->config->get(vdev, - offsetof(struct virtio_console_config, rows), - &rows, sizeof(u16)); - - port = find_port_by_id(portdev, 0); - set_console_size(port, rows, cols); - - /* - * We'll use this way of resizing only for legacy - * support. For newer userspace - * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages - * to indicate console size changes so that it can be - * done per-port. - */ - resize_console(port); - } -} - -static int init_vqs(struct ports_device *portdev) -{ - vq_callback_t **io_callbacks; - char **io_names; - struct virtqueue **vqs; - u32 i, j, nr_ports, nr_queues; - int err; - - nr_ports = portdev->config.max_nr_ports; - nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; - - vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); - io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL); - io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL); - portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), - GFP_KERNEL); - portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), - GFP_KERNEL); - if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || - !portdev->out_vqs) { - err = -ENOMEM; - goto free; - } - - /* - * For backward compat (newer host but older guest), the host - * spawns a console port first and also inits the vqs for port - * 0 before others. - */ - j = 0; - io_callbacks[j] = in_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "input"; - io_names[j + 1] = "output"; - j += 2; - - if (use_multiport(portdev)) { - io_callbacks[j] = control_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "control-i"; - io_names[j + 1] = "control-o"; - - for (i = 1; i < nr_ports; i++) { - j += 2; - io_callbacks[j] = in_intr; - io_callbacks[j + 1] = NULL; - io_names[j] = "input"; - io_names[j + 1] = "output"; - } - } - /* Find the queues. */ - err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, - io_callbacks, - (const char **)io_names); - if (err) - goto free; - - j = 0; - portdev->in_vqs[0] = vqs[0]; - portdev->out_vqs[0] = vqs[1]; - j += 2; - if (use_multiport(portdev)) { - portdev->c_ivq = vqs[j]; - portdev->c_ovq = vqs[j + 1]; - - for (i = 1; i < nr_ports; i++) { - j += 2; - portdev->in_vqs[i] = vqs[j]; - portdev->out_vqs[i] = vqs[j + 1]; - } - } - kfree(io_names); - kfree(io_callbacks); - kfree(vqs); - - return 0; - -free: - kfree(portdev->out_vqs); - kfree(portdev->in_vqs); - kfree(io_names); - kfree(io_callbacks); - kfree(vqs); - - return err; -} - -static const struct file_operations portdev_fops = { - .owner = THIS_MODULE, -}; - -/* - * Once we're further in boot, we get probed like any other virtio - * device. - * - * If the host also supports multiple console ports, we check the - * config space to see how many ports the host has spawned. We - * initialize each port found. - */ -static int __devinit virtcons_probe(struct virtio_device *vdev) -{ - struct ports_device *portdev; - int err; - bool multiport; - - portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); - if (!portdev) { - err = -ENOMEM; - goto fail; - } - - /* Attach this portdev to this virtio_device, and vice-versa. */ - portdev->vdev = vdev; - vdev->priv = portdev; - - spin_lock_irq(&pdrvdata_lock); - portdev->drv_index = pdrvdata.index++; - spin_unlock_irq(&pdrvdata_lock); - - portdev->chr_major = register_chrdev(0, "virtio-portsdev", - &portdev_fops); - if (portdev->chr_major < 0) { - dev_err(&vdev->dev, - "Error %d registering chrdev for device %u\n", - portdev->chr_major, portdev->drv_index); - err = portdev->chr_major; - goto free; - } - - multiport = false; - portdev->config.max_nr_ports = 1; - if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { - multiport = true; - vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; - - vdev->config->get(vdev, offsetof(struct virtio_console_config, - max_nr_ports), - &portdev->config.max_nr_ports, - sizeof(portdev->config.max_nr_ports)); - } - - /* Let the Host know we support multiple ports.*/ - vdev->config->finalize_features(vdev); - - err = init_vqs(portdev); - if (err < 0) { - dev_err(&vdev->dev, "Error %d initializing vqs\n", err); - goto free_chrdev; - } - - spin_lock_init(&portdev->ports_lock); - INIT_LIST_HEAD(&portdev->ports); - - if (multiport) { - unsigned int nr_added_bufs; - - spin_lock_init(&portdev->cvq_lock); - INIT_WORK(&portdev->control_work, &control_work_handler); - - nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); - if (!nr_added_bufs) { - dev_err(&vdev->dev, - "Error allocating buffers for control queue\n"); - err = -ENOMEM; - goto free_vqs; - } - } else { - /* - * For backward compatibility: Create a console port - * if we're running on older host. - */ - add_port(portdev, 0); - } - - spin_lock_irq(&pdrvdata_lock); - list_add_tail(&portdev->list, &pdrvdata.portdevs); - spin_unlock_irq(&pdrvdata_lock); - - __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, - VIRTIO_CONSOLE_DEVICE_READY, 1); - return 0; - -free_vqs: - /* The host might want to notify mgmt sw about device add failure */ - __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, - VIRTIO_CONSOLE_DEVICE_READY, 0); - vdev->config->del_vqs(vdev); - kfree(portdev->in_vqs); - kfree(portdev->out_vqs); -free_chrdev: - unregister_chrdev(portdev->chr_major, "virtio-portsdev"); -free: - kfree(portdev); -fail: - return err; -} - -static void virtcons_remove(struct virtio_device *vdev) -{ - struct ports_device *portdev; - struct port *port, *port2; - - portdev = vdev->priv; - - spin_lock_irq(&pdrvdata_lock); - list_del(&portdev->list); - spin_unlock_irq(&pdrvdata_lock); - - /* Disable interrupts for vqs */ - vdev->config->reset(vdev); - /* Finish up work that's lined up */ - cancel_work_sync(&portdev->control_work); - - list_for_each_entry_safe(port, port2, &portdev->ports, list) - unplug_port(port); - - unregister_chrdev(portdev->chr_major, "virtio-portsdev"); - - /* - * When yanking out a device, we immediately lose the - * (device-side) queues. So there's no point in keeping the - * guest side around till we drop our final reference. This - * also means that any ports which are in an open state will - * have to just stop using the port, as the vqs are going - * away. - */ - if (use_multiport(portdev)) { - struct port_buffer *buf; - unsigned int len; - - while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) - free_buf(buf); - - while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) - free_buf(buf); - } - - vdev->config->del_vqs(vdev); - kfree(portdev->in_vqs); - kfree(portdev->out_vqs); - - kfree(portdev); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, - { 0 }, -}; - -static unsigned int features[] = { - VIRTIO_CONSOLE_F_SIZE, - VIRTIO_CONSOLE_F_MULTIPORT, -}; - -static struct virtio_driver virtio_console = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), - .driver.name = KBUILD_MODNAME, - .driver.owner = THIS_MODULE, - .id_table = id_table, - .probe = virtcons_probe, - .remove = virtcons_remove, - .config_changed = config_intr, -}; - -static int __init init(void) -{ - int err; - - pdrvdata.class = class_create(THIS_MODULE, "virtio-ports"); - if (IS_ERR(pdrvdata.class)) { - err = PTR_ERR(pdrvdata.class); - pr_err("Error %d creating virtio-ports class\n", err); - return err; - } - - pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL); - if (!pdrvdata.debugfs_dir) { - pr_warning("Error %ld creating debugfs dir for virtio-ports\n", - PTR_ERR(pdrvdata.debugfs_dir)); - } - INIT_LIST_HEAD(&pdrvdata.consoles); - INIT_LIST_HEAD(&pdrvdata.portdevs); - - return register_virtio_driver(&virtio_console); -} - -static void __exit fini(void) -{ - unregister_virtio_driver(&virtio_console); - - class_destroy(pdrvdata.class); - if (pdrvdata.debugfs_dir) - debugfs_remove_recursive(pdrvdata.debugfs_dir); -} -module_init(init); -module_exit(fini); - -MODULE_DEVICE_TABLE(virtio, id_table); -MODULE_DESCRIPTION("Virtio console driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index c43ef48b1a0f..d3685f071b8d 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_R3964) += n_r3964.o obj-y += vt/ +obj-$(CONFIG_HVC_DRIVER) += hvc/ diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile new file mode 100644 index 000000000000..e6bed5f177ff --- /dev/null +++ b/drivers/tty/hvc/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o +obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o +obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o +obj-$(CONFIG_HVC_TILE) += hvc_tile.o +obj-$(CONFIG_HVC_DCC) += hvc_dcc.o +obj-$(CONFIG_HVC_BEAT) += hvc_beat.o +obj-$(CONFIG_HVC_DRIVER) += hvc_console.o +obj-$(CONFIG_HVC_IRQ) += hvc_irq.o +obj-$(CONFIG_HVC_XEN) += hvc_xen.o +obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o +obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o +obj-$(CONFIG_HVCS) += hvcs.o +obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o diff --git a/drivers/tty/hvc/hvc_beat.c b/drivers/tty/hvc/hvc_beat.c new file mode 100644 index 000000000000..5fe4631e2a61 --- /dev/null +++ b/drivers/tty/hvc/hvc_beat.c @@ -0,0 +1,134 @@ +/* + * Beat hypervisor console driver + * + * (C) Copyright 2006 TOSHIBA CORPORATION + * + * This code is based on drivers/char/hvc_rtas.c: + * (C) Copyright IBM Corporation 2001-2005 + * (C) Copyright Red Hat, Inc. 2005 + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hvc_console.h" + +extern int64_t beat_get_term_char(uint64_t, uint64_t *, uint64_t *, uint64_t *); +extern int64_t beat_put_term_char(uint64_t, uint64_t, uint64_t, uint64_t); + +struct hvc_struct *hvc_beat_dev = NULL; + +/* bug: only one queue is available regardless of vtermno */ +static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) +{ + static unsigned char q[sizeof(unsigned long) * 2] + __attribute__((aligned(sizeof(unsigned long)))); + static int qlen = 0; + u64 got; + +again: + if (qlen) { + if (qlen > cnt) { + memcpy(buf, q, cnt); + qlen -= cnt; + memmove(q + cnt, q, qlen); + return cnt; + } else { /* qlen <= cnt */ + int r; + + memcpy(buf, q, qlen); + r = qlen; + qlen = 0; + return r; + } + } + if (beat_get_term_char(vtermno, &got, + ((u64 *)q), ((u64 *)q) + 1) == 0) { + qlen = got; + goto again; + } + return 0; +} + +static int hvc_beat_put_chars(uint32_t vtermno, const char *buf, int cnt) +{ + unsigned long kb[2]; + int rest, nlen; + + for (rest = cnt; rest > 0; rest -= nlen) { + nlen = (rest > 16) ? 16 : rest; + memcpy(kb, buf, nlen); + beat_put_term_char(vtermno, nlen, kb[0], kb[1]); + buf += nlen; + } + return cnt; +} + +static const struct hv_ops hvc_beat_get_put_ops = { + .get_chars = hvc_beat_get_chars, + .put_chars = hvc_beat_put_chars, +}; + +static int hvc_beat_useit = 1; + +static int hvc_beat_config(char *p) +{ + hvc_beat_useit = simple_strtoul(p, NULL, 0); + return 0; +} + +static int __init hvc_beat_console_init(void) +{ + if (hvc_beat_useit && of_machine_is_compatible("Beat")) { + hvc_instantiate(0, 0, &hvc_beat_get_put_ops); + } + return 0; +} + +/* temp */ +static int __init hvc_beat_init(void) +{ + struct hvc_struct *hp; + + if (!firmware_has_feature(FW_FEATURE_BEAT)) + return -ENODEV; + + hp = hvc_alloc(0, NO_IRQ, &hvc_beat_get_put_ops, 16); + if (IS_ERR(hp)) + return PTR_ERR(hp); + hvc_beat_dev = hp; + return 0; +} + +static void __exit hvc_beat_exit(void) +{ + if (hvc_beat_dev) + hvc_remove(hvc_beat_dev); +} + +module_init(hvc_beat_init); +module_exit(hvc_beat_exit); + +__setup("hvc_beat=", hvc_beat_config); + +console_initcall(hvc_beat_console_init); diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c new file mode 100644 index 000000000000..e9cba13ee800 --- /dev/null +++ b/drivers/tty/hvc/hvc_console.c @@ -0,0 +1,914 @@ +/* + * Copyright (C) 2001 Anton Blanchard , IBM + * Copyright (C) 2001 Paul Mackerras , IBM + * Copyright (C) 2004 Benjamin Herrenschmidt , IBM Corp. + * Copyright (C) 2004 IBM Corporation + * + * Additional Author(s): + * Ryan S. Arnold + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hvc_console.h" + +#define HVC_MAJOR 229 +#define HVC_MINOR 0 + +/* + * Wait this long per iteration while trying to push buffered data to the + * hypervisor before allowing the tty to complete a close operation. + */ +#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ + +/* + * These sizes are most efficient for vio, because they are the + * native transfer size. We could make them selectable in the + * future to better deal with backends that want other buffer sizes. + */ +#define N_OUTBUF 16 +#define N_INBUF 16 + +#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) + +static struct tty_driver *hvc_driver; +static struct task_struct *hvc_task; + +/* Picks up late kicks after list walk but before schedule() */ +static int hvc_kicked; + +static int hvc_init(void); + +#ifdef CONFIG_MAGIC_SYSRQ +static int sysrq_pressed; +#endif + +/* dynamic list of hvc_struct instances */ +static LIST_HEAD(hvc_structs); + +/* + * Protect the list of hvc_struct instances from inserts and removals during + * list traversal. + */ +static DEFINE_SPINLOCK(hvc_structs_lock); + +/* + * This value is used to assign a tty->index value to a hvc_struct based + * upon order of exposure via hvc_probe(), when we can not match it to + * a console candidate registered with hvc_instantiate(). + */ +static int last_hvc = -1; + +/* + * Do not call this function with either the hvc_structs_lock or the hvc_struct + * lock held. If successful, this function increments the kref reference + * count against the target hvc_struct so it should be released when finished. + */ +static struct hvc_struct *hvc_get_by_index(int index) +{ + struct hvc_struct *hp; + unsigned long flags; + + spin_lock(&hvc_structs_lock); + + list_for_each_entry(hp, &hvc_structs, next) { + spin_lock_irqsave(&hp->lock, flags); + if (hp->index == index) { + kref_get(&hp->kref); + spin_unlock_irqrestore(&hp->lock, flags); + spin_unlock(&hvc_structs_lock); + return hp; + } + spin_unlock_irqrestore(&hp->lock, flags); + } + hp = NULL; + + spin_unlock(&hvc_structs_lock); + return hp; +} + + +/* + * Initial console vtermnos for console API usage prior to full console + * initialization. Any vty adapter outside this range will not have usable + * console interfaces but can still be used as a tty device. This has to be + * static because kmalloc will not work during early console init. + */ +static const struct hv_ops *cons_ops[MAX_NR_HVC_CONSOLES]; +static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] = + {[0 ... MAX_NR_HVC_CONSOLES - 1] = -1}; + +/* + * Console APIs, NOT TTY. These APIs are available immediately when + * hvc_console_setup() finds adapters. + */ + +static void hvc_console_print(struct console *co, const char *b, + unsigned count) +{ + char c[N_OUTBUF] __ALIGNED__; + unsigned i = 0, n = 0; + int r, donecr = 0, index = co->index; + + /* Console access attempt outside of acceptable console range. */ + if (index >= MAX_NR_HVC_CONSOLES) + return; + + /* This console adapter was removed so it is not usable. */ + if (vtermnos[index] == -1) + return; + + while (count > 0 || i > 0) { + if (count > 0 && i < sizeof(c)) { + if (b[n] == '\n' && !donecr) { + c[i++] = '\r'; + donecr = 1; + } else { + c[i++] = b[n++]; + donecr = 0; + --count; + } + } else { + r = cons_ops[index]->put_chars(vtermnos[index], c, i); + if (r <= 0) { + /* throw away chars on error */ + i = 0; + } else if (r > 0) { + i -= r; + if (i > 0) + memmove(c, c+r, i); + } + } + } +} + +static struct tty_driver *hvc_console_device(struct console *c, int *index) +{ + if (vtermnos[c->index] == -1) + return NULL; + + *index = c->index; + return hvc_driver; +} + +static int __init hvc_console_setup(struct console *co, char *options) +{ + if (co->index < 0 || co->index >= MAX_NR_HVC_CONSOLES) + return -ENODEV; + + if (vtermnos[co->index] == -1) + return -ENODEV; + + return 0; +} + +static struct console hvc_console = { + .name = "hvc", + .write = hvc_console_print, + .device = hvc_console_device, + .setup = hvc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +/* + * Early console initialization. Precedes driver initialization. + * + * (1) we are first, and the user specified another driver + * -- index will remain -1 + * (2) we are first and the user specified no driver + * -- index will be set to 0, then we will fail setup. + * (3) we are first and the user specified our driver + * -- index will be set to user specified driver, and we will fail + * (4) we are after driver, and this initcall will register us + * -- if the user didn't specify a driver then the console will match + * + * Note that for cases 2 and 3, we will match later when the io driver + * calls hvc_instantiate() and call register again. + */ +static int __init hvc_console_init(void) +{ + register_console(&hvc_console); + return 0; +} +console_initcall(hvc_console_init); + +/* callback when the kboject ref count reaches zero. */ +static void destroy_hvc_struct(struct kref *kref) +{ + struct hvc_struct *hp = container_of(kref, struct hvc_struct, kref); + unsigned long flags; + + spin_lock(&hvc_structs_lock); + + spin_lock_irqsave(&hp->lock, flags); + list_del(&(hp->next)); + spin_unlock_irqrestore(&hp->lock, flags); + + spin_unlock(&hvc_structs_lock); + + kfree(hp); +} + +/* + * hvc_instantiate() is an early console discovery method which locates + * consoles * prior to the vio subsystem discovering them. Hotplugged + * vty adapters do NOT get an hvc_instantiate() callback since they + * appear after early console init. + */ +int hvc_instantiate(uint32_t vtermno, int index, const struct hv_ops *ops) +{ + struct hvc_struct *hp; + + if (index < 0 || index >= MAX_NR_HVC_CONSOLES) + return -1; + + if (vtermnos[index] != -1) + return -1; + + /* make sure no no tty has been registered in this index */ + hp = hvc_get_by_index(index); + if (hp) { + kref_put(&hp->kref, destroy_hvc_struct); + return -1; + } + + vtermnos[index] = vtermno; + cons_ops[index] = ops; + + /* reserve all indices up to and including this index */ + if (last_hvc < index) + last_hvc = index; + + /* if this index is what the user requested, then register + * now (setup won't fail at this point). It's ok to just + * call register again if previously .setup failed. + */ + if (index == hvc_console.index) + register_console(&hvc_console); + + return 0; +} +EXPORT_SYMBOL_GPL(hvc_instantiate); + +/* Wake the sleeping khvcd */ +void hvc_kick(void) +{ + hvc_kicked = 1; + wake_up_process(hvc_task); +} +EXPORT_SYMBOL_GPL(hvc_kick); + +static void hvc_unthrottle(struct tty_struct *tty) +{ + hvc_kick(); +} + +/* + * The TTY interface won't be used until after the vio layer has exposed the vty + * adapter to the kernel. + */ +static int hvc_open(struct tty_struct *tty, struct file * filp) +{ + struct hvc_struct *hp; + unsigned long flags; + int rc = 0; + + /* Auto increments kref reference if found. */ + if (!(hp = hvc_get_by_index(tty->index))) + return -ENODEV; + + spin_lock_irqsave(&hp->lock, flags); + /* Check and then increment for fast path open. */ + if (hp->count++ > 0) { + tty_kref_get(tty); + spin_unlock_irqrestore(&hp->lock, flags); + hvc_kick(); + return 0; + } /* else count == 0 */ + + tty->driver_data = hp; + + hp->tty = tty_kref_get(tty); + + spin_unlock_irqrestore(&hp->lock, flags); + + if (hp->ops->notifier_add) + rc = hp->ops->notifier_add(hp, hp->data); + + /* + * If the notifier fails we return an error. The tty layer + * will call hvc_close() after a failed open but we don't want to clean + * up there so we'll clean up here and clear out the previously set + * tty fields and return the kref reference. + */ + if (rc) { + spin_lock_irqsave(&hp->lock, flags); + hp->tty = NULL; + spin_unlock_irqrestore(&hp->lock, flags); + tty_kref_put(tty); + tty->driver_data = NULL; + kref_put(&hp->kref, destroy_hvc_struct); + printk(KERN_ERR "hvc_open: request_irq failed with rc %d.\n", rc); + } + /* Force wakeup of the polling thread */ + hvc_kick(); + + return rc; +} + +static void hvc_close(struct tty_struct *tty, struct file * filp) +{ + struct hvc_struct *hp; + unsigned long flags; + + if (tty_hung_up_p(filp)) + return; + + /* + * No driver_data means that this close was issued after a failed + * hvc_open by the tty layer's release_dev() function and we can just + * exit cleanly because the kref reference wasn't made. + */ + if (!tty->driver_data) + return; + + hp = tty->driver_data; + + spin_lock_irqsave(&hp->lock, flags); + + if (--hp->count == 0) { + /* We are done with the tty pointer now. */ + hp->tty = NULL; + spin_unlock_irqrestore(&hp->lock, flags); + + if (hp->ops->notifier_del) + hp->ops->notifier_del(hp, hp->data); + + /* cancel pending tty resize work */ + cancel_work_sync(&hp->tty_resize); + + /* + * Chain calls chars_in_buffer() and returns immediately if + * there is no buffered data otherwise sleeps on a wait queue + * waking periodically to check chars_in_buffer(). + */ + tty_wait_until_sent(tty, HVC_CLOSE_WAIT); + } else { + if (hp->count < 0) + printk(KERN_ERR "hvc_close %X: oops, count is %d\n", + hp->vtermno, hp->count); + spin_unlock_irqrestore(&hp->lock, flags); + } + + tty_kref_put(tty); + kref_put(&hp->kref, destroy_hvc_struct); +} + +static void hvc_hangup(struct tty_struct *tty) +{ + struct hvc_struct *hp = tty->driver_data; + unsigned long flags; + int temp_open_count; + + if (!hp) + return; + + /* cancel pending tty resize work */ + cancel_work_sync(&hp->tty_resize); + + spin_lock_irqsave(&hp->lock, flags); + + /* + * The N_TTY line discipline has problems such that in a close vs + * open->hangup case this can be called after the final close so prevent + * that from happening for now. + */ + if (hp->count <= 0) { + spin_unlock_irqrestore(&hp->lock, flags); + return; + } + + temp_open_count = hp->count; + hp->count = 0; + hp->n_outbuf = 0; + hp->tty = NULL; + + spin_unlock_irqrestore(&hp->lock, flags); + + if (hp->ops->notifier_hangup) + hp->ops->notifier_hangup(hp, hp->data); + + while(temp_open_count) { + --temp_open_count; + tty_kref_put(tty); + kref_put(&hp->kref, destroy_hvc_struct); + } +} + +/* + * Push buffered characters whether they were just recently buffered or waiting + * on a blocked hypervisor. Call this function with hp->lock held. + */ +static int hvc_push(struct hvc_struct *hp) +{ + int n; + + n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); + if (n <= 0) { + if (n == 0) { + hp->do_wakeup = 1; + return 0; + } + /* throw away output on error; this happens when + there is no session connected to the vterm. */ + hp->n_outbuf = 0; + } else + hp->n_outbuf -= n; + if (hp->n_outbuf > 0) + memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf); + else + hp->do_wakeup = 1; + + return n; +} + +static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct hvc_struct *hp = tty->driver_data; + unsigned long flags; + int rsize, written = 0; + + /* This write was probably executed during a tty close. */ + if (!hp) + return -EPIPE; + + if (hp->count <= 0) + return -EIO; + + spin_lock_irqsave(&hp->lock, flags); + + /* Push pending writes */ + if (hp->n_outbuf > 0) + hvc_push(hp); + + while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) { + if (rsize > count) + rsize = count; + memcpy(hp->outbuf + hp->n_outbuf, buf, rsize); + count -= rsize; + buf += rsize; + hp->n_outbuf += rsize; + written += rsize; + hvc_push(hp); + } + spin_unlock_irqrestore(&hp->lock, flags); + + /* + * Racy, but harmless, kick thread if there is still pending data. + */ + if (hp->n_outbuf) + hvc_kick(); + + return written; +} + +/** + * hvc_set_winsz() - Resize the hvc tty terminal window. + * @work: work structure. + * + * The routine shall not be called within an atomic context because it + * might sleep. + * + * Locking: hp->lock + */ +static void hvc_set_winsz(struct work_struct *work) +{ + struct hvc_struct *hp; + unsigned long hvc_flags; + struct tty_struct *tty; + struct winsize ws; + + hp = container_of(work, struct hvc_struct, tty_resize); + + spin_lock_irqsave(&hp->lock, hvc_flags); + if (!hp->tty) { + spin_unlock_irqrestore(&hp->lock, hvc_flags); + return; + } + ws = hp->ws; + tty = tty_kref_get(hp->tty); + spin_unlock_irqrestore(&hp->lock, hvc_flags); + + tty_do_resize(tty, &ws); + tty_kref_put(tty); +} + +/* + * This is actually a contract between the driver and the tty layer outlining + * how much write room the driver can guarantee will be sent OR BUFFERED. This + * driver MUST honor the return value. + */ +static int hvc_write_room(struct tty_struct *tty) +{ + struct hvc_struct *hp = tty->driver_data; + + if (!hp) + return -1; + + return hp->outbuf_size - hp->n_outbuf; +} + +static int hvc_chars_in_buffer(struct tty_struct *tty) +{ + struct hvc_struct *hp = tty->driver_data; + + if (!hp) + return 0; + return hp->n_outbuf; +} + +/* + * timeout will vary between the MIN and MAX values defined here. By default + * and during console activity we will use a default MIN_TIMEOUT of 10. When + * the console is idle, we increase the timeout value on each pass through + * msleep until we reach the max. This may be noticeable as a brief (average + * one second) delay on the console before the console responds to input when + * there has been no input for some time. + */ +#define MIN_TIMEOUT (10) +#define MAX_TIMEOUT (2000) +static u32 timeout = MIN_TIMEOUT; + +#define HVC_POLL_READ 0x00000001 +#define HVC_POLL_WRITE 0x00000002 + +int hvc_poll(struct hvc_struct *hp) +{ + struct tty_struct *tty; + int i, n, poll_mask = 0; + char buf[N_INBUF] __ALIGNED__; + unsigned long flags; + int read_total = 0; + int written_total = 0; + + spin_lock_irqsave(&hp->lock, flags); + + /* Push pending writes */ + if (hp->n_outbuf > 0) + written_total = hvc_push(hp); + + /* Reschedule us if still some write pending */ + if (hp->n_outbuf > 0) { + poll_mask |= HVC_POLL_WRITE; + /* If hvc_push() was not able to write, sleep a few msecs */ + timeout = (written_total) ? 0 : MIN_TIMEOUT; + } + + /* No tty attached, just skip */ + tty = tty_kref_get(hp->tty); + if (tty == NULL) + goto bail; + + /* Now check if we can get data (are we throttled ?) */ + if (test_bit(TTY_THROTTLED, &tty->flags)) + goto throttled; + + /* If we aren't notifier driven and aren't throttled, we always + * request a reschedule + */ + if (!hp->irq_requested) + poll_mask |= HVC_POLL_READ; + + /* Read data if any */ + for (;;) { + int count = tty_buffer_request_room(tty, N_INBUF); + + /* If flip is full, just reschedule a later read */ + if (count == 0) { + poll_mask |= HVC_POLL_READ; + break; + } + + n = hp->ops->get_chars(hp->vtermno, buf, count); + if (n <= 0) { + /* Hangup the tty when disconnected from host */ + if (n == -EPIPE) { + spin_unlock_irqrestore(&hp->lock, flags); + tty_hangup(tty); + spin_lock_irqsave(&hp->lock, flags); + } else if ( n == -EAGAIN ) { + /* + * Some back-ends can only ensure a certain min + * num of bytes read, which may be > 'count'. + * Let the tty clear the flip buff to make room. + */ + poll_mask |= HVC_POLL_READ; + } + break; + } + for (i = 0; i < n; ++i) { +#ifdef CONFIG_MAGIC_SYSRQ + if (hp->index == hvc_console.index) { + /* Handle the SysRq Hack */ + /* XXX should support a sequence */ + if (buf[i] == '\x0f') { /* ^O */ + /* if ^O is pressed again, reset + * sysrq_pressed and flip ^O char */ + sysrq_pressed = !sysrq_pressed; + if (sysrq_pressed) + continue; + } else if (sysrq_pressed) { + handle_sysrq(buf[i]); + sysrq_pressed = 0; + continue; + } + } +#endif /* CONFIG_MAGIC_SYSRQ */ + tty_insert_flip_char(tty, buf[i], 0); + } + + read_total += n; + } + throttled: + /* Wakeup write queue if necessary */ + if (hp->do_wakeup) { + hp->do_wakeup = 0; + tty_wakeup(tty); + } + bail: + spin_unlock_irqrestore(&hp->lock, flags); + + if (read_total) { + /* Activity is occurring, so reset the polling backoff value to + a minimum for performance. */ + timeout = MIN_TIMEOUT; + + tty_flip_buffer_push(tty); + } + if (tty) + tty_kref_put(tty); + + return poll_mask; +} +EXPORT_SYMBOL_GPL(hvc_poll); + +/** + * __hvc_resize() - Update terminal window size information. + * @hp: HVC console pointer + * @ws: Terminal window size structure + * + * Stores the specified window size information in the hvc structure of @hp. + * The function schedule the tty resize update. + * + * Locking: Locking free; the function MUST be called holding hp->lock + */ +void __hvc_resize(struct hvc_struct *hp, struct winsize ws) +{ + hp->ws = ws; + schedule_work(&hp->tty_resize); +} +EXPORT_SYMBOL_GPL(__hvc_resize); + +/* + * This kthread is either polling or interrupt driven. This is determined by + * calling hvc_poll() who determines whether a console adapter support + * interrupts. + */ +static int khvcd(void *unused) +{ + int poll_mask; + struct hvc_struct *hp; + + set_freezable(); + do { + poll_mask = 0; + hvc_kicked = 0; + try_to_freeze(); + wmb(); + if (!cpus_are_in_xmon()) { + spin_lock(&hvc_structs_lock); + list_for_each_entry(hp, &hvc_structs, next) { + poll_mask |= hvc_poll(hp); + } + spin_unlock(&hvc_structs_lock); + } else + poll_mask |= HVC_POLL_READ; + if (hvc_kicked) + continue; + set_current_state(TASK_INTERRUPTIBLE); + if (!hvc_kicked) { + if (poll_mask == 0) + schedule(); + else { + if (timeout < MAX_TIMEOUT) + timeout += (timeout >> 6) + 1; + + msleep_interruptible(timeout); + } + } + __set_current_state(TASK_RUNNING); + } while (!kthread_should_stop()); + + return 0; +} + +static const struct tty_operations hvc_ops = { + .open = hvc_open, + .close = hvc_close, + .write = hvc_write, + .hangup = hvc_hangup, + .unthrottle = hvc_unthrottle, + .write_room = hvc_write_room, + .chars_in_buffer = hvc_chars_in_buffer, +}; + +struct hvc_struct *hvc_alloc(uint32_t vtermno, int data, + const struct hv_ops *ops, + int outbuf_size) +{ + struct hvc_struct *hp; + int i; + + /* We wait until a driver actually comes along */ + if (!hvc_driver) { + int err = hvc_init(); + if (err) + return ERR_PTR(err); + } + + hp = kzalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size, + GFP_KERNEL); + if (!hp) + return ERR_PTR(-ENOMEM); + + hp->vtermno = vtermno; + hp->data = data; + hp->ops = ops; + hp->outbuf_size = outbuf_size; + hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))]; + + kref_init(&hp->kref); + + INIT_WORK(&hp->tty_resize, hvc_set_winsz); + spin_lock_init(&hp->lock); + spin_lock(&hvc_structs_lock); + + /* + * find index to use: + * see if this vterm id matches one registered for console. + */ + for (i=0; i < MAX_NR_HVC_CONSOLES; i++) + if (vtermnos[i] == hp->vtermno && + cons_ops[i] == hp->ops) + break; + + /* no matching slot, just use a counter */ + if (i >= MAX_NR_HVC_CONSOLES) + i = ++last_hvc; + + hp->index = i; + + list_add_tail(&(hp->next), &hvc_structs); + spin_unlock(&hvc_structs_lock); + + return hp; +} +EXPORT_SYMBOL_GPL(hvc_alloc); + +int hvc_remove(struct hvc_struct *hp) +{ + unsigned long flags; + struct tty_struct *tty; + + spin_lock_irqsave(&hp->lock, flags); + tty = tty_kref_get(hp->tty); + + if (hp->index < MAX_NR_HVC_CONSOLES) + vtermnos[hp->index] = -1; + + /* Don't whack hp->irq because tty_hangup() will need to free the irq. */ + + spin_unlock_irqrestore(&hp->lock, flags); + + /* + * We 'put' the instance that was grabbed when the kref instance + * was initialized using kref_init(). Let the last holder of this + * kref cause it to be removed, which will probably be the tty_vhangup + * below. + */ + kref_put(&hp->kref, destroy_hvc_struct); + + /* + * This function call will auto chain call hvc_hangup. + */ + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + return 0; +} +EXPORT_SYMBOL_GPL(hvc_remove); + +/* Driver initialization: called as soon as someone uses hvc_alloc(). */ +static int hvc_init(void) +{ + struct tty_driver *drv; + int err; + + /* We need more than hvc_count adapters due to hotplug additions. */ + drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS); + if (!drv) { + err = -ENOMEM; + goto out; + } + + drv->owner = THIS_MODULE; + drv->driver_name = "hvc"; + drv->name = "hvc"; + drv->major = HVC_MAJOR; + drv->minor_start = HVC_MINOR; + drv->type = TTY_DRIVER_TYPE_SYSTEM; + drv->init_termios = tty_std_termios; + drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + tty_set_operations(drv, &hvc_ops); + + /* Always start the kthread because there can be hotplug vty adapters + * added later. */ + hvc_task = kthread_run(khvcd, NULL, "khvcd"); + if (IS_ERR(hvc_task)) { + printk(KERN_ERR "Couldn't create kthread for console.\n"); + err = PTR_ERR(hvc_task); + goto put_tty; + } + + err = tty_register_driver(drv); + if (err) { + printk(KERN_ERR "Couldn't register hvc console driver\n"); + goto stop_thread; + } + + /* + * Make sure tty is fully registered before allowing it to be + * found by hvc_console_device. + */ + smp_mb(); + hvc_driver = drv; + return 0; + +stop_thread: + kthread_stop(hvc_task); + hvc_task = NULL; +put_tty: + put_tty_driver(drv); +out: + return err; +} + +/* This isn't particularly necessary due to this being a console driver + * but it is nice to be thorough. + */ +static void __exit hvc_exit(void) +{ + if (hvc_driver) { + kthread_stop(hvc_task); + + tty_unregister_driver(hvc_driver); + /* return tty_struct instances allocated in hvc_init(). */ + put_tty_driver(hvc_driver); + unregister_console(&hvc_console); + } +} +module_exit(hvc_exit); diff --git a/drivers/tty/hvc/hvc_console.h b/drivers/tty/hvc/hvc_console.h new file mode 100644 index 000000000000..54381eba4e4a --- /dev/null +++ b/drivers/tty/hvc/hvc_console.h @@ -0,0 +1,119 @@ +/* + * hvc_console.h + * Copyright (C) 2005 IBM Corporation + * + * Author(s): + * Ryan S. Arnold + * + * hvc_console header information: + * moved here from arch/powerpc/include/asm/hvconsole.h + * and drivers/char/hvc_console.c + * + * 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 HVC_CONSOLE_H +#define HVC_CONSOLE_H +#include +#include +#include + +/* + * This is the max number of console adapters that can/will be found as + * console devices on first stage console init. Any number beyond this range + * can't be used as a console device but is still a valid tty device. + */ +#define MAX_NR_HVC_CONSOLES 16 + +/* + * The Linux TTY code does not support dynamic addition of tty derived devices + * so we need to know how many tty devices we might need when space is allocated + * for the tty device. Since this driver supports hotplug of vty adapters we + * need to make sure we have enough allocated. + */ +#define HVC_ALLOC_TTY_ADAPTERS 8 + +struct hvc_struct { + spinlock_t lock; + int index; + struct tty_struct *tty; + int count; + int do_wakeup; + char *outbuf; + int outbuf_size; + int n_outbuf; + uint32_t vtermno; + const struct hv_ops *ops; + int irq_requested; + int data; + struct winsize ws; + struct work_struct tty_resize; + struct list_head next; + struct kref kref; /* ref count & hvc_struct lifetime */ +}; + +/* implemented by a low level driver */ +struct hv_ops { + int (*get_chars)(uint32_t vtermno, char *buf, int count); + int (*put_chars)(uint32_t vtermno, const char *buf, int count); + + /* Callbacks for notification. Called in open, close and hangup */ + int (*notifier_add)(struct hvc_struct *hp, int irq); + void (*notifier_del)(struct hvc_struct *hp, int irq); + void (*notifier_hangup)(struct hvc_struct *hp, int irq); +}; + +/* Register a vterm and a slot index for use as a console (console_init) */ +extern int hvc_instantiate(uint32_t vtermno, int index, + const struct hv_ops *ops); + +/* register a vterm for hvc tty operation (module_init or hotplug add) */ +extern struct hvc_struct * hvc_alloc(uint32_t vtermno, int data, + const struct hv_ops *ops, int outbuf_size); +/* remove a vterm from hvc tty operation (module_exit or hotplug remove) */ +extern int hvc_remove(struct hvc_struct *hp); + +/* data available */ +int hvc_poll(struct hvc_struct *hp); +void hvc_kick(void); + +/* Resize hvc tty terminal window */ +extern void __hvc_resize(struct hvc_struct *hp, struct winsize ws); + +static inline void hvc_resize(struct hvc_struct *hp, struct winsize ws) +{ + unsigned long flags; + + spin_lock_irqsave(&hp->lock, flags); + __hvc_resize(hp, ws); + spin_unlock_irqrestore(&hp->lock, flags); +} + +/* default notifier for irq based notification */ +extern int notifier_add_irq(struct hvc_struct *hp, int data); +extern void notifier_del_irq(struct hvc_struct *hp, int data); +extern void notifier_hangup_irq(struct hvc_struct *hp, int data); + + +#if defined(CONFIG_XMON) && defined(CONFIG_SMP) +#include +#else +static inline int cpus_are_in_xmon(void) +{ + return 0; +} +#endif + +#endif // HVC_CONSOLE_H diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c new file mode 100644 index 000000000000..6470f63deb4b --- /dev/null +++ b/drivers/tty/hvc/hvc_dcc.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "hvc_console.h" + +/* DCC Status Bits */ +#define DCC_STATUS_RX (1 << 30) +#define DCC_STATUS_TX (1 << 29) + +static inline u32 __dcc_getstatus(void) +{ + u32 __ret; + + asm("mrc p14, 0, %0, c0, c1, 0 @ read comms ctrl reg" + : "=r" (__ret) : : "cc"); + + return __ret; +} + + +#if defined(CONFIG_CPU_V7) +static inline char __dcc_getchar(void) +{ + char __c; + + asm("get_wait: mrc p14, 0, pc, c0, c1, 0 \n\ + bne get_wait \n\ + mrc p14, 0, %0, c0, c5, 0 @ read comms data reg" + : "=r" (__c) : : "cc"); + + return __c; +} +#else +static inline char __dcc_getchar(void) +{ + char __c; + + asm("mrc p14, 0, %0, c0, c5, 0 @ read comms data reg" + : "=r" (__c)); + + return __c; +} +#endif + +#if defined(CONFIG_CPU_V7) +static inline void __dcc_putchar(char c) +{ + asm("put_wait: mrc p14, 0, pc, c0, c1, 0 \n\ + bcs put_wait \n\ + mcr p14, 0, %0, c0, c5, 0 " + : : "r" (c) : "cc"); +} +#else +static inline void __dcc_putchar(char c) +{ + asm("mcr p14, 0, %0, c0, c5, 0 @ write a char" + : /* no output register */ + : "r" (c)); +} +#endif + +static int hvc_dcc_put_chars(uint32_t vt, const char *buf, int count) +{ + int i; + + for (i = 0; i < count; i++) { + while (__dcc_getstatus() & DCC_STATUS_TX) + cpu_relax(); + + __dcc_putchar((char)(buf[i] & 0xFF)); + } + + return count; +} + +static int hvc_dcc_get_chars(uint32_t vt, char *buf, int count) +{ + int i; + + for (i = 0; i < count; ++i) { + int c = -1; + + if (__dcc_getstatus() & DCC_STATUS_RX) + c = __dcc_getchar(); + if (c < 0) + break; + buf[i] = c; + } + + return i; +} + +static const struct hv_ops hvc_dcc_get_put_ops = { + .get_chars = hvc_dcc_get_chars, + .put_chars = hvc_dcc_put_chars, +}; + +static int __init hvc_dcc_console_init(void) +{ + hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); + return 0; +} +console_initcall(hvc_dcc_console_init); + +static int __init hvc_dcc_init(void) +{ + hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); + return 0; +} +device_initcall(hvc_dcc_init); diff --git a/drivers/tty/hvc/hvc_irq.c b/drivers/tty/hvc/hvc_irq.c new file mode 100644 index 000000000000..2623e177e8d6 --- /dev/null +++ b/drivers/tty/hvc/hvc_irq.c @@ -0,0 +1,49 @@ +/* + * Copyright IBM Corp. 2001,2008 + * + * This file contains the IRQ specific code for hvc_console + * + */ + +#include + +#include "hvc_console.h" + +static irqreturn_t hvc_handle_interrupt(int irq, void *dev_instance) +{ + /* if hvc_poll request a repoll, then kick the hvcd thread */ + if (hvc_poll(dev_instance)) + hvc_kick(); + return IRQ_HANDLED; +} + +/* + * For IRQ based systems these callbacks can be used + */ +int notifier_add_irq(struct hvc_struct *hp, int irq) +{ + int rc; + + if (!irq) { + hp->irq_requested = 0; + return 0; + } + rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, + "hvc_console", hp); + if (!rc) + hp->irq_requested = 1; + return rc; +} + +void notifier_del_irq(struct hvc_struct *hp, int irq) +{ + if (!hp->irq_requested) + return; + free_irq(irq, hp); + hp->irq_requested = 0; +} + +void notifier_hangup_irq(struct hvc_struct *hp, int irq) +{ + notifier_del_irq(hp, irq); +} diff --git a/drivers/tty/hvc/hvc_iseries.c b/drivers/tty/hvc/hvc_iseries.c new file mode 100644 index 000000000000..21c54955084e --- /dev/null +++ b/drivers/tty/hvc/hvc_iseries.c @@ -0,0 +1,598 @@ +/* + * iSeries vio driver interface to hvc_console.c + * + * This code is based heavily on hvc_vio.c and viocons.c + * + * Copyright (C) 2006 Stephen Rothwell, IBM Corporation + * + * 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 +#include +#include +#include +#include + +#include "hvc_console.h" + +#define VTTY_PORTS 10 + +static DEFINE_SPINLOCK(consolelock); +static DEFINE_SPINLOCK(consoleloglock); + +static const char hvc_driver_name[] = "hvc_console"; + +#define IN_BUF_SIZE 200 + +/* + * Our port information. + */ +static struct port_info { + HvLpIndex lp; + u64 seq; /* sequence number of last HV send */ + u64 ack; /* last ack from HV */ + struct hvc_struct *hp; + int in_start; + int in_end; + unsigned char in_buf[IN_BUF_SIZE]; +} port_info[VTTY_PORTS] = { + [ 0 ... VTTY_PORTS - 1 ] = { + .lp = HvLpIndexInvalid + } +}; + +#define viochar_is_console(pi) ((pi) == &port_info[0]) + +static struct vio_device_id hvc_driver_table[] __devinitdata = { + {"serial", "IBM,iSeries-vty"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, hvc_driver_table); + +static void hvlog(char *fmt, ...) +{ + int i; + unsigned long flags; + va_list args; + static char buf[256]; + + spin_lock_irqsave(&consoleloglock, flags); + va_start(args, fmt); + i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); + va_end(args); + buf[i++] = '\r'; + HvCall_writeLogBuffer(buf, i); + spin_unlock_irqrestore(&consoleloglock, flags); +} + +/* + * Initialize the common fields in a charLpEvent + */ +static void init_data_event(struct viocharlpevent *viochar, HvLpIndex lp) +{ + struct HvLpEvent *hev = &viochar->event; + + memset(viochar, 0, sizeof(struct viocharlpevent)); + + hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DEFERRED_ACK | + HV_LP_EVENT_INT; + hev->xType = HvLpEvent_Type_VirtualIo; + hev->xSubtype = viomajorsubtype_chario | viochardata; + hev->xSourceLp = HvLpConfig_getLpIndex(); + hev->xTargetLp = lp; + hev->xSizeMinus1 = sizeof(struct viocharlpevent); + hev->xSourceInstanceId = viopath_sourceinst(lp); + hev->xTargetInstanceId = viopath_targetinst(lp); +} + +static int get_chars(uint32_t vtermno, char *buf, int count) +{ + struct port_info *pi; + int n = 0; + unsigned long flags; + + if (vtermno >= VTTY_PORTS) + return -EINVAL; + if (count == 0) + return 0; + + pi = &port_info[vtermno]; + spin_lock_irqsave(&consolelock, flags); + + if (pi->in_end == 0) + goto done; + + n = pi->in_end - pi->in_start; + if (n > count) + n = count; + memcpy(buf, &pi->in_buf[pi->in_start], n); + pi->in_start += n; + if (pi->in_start == pi->in_end) { + pi->in_start = 0; + pi->in_end = 0; + } +done: + spin_unlock_irqrestore(&consolelock, flags); + return n; +} + +static int put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct viocharlpevent *viochar; + struct port_info *pi; + HvLpEvent_Rc hvrc; + unsigned long flags; + int sent = 0; + + if (vtermno >= VTTY_PORTS) + return -EINVAL; + + pi = &port_info[vtermno]; + + spin_lock_irqsave(&consolelock, flags); + + if (viochar_is_console(pi) && !viopath_isactive(pi->lp)) { + HvCall_writeLogBuffer(buf, count); + sent = count; + goto done; + } + + viochar = vio_get_event_buffer(viomajorsubtype_chario); + if (viochar == NULL) { + hvlog("\n\rviocons: Can't get viochar buffer."); + goto done; + } + + while ((count > 0) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { + int len; + + len = (count > VIOCHAR_MAX_DATA) ? VIOCHAR_MAX_DATA : count; + + if (viochar_is_console(pi)) + HvCall_writeLogBuffer(buf, len); + + init_data_event(viochar, pi->lp); + + viochar->len = len; + viochar->event.xCorrelationToken = pi->seq++; + viochar->event.xSizeMinus1 = + offsetof(struct viocharlpevent, data) + len; + + memcpy(viochar->data, buf, len); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) + hvlog("\n\rerror sending event! return code %d\n\r", + (int)hvrc); + sent += len; + count -= len; + buf += len; + } + + vio_free_event_buffer(viomajorsubtype_chario, viochar); +done: + spin_unlock_irqrestore(&consolelock, flags); + return sent; +} + +static const struct hv_ops hvc_get_put_ops = { + .get_chars = get_chars, + .put_chars = put_chars, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int __devinit hvc_vio_probe(struct vio_dev *vdev, + const struct vio_device_id *id) +{ + struct hvc_struct *hp; + struct port_info *pi; + + /* probed with invalid parameters. */ + if (!vdev || !id) + return -EPERM; + + if (vdev->unit_address >= VTTY_PORTS) + return -ENODEV; + + pi = &port_info[vdev->unit_address]; + + hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, + VIOCHAR_MAX_DATA); + if (IS_ERR(hp)) + return PTR_ERR(hp); + pi->hp = hp; + dev_set_drvdata(&vdev->dev, pi); + + return 0; +} + +static int __devexit hvc_vio_remove(struct vio_dev *vdev) +{ + struct port_info *pi = dev_get_drvdata(&vdev->dev); + struct hvc_struct *hp = pi->hp; + + return hvc_remove(hp); +} + +static struct vio_driver hvc_vio_driver = { + .id_table = hvc_driver_table, + .probe = hvc_vio_probe, + .remove = __devexit_p(hvc_vio_remove), + .driver = { + .name = hvc_driver_name, + .owner = THIS_MODULE, + } +}; + +static void hvc_open_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtual_device; + struct port_info *pi; + int reject = 0; + + if (hvlpevent_is_ack(event)) { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, flags); + + pi = &port_info[port]; + if (event->xRc == HvLpEvent_Rc_Good) { + pi->seq = pi->ack = 0; + /* + * This line allows connections from the primary + * partition but once one is connected from the + * primary partition nothing short of a reboot + * of linux will allow access from the hosting + * partition again without a required iSeries fix. + */ + pi->lp = event->xTargetLp; + } + + spin_unlock_irqrestore(&consolelock, flags); + if (event->xRc != HvLpEvent_Rc_Good) + printk(KERN_WARNING + "hvc: handle_open_event: event->xRc == (%d).\n", + event->xRc); + + if (event->xCorrelationToken != 0) { + atomic_t *aptr= (atomic_t *)event->xCorrelationToken; + atomic_set(aptr, 1); + } else + printk(KERN_WARNING + "hvc: weird...got open ack without atomic\n"); + return; + } + + /* This had better require an ack, otherwise complain */ + if (!hvlpevent_need_ack(event)) { + printk(KERN_WARNING "hvc: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) { + event->xRc = HvLpEvent_Rc_SubtypeError; + cevent->subtype_result_code = viorc_openRejected; + /* + * Flag state here since we can't printk while holding + * the consolelock spinlock. + */ + reject = 1; + } else { + pi = &port_info[port]; + if ((pi->lp != HvLpIndexInvalid) && + (pi->lp != event->xSourceLp)) { + /* + * If this is tty is already connected to a different + * partition, fail. + */ + event->xRc = HvLpEvent_Rc_SubtypeError; + cevent->subtype_result_code = viorc_openRejected; + reject = 2; + } else { + pi->lp = event->xSourceLp; + event->xRc = HvLpEvent_Rc_Good; + cevent->subtype_result_code = viorc_good; + pi->seq = pi->ack = 0; + } + } + + spin_unlock_irqrestore(&consolelock, flags); + + if (reject == 1) + printk(KERN_WARNING "hvc: open rejected: bad virtual tty.\n"); + else if (reject == 2) + printk(KERN_WARNING "hvc: open rejected: console in exclusive " + "use by another partition.\n"); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); +} + +/* + * Handle a close charLpEvent. This should ONLY be an Interrupt because the + * virtual console should never actually issue a close event to the hypervisor + * because the virtual console never goes away. A close event coming from the + * hypervisor simply means that there are no client consoles connected to the + * virtual console. + */ +static void hvc_close_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + u8 port = cevent->virtual_device; + + if (!hvlpevent_is_int(event)) { + printk(KERN_WARNING + "hvc: got unexpected close acknowledgement\n"); + return; + } + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING + "hvc: close message from invalid virtual device.\n"); + return; + } + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, flags); + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, flags); +} + +static void hvc_data_event(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + struct port_info *pi; + int n; + u8 port = cevent->virtual_device; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING "hvc: data on invalid virtual device %d\n", + port); + return; + } + if (cevent->len == 0) + return; + + /* + * Change 05/01/2003 - Ryan Arnold: If a partition other than + * the current exclusive partition tries to send us data + * events then just drop them on the floor because we don't + * want his stinking data. He isn't authorized to receive + * data because he wasn't the first one to get the console, + * therefore he shouldn't be allowed to send data either. + * This will work without an iSeries fix. + */ + pi = &port_info[port]; + if (pi->lp != event->xSourceLp) + return; + + spin_lock_irqsave(&consolelock, flags); + + n = IN_BUF_SIZE - pi->in_end; + if (n > cevent->len) + n = cevent->len; + if (n > 0) { + memcpy(&pi->in_buf[pi->in_end], cevent->data, n); + pi->in_end += n; + } + spin_unlock_irqrestore(&consolelock, flags); + if (n == 0) + printk(KERN_WARNING "hvc: input buffer overflow\n"); +} + +static void hvc_ack_event(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + unsigned long flags; + u8 port = cevent->virtual_device; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING "hvc: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + port_info[port].ack = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, flags); +} + +static void hvc_config_event(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *)event; + + if (cevent->data[0] == 0x01) + printk(KERN_INFO "hvc: window resized to %d: %d: %d: %d\n", + cevent->data[1], cevent->data[2], + cevent->data[3], cevent->data[4]); + else + printk(KERN_WARNING "hvc: unknown config event\n"); +} + +static void hvc_handle_event(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) + return; + + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) { + case viocharopen: + hvc_open_event(event); + break; + case viocharclose: + hvc_close_event(event); + break; + case viochardata: + hvc_data_event(event); + break; + case viocharack: + hvc_ack_event(event); + break; + case viocharconfig: + hvc_config_event(event); + break; + default: + if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +static int __init send_open(HvLpIndex remoteLp, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario | viocharopen, + HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(remoteLp), + viopath_targetinst(remoteLp), + (u64)(unsigned long)sem, VIOVERSION << 16, + 0, 0, 0, 0); +} + +static int __init hvc_vio_init(void) +{ + atomic_t wait_flag; + int rc; + + if (!firmware_has_feature(FW_FEATURE_ISERIES)) + return -EIO; + + /* +2 for fudge */ + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), + viomajorsubtype_chario, VIOCHAR_WINDOW + 2); + if (rc) + printk(KERN_WARNING "hvc: error opening to primary %d\n", rc); + + if (viopath_hostLp == HvLpIndexInvalid) + vio_set_hostlp(); + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { + printk(KERN_INFO "hvc: open path to hosting (%d)\n", + viopath_hostLp); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, + VIOCHAR_WINDOW + 2); /* +2 for fudge */ + if (rc) + printk(KERN_WARNING + "error opening to partition %d: %d\n", + viopath_hostLp, rc); + } + + if (vio_setHandler(viomajorsubtype_chario, hvc_handle_event) < 0) + printk(KERN_WARNING + "hvc: error seting handler for console events!\n"); + + /* + * First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + atomic_set(&wait_flag, 0); + if ((viopath_isactive(viopath_hostLp)) && + (send_open(viopath_hostLp, &wait_flag) == 0)) { + printk(KERN_INFO "hvc: hosting partition %d\n", viopath_hostLp); + while (atomic_read(&wait_flag) == 0) + mb(); + atomic_set(&wait_flag, 0); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (send_open(HvLpConfig_getPrimaryLpIndex(), &wait_flag) == 0)) { + printk(KERN_INFO "hvc: opening console to primary partition\n"); + while (atomic_read(&wait_flag) == 0) + mb(); + } + + /* Register as a vio device to receive callbacks */ + rc = vio_register_driver(&hvc_vio_driver); + + return rc; +} +module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ + +static void __exit hvc_vio_exit(void) +{ + vio_unregister_driver(&hvc_vio_driver); +} +module_exit(hvc_vio_exit); + +/* the device tree order defines our numbering */ +static int __init hvc_find_vtys(void) +{ + struct device_node *vty; + int num_found = 0; + + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + const uint32_t *vtermno; + + /* We have statically defined space for only a certain number + * of console adapters. + */ + if ((num_found >= MAX_NR_HVC_CONSOLES) || + (num_found >= VTTY_PORTS)) { + of_node_put(vty); + break; + } + + vtermno = of_get_property(vty, "reg", NULL); + if (!vtermno) + continue; + + if (!of_device_is_compatible(vty, "IBM,iSeries-vty")) + continue; + + if (num_found == 0) + add_preferred_console("hvc", 0, NULL); + hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); + ++num_found; + } + + return num_found; +} +console_initcall(hvc_find_vtys); diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c new file mode 100644 index 000000000000..c3425bb3a1f6 --- /dev/null +++ b/drivers/tty/hvc/hvc_iucv.c @@ -0,0 +1,1337 @@ +/* + * hvc_iucv.c - z/VM IUCV hypervisor console (HVC) device driver + * + * This HVC device driver provides terminal access using + * z/VM IUCV communication paths. + * + * Copyright IBM Corp. 2008, 2009 + * + * Author(s): Hendrik Brueckner + */ +#define KMSG_COMPONENT "hvc_iucv" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hvc_console.h" + + +/* General device driver settings */ +#define HVC_IUCV_MAGIC 0xc9e4c3e5 +#define MAX_HVC_IUCV_LINES HVC_ALLOC_TTY_ADAPTERS +#define MEMPOOL_MIN_NR (PAGE_SIZE / sizeof(struct iucv_tty_buffer)/4) + +/* IUCV TTY message */ +#define MSG_VERSION 0x02 /* Message version */ +#define MSG_TYPE_ERROR 0x01 /* Error message */ +#define MSG_TYPE_TERMENV 0x02 /* Terminal environment variable */ +#define MSG_TYPE_TERMIOS 0x04 /* Terminal IO struct update */ +#define MSG_TYPE_WINSIZE 0x08 /* Terminal window size update */ +#define MSG_TYPE_DATA 0x10 /* Terminal data */ + +struct iucv_tty_msg { + u8 version; /* Message version */ + u8 type; /* Message type */ +#define MSG_MAX_DATALEN ((u16)(~0)) + u16 datalen; /* Payload length */ + u8 data[]; /* Payload buffer */ +} __attribute__((packed)); +#define MSG_SIZE(s) ((s) + offsetof(struct iucv_tty_msg, data)) + +enum iucv_state_t { + IUCV_DISCONN = 0, + IUCV_CONNECTED = 1, + IUCV_SEVERED = 2, +}; + +enum tty_state_t { + TTY_CLOSED = 0, + TTY_OPENED = 1, +}; + +struct hvc_iucv_private { + struct hvc_struct *hvc; /* HVC struct reference */ + u8 srv_name[8]; /* IUCV service name (ebcdic) */ + unsigned char is_console; /* Linux console usage flag */ + enum iucv_state_t iucv_state; /* IUCV connection status */ + enum tty_state_t tty_state; /* TTY status */ + struct iucv_path *path; /* IUCV path pointer */ + spinlock_t lock; /* hvc_iucv_private lock */ +#define SNDBUF_SIZE (PAGE_SIZE) /* must be < MSG_MAX_DATALEN */ + void *sndbuf; /* send buffer */ + size_t sndbuf_len; /* length of send buffer */ +#define QUEUE_SNDBUF_DELAY (HZ / 25) + struct delayed_work sndbuf_work; /* work: send iucv msg(s) */ + wait_queue_head_t sndbuf_waitq; /* wait for send completion */ + struct list_head tty_outqueue; /* outgoing IUCV messages */ + struct list_head tty_inqueue; /* incoming IUCV messages */ + struct device *dev; /* device structure */ +}; + +struct iucv_tty_buffer { + struct list_head list; /* list pointer */ + struct iucv_message msg; /* store an IUCV message */ + size_t offset; /* data buffer offset */ + struct iucv_tty_msg *mbuf; /* buffer to store input/output data */ +}; + +/* IUCV callback handler */ +static int hvc_iucv_path_pending(struct iucv_path *, u8[8], u8[16]); +static void hvc_iucv_path_severed(struct iucv_path *, u8[16]); +static void hvc_iucv_msg_pending(struct iucv_path *, struct iucv_message *); +static void hvc_iucv_msg_complete(struct iucv_path *, struct iucv_message *); + + +/* Kernel module parameter: use one terminal device as default */ +static unsigned long hvc_iucv_devices = 1; + +/* Array of allocated hvc iucv tty lines... */ +static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; +#define IUCV_HVC_CON_IDX (0) +/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */ +#define MAX_VMID_FILTER (500) +static size_t hvc_iucv_filter_size; +static void *hvc_iucv_filter; +static const char *hvc_iucv_filter_string; +static DEFINE_RWLOCK(hvc_iucv_filter_lock); + +/* Kmem cache and mempool for iucv_tty_buffer elements */ +static struct kmem_cache *hvc_iucv_buffer_cache; +static mempool_t *hvc_iucv_mempool; + +/* IUCV handler callback functions */ +static struct iucv_handler hvc_iucv_handler = { + .path_pending = hvc_iucv_path_pending, + .path_severed = hvc_iucv_path_severed, + .message_complete = hvc_iucv_msg_complete, + .message_pending = hvc_iucv_msg_pending, +}; + + +/** + * hvc_iucv_get_private() - Return a struct hvc_iucv_private instance. + * @num: The HVC virtual terminal number (vtermno) + * + * This function returns the struct hvc_iucv_private instance that corresponds + * to the HVC virtual terminal number specified as parameter @num. + */ +struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num) +{ + if ((num < HVC_IUCV_MAGIC) || (num - HVC_IUCV_MAGIC > hvc_iucv_devices)) + return NULL; + return hvc_iucv_table[num - HVC_IUCV_MAGIC]; +} + +/** + * alloc_tty_buffer() - Return a new struct iucv_tty_buffer element. + * @size: Size of the internal buffer used to store data. + * @flags: Memory allocation flags passed to mempool. + * + * This function allocates a new struct iucv_tty_buffer element and, optionally, + * allocates an internal data buffer with the specified size @size. + * The internal data buffer is always allocated with GFP_DMA which is + * required for receiving and sending data with IUCV. + * Note: The total message size arises from the internal buffer size and the + * members of the iucv_tty_msg structure. + * The function returns NULL if memory allocation has failed. + */ +static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags) +{ + struct iucv_tty_buffer *bufp; + + bufp = mempool_alloc(hvc_iucv_mempool, flags); + if (!bufp) + return NULL; + memset(bufp, 0, sizeof(*bufp)); + + if (size > 0) { + bufp->msg.length = MSG_SIZE(size); + bufp->mbuf = kmalloc(bufp->msg.length, flags | GFP_DMA); + if (!bufp->mbuf) { + mempool_free(bufp, hvc_iucv_mempool); + return NULL; + } + bufp->mbuf->version = MSG_VERSION; + bufp->mbuf->type = MSG_TYPE_DATA; + bufp->mbuf->datalen = (u16) size; + } + return bufp; +} + +/** + * destroy_tty_buffer() - destroy struct iucv_tty_buffer element. + * @bufp: Pointer to a struct iucv_tty_buffer element, SHALL NOT be NULL. + */ +static void destroy_tty_buffer(struct iucv_tty_buffer *bufp) +{ + kfree(bufp->mbuf); + mempool_free(bufp, hvc_iucv_mempool); +} + +/** + * destroy_tty_buffer_list() - call destroy_tty_buffer() for each list element. + * @list: List containing struct iucv_tty_buffer elements. + */ +static void destroy_tty_buffer_list(struct list_head *list) +{ + struct iucv_tty_buffer *ent, *next; + + list_for_each_entry_safe(ent, next, list, list) { + list_del(&ent->list); + destroy_tty_buffer(ent); + } +} + +/** + * hvc_iucv_write() - Receive IUCV message & write data to HVC buffer. + * @priv: Pointer to struct hvc_iucv_private + * @buf: HVC buffer for writing received terminal data. + * @count: HVC buffer size. + * @has_more_data: Pointer to an int variable. + * + * The function picks up pending messages from the input queue and receives + * the message data that is then written to the specified buffer @buf. + * If the buffer size @count is less than the data message size, the + * message is kept on the input queue and @has_more_data is set to 1. + * If all message data has been written, the message is removed from + * the input queue. + * + * The function returns the number of bytes written to the terminal, zero if + * there are no pending data messages available or if there is no established + * IUCV path. + * If the IUCV path has been severed, then -EPIPE is returned to cause a + * hang up (that is issued by the HVC layer). + */ +static int hvc_iucv_write(struct hvc_iucv_private *priv, + char *buf, int count, int *has_more_data) +{ + struct iucv_tty_buffer *rb; + int written; + int rc; + + /* immediately return if there is no IUCV connection */ + if (priv->iucv_state == IUCV_DISCONN) + return 0; + + /* if the IUCV path has been severed, return -EPIPE to inform the + * HVC layer to hang up the tty device. */ + if (priv->iucv_state == IUCV_SEVERED) + return -EPIPE; + + /* check if there are pending messages */ + if (list_empty(&priv->tty_inqueue)) + return 0; + + /* receive an iucv message and flip data to the tty (ldisc) */ + rb = list_first_entry(&priv->tty_inqueue, struct iucv_tty_buffer, list); + + written = 0; + if (!rb->mbuf) { /* message not yet received ... */ + /* allocate mem to store msg data; if no memory is available + * then leave the buffer on the list and re-try later */ + rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA); + if (!rb->mbuf) + return -ENOMEM; + + rc = __iucv_message_receive(priv->path, &rb->msg, 0, + rb->mbuf, rb->msg.length, NULL); + switch (rc) { + case 0: /* Successful */ + break; + case 2: /* No message found */ + case 9: /* Message purged */ + break; + default: + written = -EIO; + } + /* remove buffer if an error has occured or received data + * is not correct */ + if (rc || (rb->mbuf->version != MSG_VERSION) || + (rb->msg.length != MSG_SIZE(rb->mbuf->datalen))) + goto out_remove_buffer; + } + + switch (rb->mbuf->type) { + case MSG_TYPE_DATA: + written = min_t(int, rb->mbuf->datalen - rb->offset, count); + memcpy(buf, rb->mbuf->data + rb->offset, written); + if (written < (rb->mbuf->datalen - rb->offset)) { + rb->offset += written; + *has_more_data = 1; + goto out_written; + } + break; + + case MSG_TYPE_WINSIZE: + if (rb->mbuf->datalen != sizeof(struct winsize)) + break; + /* The caller must ensure that the hvc is locked, which + * is the case when called from hvc_iucv_get_chars() */ + __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); + break; + + case MSG_TYPE_ERROR: /* ignored ... */ + case MSG_TYPE_TERMENV: /* ignored ... */ + case MSG_TYPE_TERMIOS: /* ignored ... */ + break; + } + +out_remove_buffer: + list_del(&rb->list); + destroy_tty_buffer(rb); + *has_more_data = !list_empty(&priv->tty_inqueue); + +out_written: + return written; +} + +/** + * hvc_iucv_get_chars() - HVC get_chars operation. + * @vtermno: HVC virtual terminal number. + * @buf: Pointer to a buffer to store data + * @count: Size of buffer available for writing + * + * The HVC thread calls this method to read characters from the back-end. + * If an IUCV communication path has been established, pending IUCV messages + * are received and data is copied into buffer @buf up to @count bytes. + * + * Locking: The routine gets called under an irqsave() spinlock; and + * the routine locks the struct hvc_iucv_private->lock to call + * helper functions. + */ +static int hvc_iucv_get_chars(uint32_t vtermno, char *buf, int count) +{ + struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); + int written; + int has_more_data; + + if (count <= 0) + return 0; + + if (!priv) + return -ENODEV; + + spin_lock(&priv->lock); + has_more_data = 0; + written = hvc_iucv_write(priv, buf, count, &has_more_data); + spin_unlock(&priv->lock); + + /* if there are still messages on the queue... schedule another run */ + if (has_more_data) + hvc_kick(); + + return written; +} + +/** + * hvc_iucv_queue() - Buffer terminal data for sending. + * @priv: Pointer to struct hvc_iucv_private instance. + * @buf: Buffer containing data to send. + * @count: Size of buffer and amount of data to send. + * + * The function queues data for sending. To actually send the buffered data, + * a work queue function is scheduled (with QUEUE_SNDBUF_DELAY). + * The function returns the number of data bytes that has been buffered. + * + * If the device is not connected, data is ignored and the function returns + * @count. + * If the buffer is full, the function returns 0. + * If an existing IUCV communicaton path has been severed, -EPIPE is returned + * (that can be passed to HVC layer to cause a tty hangup). + */ +static int hvc_iucv_queue(struct hvc_iucv_private *priv, const char *buf, + int count) +{ + size_t len; + + if (priv->iucv_state == IUCV_DISCONN) + return count; /* ignore data */ + + if (priv->iucv_state == IUCV_SEVERED) + return -EPIPE; + + len = min_t(size_t, count, SNDBUF_SIZE - priv->sndbuf_len); + if (!len) + return 0; + + memcpy(priv->sndbuf + priv->sndbuf_len, buf, len); + priv->sndbuf_len += len; + + if (priv->iucv_state == IUCV_CONNECTED) + schedule_delayed_work(&priv->sndbuf_work, QUEUE_SNDBUF_DELAY); + + return len; +} + +/** + * hvc_iucv_send() - Send an IUCV message containing terminal data. + * @priv: Pointer to struct hvc_iucv_private instance. + * + * If an IUCV communication path has been established, the buffered output data + * is sent via an IUCV message and the number of bytes sent is returned. + * Returns 0 if there is no established IUCV communication path or + * -EPIPE if an existing IUCV communicaton path has been severed. + */ +static int hvc_iucv_send(struct hvc_iucv_private *priv) +{ + struct iucv_tty_buffer *sb; + int rc, len; + + if (priv->iucv_state == IUCV_SEVERED) + return -EPIPE; + + if (priv->iucv_state == IUCV_DISCONN) + return -EIO; + + if (!priv->sndbuf_len) + return 0; + + /* allocate internal buffer to store msg data and also compute total + * message length */ + sb = alloc_tty_buffer(priv->sndbuf_len, GFP_ATOMIC); + if (!sb) + return -ENOMEM; + + memcpy(sb->mbuf->data, priv->sndbuf, priv->sndbuf_len); + sb->mbuf->datalen = (u16) priv->sndbuf_len; + sb->msg.length = MSG_SIZE(sb->mbuf->datalen); + + list_add_tail(&sb->list, &priv->tty_outqueue); + + rc = __iucv_message_send(priv->path, &sb->msg, 0, 0, + (void *) sb->mbuf, sb->msg.length); + if (rc) { + /* drop the message here; however we might want to handle + * 0x03 (msg limit reached) by trying again... */ + list_del(&sb->list); + destroy_tty_buffer(sb); + } + len = priv->sndbuf_len; + priv->sndbuf_len = 0; + + return len; +} + +/** + * hvc_iucv_sndbuf_work() - Send buffered data over IUCV + * @work: Work structure. + * + * This work queue function sends buffered output data over IUCV and, + * if not all buffered data could be sent, reschedules itself. + */ +static void hvc_iucv_sndbuf_work(struct work_struct *work) +{ + struct hvc_iucv_private *priv; + + priv = container_of(work, struct hvc_iucv_private, sndbuf_work.work); + if (!priv) + return; + + spin_lock_bh(&priv->lock); + hvc_iucv_send(priv); + spin_unlock_bh(&priv->lock); +} + +/** + * hvc_iucv_put_chars() - HVC put_chars operation. + * @vtermno: HVC virtual terminal number. + * @buf: Pointer to an buffer to read data from + * @count: Size of buffer available for reading + * + * The HVC thread calls this method to write characters to the back-end. + * The function calls hvc_iucv_queue() to queue terminal data for sending. + * + * Locking: The method gets called under an irqsave() spinlock; and + * locks struct hvc_iucv_private->lock. + */ +static int hvc_iucv_put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct hvc_iucv_private *priv = hvc_iucv_get_private(vtermno); + int queued; + + if (count <= 0) + return 0; + + if (!priv) + return -ENODEV; + + spin_lock(&priv->lock); + queued = hvc_iucv_queue(priv, buf, count); + spin_unlock(&priv->lock); + + return queued; +} + +/** + * hvc_iucv_notifier_add() - HVC notifier for opening a TTY for the first time. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): the index of an struct + * hvc_iucv_private instance. + * + * The function sets the tty state to TTY_OPENED for the struct hvc_iucv_private + * instance that is derived from @id. Always returns 0. + * + * Locking: struct hvc_iucv_private->lock, spin_lock_bh + */ +static int hvc_iucv_notifier_add(struct hvc_struct *hp, int id) +{ + struct hvc_iucv_private *priv; + + priv = hvc_iucv_get_private(id); + if (!priv) + return 0; + + spin_lock_bh(&priv->lock); + priv->tty_state = TTY_OPENED; + spin_unlock_bh(&priv->lock); + + return 0; +} + +/** + * hvc_iucv_cleanup() - Clean up and reset a z/VM IUCV HVC instance. + * @priv: Pointer to the struct hvc_iucv_private instance. + */ +static void hvc_iucv_cleanup(struct hvc_iucv_private *priv) +{ + destroy_tty_buffer_list(&priv->tty_outqueue); + destroy_tty_buffer_list(&priv->tty_inqueue); + + priv->tty_state = TTY_CLOSED; + priv->iucv_state = IUCV_DISCONN; + + priv->sndbuf_len = 0; +} + +/** + * tty_outqueue_empty() - Test if the tty outq is empty + * @priv: Pointer to struct hvc_iucv_private instance. + */ +static inline int tty_outqueue_empty(struct hvc_iucv_private *priv) +{ + int rc; + + spin_lock_bh(&priv->lock); + rc = list_empty(&priv->tty_outqueue); + spin_unlock_bh(&priv->lock); + + return rc; +} + +/** + * flush_sndbuf_sync() - Flush send buffer and wait for completion + * @priv: Pointer to struct hvc_iucv_private instance. + * + * The routine cancels a pending sndbuf work, calls hvc_iucv_send() + * to flush any buffered terminal output data and waits for completion. + */ +static void flush_sndbuf_sync(struct hvc_iucv_private *priv) +{ + int sync_wait; + + cancel_delayed_work_sync(&priv->sndbuf_work); + + spin_lock_bh(&priv->lock); + hvc_iucv_send(priv); /* force sending buffered data */ + sync_wait = !list_empty(&priv->tty_outqueue); /* anything queued ? */ + spin_unlock_bh(&priv->lock); + + if (sync_wait) + wait_event_timeout(priv->sndbuf_waitq, + tty_outqueue_empty(priv), HZ/10); +} + +/** + * hvc_iucv_hangup() - Sever IUCV path and schedule hvc tty hang up + * @priv: Pointer to hvc_iucv_private structure + * + * This routine severs an existing IUCV communication path and hangs + * up the underlying HVC terminal device. + * The hang-up occurs only if an IUCV communication path is established; + * otherwise there is no need to hang up the terminal device. + * + * The IUCV HVC hang-up is separated into two steps: + * 1. After the IUCV path has been severed, the iucv_state is set to + * IUCV_SEVERED. + * 2. Later, when the HVC thread calls hvc_iucv_get_chars(), the + * IUCV_SEVERED state causes the tty hang-up in the HVC layer. + * + * If the tty has not yet been opened, clean up the hvc_iucv_private + * structure to allow re-connects. + * If the tty has been opened, let get_chars() return -EPIPE to signal + * the HVC layer to hang up the tty and, if so, wake up the HVC thread + * to call get_chars()... + * + * Special notes on hanging up a HVC terminal instantiated as console: + * Hang-up: 1. do_tty_hangup() replaces file ops (= hung_up_tty_fops) + * 2. do_tty_hangup() calls tty->ops->close() for console_filp + * => no hangup notifier is called by HVC (default) + * 2. hvc_close() returns because of tty_hung_up_p(filp) + * => no delete notifier is called! + * Finally, the back-end is not being notified, thus, the tty session is + * kept active (TTY_OPEN) to be ready for re-connects. + * + * Locking: spin_lock(&priv->lock) w/o disabling bh + */ +static void hvc_iucv_hangup(struct hvc_iucv_private *priv) +{ + struct iucv_path *path; + + path = NULL; + spin_lock(&priv->lock); + if (priv->iucv_state == IUCV_CONNECTED) { + path = priv->path; + priv->path = NULL; + priv->iucv_state = IUCV_SEVERED; + if (priv->tty_state == TTY_CLOSED) + hvc_iucv_cleanup(priv); + else + /* console is special (see above) */ + if (priv->is_console) { + hvc_iucv_cleanup(priv); + priv->tty_state = TTY_OPENED; + } else + hvc_kick(); + } + spin_unlock(&priv->lock); + + /* finally sever path (outside of priv->lock due to lock ordering) */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** + * hvc_iucv_notifier_hangup() - HVC notifier for TTY hangups. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): + * the index of an struct hvc_iucv_private instance. + * + * This routine notifies the HVC back-end that a tty hangup (carrier loss, + * virtual or otherwise) has occured. + * The z/VM IUCV HVC device driver ignores virtual hangups (vhangup()) + * to keep an existing IUCV communication path established. + * (Background: vhangup() is called from user space (by getty or login) to + * disable writing to the tty by other applications). + * If the tty has been opened and an established IUCV path has been severed + * (we caused the tty hangup), the function calls hvc_iucv_cleanup(). + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_notifier_hangup(struct hvc_struct *hp, int id) +{ + struct hvc_iucv_private *priv; + + priv = hvc_iucv_get_private(id); + if (!priv) + return; + + flush_sndbuf_sync(priv); + + spin_lock_bh(&priv->lock); + /* NOTE: If the hangup was scheduled by ourself (from the iucv + * path_servered callback [IUCV_SEVERED]), we have to clean up + * our structure and to set state to TTY_CLOSED. + * If the tty was hung up otherwise (e.g. vhangup()), then we + * ignore this hangup and keep an established IUCV path open... + * (...the reason is that we are not able to connect back to the + * client if we disconnect on hang up) */ + priv->tty_state = TTY_CLOSED; + + if (priv->iucv_state == IUCV_SEVERED) + hvc_iucv_cleanup(priv); + spin_unlock_bh(&priv->lock); +} + +/** + * hvc_iucv_notifier_del() - HVC notifier for closing a TTY for the last time. + * @hp: Pointer to the HVC device (struct hvc_struct) + * @id: Additional data (originally passed to hvc_alloc): + * the index of an struct hvc_iucv_private instance. + * + * This routine notifies the HVC back-end that the last tty device fd has been + * closed. The function calls hvc_iucv_cleanup() to clean up the struct + * hvc_iucv_private instance. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id) +{ + struct hvc_iucv_private *priv; + struct iucv_path *path; + + priv = hvc_iucv_get_private(id); + if (!priv) + return; + + flush_sndbuf_sync(priv); + + spin_lock_bh(&priv->lock); + path = priv->path; /* save reference to IUCV path */ + priv->path = NULL; + hvc_iucv_cleanup(priv); + spin_unlock_bh(&priv->lock); + + /* sever IUCV path outside of priv->lock due to lock ordering of: + * priv->lock <--> iucv_table_lock */ + if (path) { + iucv_path_sever(path, NULL); + iucv_path_free(path); + } +} + +/** + * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID + * @ipvmid: Originating z/VM user ID (right padded with blanks) + * + * Returns 0 if the z/VM user ID @ipvmid is allowed to connection, otherwise + * non-zero. + */ +static int hvc_iucv_filter_connreq(u8 ipvmid[8]) +{ + size_t i; + + /* Note: default policy is ACCEPT if no filter is set */ + if (!hvc_iucv_filter_size) + return 0; + + for (i = 0; i < hvc_iucv_filter_size; i++) + if (0 == memcmp(ipvmid, hvc_iucv_filter + (8 * i), 8)) + return 0; + return 1; +} + +/** + * hvc_iucv_path_pending() - IUCV handler to process a connection request. + * @path: Pending path (struct iucv_path) + * @ipvmid: z/VM system identifier of originator + * @ipuser: User specified data for this path + * (AF_IUCV: port/service name and originator port) + * + * The function uses the @ipuser data to determine if the pending path belongs + * to a terminal managed by this device driver. + * If the path belongs to this driver, ensure that the terminal is not accessed + * multiple times (only one connection to a terminal is allowed). + * If the terminal is not yet connected, the pending path is accepted and is + * associated to the appropriate struct hvc_iucv_private instance. + * + * Returns 0 if @path belongs to a terminal managed by the this device driver; + * otherwise returns -ENODEV in order to dispatch this path to other handlers. + * + * Locking: struct hvc_iucv_private->lock + */ +static int hvc_iucv_path_pending(struct iucv_path *path, + u8 ipvmid[8], u8 ipuser[16]) +{ + struct hvc_iucv_private *priv; + u8 nuser_data[16]; + u8 vm_user_id[9]; + int i, rc; + + priv = NULL; + for (i = 0; i < hvc_iucv_devices; i++) + if (hvc_iucv_table[i] && + (0 == memcmp(hvc_iucv_table[i]->srv_name, ipuser, 8))) { + priv = hvc_iucv_table[i]; + break; + } + if (!priv) + return -ENODEV; + + /* Enforce that ipvmid is allowed to connect to us */ + read_lock(&hvc_iucv_filter_lock); + rc = hvc_iucv_filter_connreq(ipvmid); + read_unlock(&hvc_iucv_filter_lock); + if (rc) { + iucv_path_sever(path, ipuser); + iucv_path_free(path); + memcpy(vm_user_id, ipvmid, 8); + vm_user_id[8] = 0; + pr_info("A connection request from z/VM user ID %s " + "was refused\n", vm_user_id); + return 0; + } + + spin_lock(&priv->lock); + + /* If the terminal is already connected or being severed, then sever + * this path to enforce that there is only ONE established communication + * path per terminal. */ + if (priv->iucv_state != IUCV_DISCONN) { + iucv_path_sever(path, ipuser); + iucv_path_free(path); + goto out_path_handled; + } + + /* accept path */ + memcpy(nuser_data, ipuser + 8, 8); /* remote service (for af_iucv) */ + memcpy(nuser_data + 8, ipuser, 8); /* local service (for af_iucv) */ + path->msglim = 0xffff; /* IUCV MSGLIMIT */ + path->flags &= ~IUCV_IPRMDATA; /* TODO: use IUCV_IPRMDATA */ + rc = iucv_path_accept(path, &hvc_iucv_handler, nuser_data, priv); + if (rc) { + iucv_path_sever(path, ipuser); + iucv_path_free(path); + goto out_path_handled; + } + priv->path = path; + priv->iucv_state = IUCV_CONNECTED; + + /* flush buffered output data... */ + schedule_delayed_work(&priv->sndbuf_work, 5); + +out_path_handled: + spin_unlock(&priv->lock); + return 0; +} + +/** + * hvc_iucv_path_severed() - IUCV handler to process a path sever. + * @path: Pending path (struct iucv_path) + * @ipuser: User specified data for this path + * (AF_IUCV: port/service name and originator port) + * + * This function calls the hvc_iucv_hangup() function for the + * respective IUCV HVC terminal. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_path_severed(struct iucv_path *path, u8 ipuser[16]) +{ + struct hvc_iucv_private *priv = path->private; + + hvc_iucv_hangup(priv); +} + +/** + * hvc_iucv_msg_pending() - IUCV handler to process an incoming IUCV message. + * @path: Pending path (struct iucv_path) + * @msg: Pointer to the IUCV message + * + * The function puts an incoming message on the input queue for later + * processing (by hvc_iucv_get_chars() / hvc_iucv_write()). + * If the tty has not yet been opened, the message is rejected. + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_msg_pending(struct iucv_path *path, + struct iucv_message *msg) +{ + struct hvc_iucv_private *priv = path->private; + struct iucv_tty_buffer *rb; + + /* reject messages that exceed max size of iucv_tty_msg->datalen */ + if (msg->length > MSG_SIZE(MSG_MAX_DATALEN)) { + iucv_message_reject(path, msg); + return; + } + + spin_lock(&priv->lock); + + /* reject messages if tty has not yet been opened */ + if (priv->tty_state == TTY_CLOSED) { + iucv_message_reject(path, msg); + goto unlock_return; + } + + /* allocate tty buffer to save iucv msg only */ + rb = alloc_tty_buffer(0, GFP_ATOMIC); + if (!rb) { + iucv_message_reject(path, msg); + goto unlock_return; /* -ENOMEM */ + } + rb->msg = *msg; + + list_add_tail(&rb->list, &priv->tty_inqueue); + + hvc_kick(); /* wake up hvc thread */ + +unlock_return: + spin_unlock(&priv->lock); +} + +/** + * hvc_iucv_msg_complete() - IUCV handler to process message completion + * @path: Pending path (struct iucv_path) + * @msg: Pointer to the IUCV message + * + * The function is called upon completion of message delivery to remove the + * message from the outqueue. Additional delivery information can be found + * msg->audit: rejected messages (0x040000 (IPADRJCT)), and + * purged messages (0x010000 (IPADPGNR)). + * + * Locking: struct hvc_iucv_private->lock + */ +static void hvc_iucv_msg_complete(struct iucv_path *path, + struct iucv_message *msg) +{ + struct hvc_iucv_private *priv = path->private; + struct iucv_tty_buffer *ent, *next; + LIST_HEAD(list_remove); + + spin_lock(&priv->lock); + list_for_each_entry_safe(ent, next, &priv->tty_outqueue, list) + if (ent->msg.id == msg->id) { + list_move(&ent->list, &list_remove); + break; + } + wake_up(&priv->sndbuf_waitq); + spin_unlock(&priv->lock); + destroy_tty_buffer_list(&list_remove); +} + +/** + * hvc_iucv_pm_freeze() - Freeze PM callback + * @dev: IUVC HVC terminal device + * + * Sever an established IUCV communication path and + * trigger a hang-up of the underlying HVC terminal. + */ +static int hvc_iucv_pm_freeze(struct device *dev) +{ + struct hvc_iucv_private *priv = dev_get_drvdata(dev); + + local_bh_disable(); + hvc_iucv_hangup(priv); + local_bh_enable(); + + return 0; +} + +/** + * hvc_iucv_pm_restore_thaw() - Thaw and restore PM callback + * @dev: IUVC HVC terminal device + * + * Wake up the HVC thread to trigger hang-up and respective + * HVC back-end notifier invocations. + */ +static int hvc_iucv_pm_restore_thaw(struct device *dev) +{ + hvc_kick(); + return 0; +} + + +/* HVC operations */ +static const struct hv_ops hvc_iucv_ops = { + .get_chars = hvc_iucv_get_chars, + .put_chars = hvc_iucv_put_chars, + .notifier_add = hvc_iucv_notifier_add, + .notifier_del = hvc_iucv_notifier_del, + .notifier_hangup = hvc_iucv_notifier_hangup, +}; + +/* Suspend / resume device operations */ +static const struct dev_pm_ops hvc_iucv_pm_ops = { + .freeze = hvc_iucv_pm_freeze, + .thaw = hvc_iucv_pm_restore_thaw, + .restore = hvc_iucv_pm_restore_thaw, +}; + +/* IUCV HVC device driver */ +static struct device_driver hvc_iucv_driver = { + .name = KMSG_COMPONENT, + .bus = &iucv_bus, + .pm = &hvc_iucv_pm_ops, +}; + +/** + * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance + * @id: hvc_iucv_table index + * @is_console: Flag if the instance is used as Linux console + * + * This function allocates a new hvc_iucv_private structure and stores + * the instance in hvc_iucv_table at index @id. + * Returns 0 on success; otherwise non-zero. + */ +static int __init hvc_iucv_alloc(int id, unsigned int is_console) +{ + struct hvc_iucv_private *priv; + char name[9]; + int rc; + + priv = kzalloc(sizeof(struct hvc_iucv_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->tty_outqueue); + INIT_LIST_HEAD(&priv->tty_inqueue); + INIT_DELAYED_WORK(&priv->sndbuf_work, hvc_iucv_sndbuf_work); + init_waitqueue_head(&priv->sndbuf_waitq); + + priv->sndbuf = (void *) get_zeroed_page(GFP_KERNEL); + if (!priv->sndbuf) { + kfree(priv); + return -ENOMEM; + } + + /* set console flag */ + priv->is_console = is_console; + + /* allocate hvc device */ + priv->hvc = hvc_alloc(HVC_IUCV_MAGIC + id, /* PAGE_SIZE */ + HVC_IUCV_MAGIC + id, &hvc_iucv_ops, 256); + if (IS_ERR(priv->hvc)) { + rc = PTR_ERR(priv->hvc); + goto out_error_hvc; + } + + /* notify HVC thread instead of using polling */ + priv->hvc->irq_requested = 1; + + /* setup iucv related information */ + snprintf(name, 9, "lnxhvc%-2d", id); + memcpy(priv->srv_name, name, 8); + ASCEBC(priv->srv_name, 8); + + /* create and setup device */ + priv->dev = kzalloc(sizeof(*priv->dev), GFP_KERNEL); + if (!priv->dev) { + rc = -ENOMEM; + goto out_error_dev; + } + dev_set_name(priv->dev, "hvc_iucv%d", id); + dev_set_drvdata(priv->dev, priv); + priv->dev->bus = &iucv_bus; + priv->dev->parent = iucv_root; + priv->dev->driver = &hvc_iucv_driver; + priv->dev->release = (void (*)(struct device *)) kfree; + rc = device_register(priv->dev); + if (rc) { + put_device(priv->dev); + goto out_error_dev; + } + + hvc_iucv_table[id] = priv; + return 0; + +out_error_dev: + hvc_remove(priv->hvc); +out_error_hvc: + free_page((unsigned long) priv->sndbuf); + kfree(priv); + + return rc; +} + +/** + * hvc_iucv_destroy() - Destroy and free hvc_iucv_private instances + */ +static void __init hvc_iucv_destroy(struct hvc_iucv_private *priv) +{ + hvc_remove(priv->hvc); + device_unregister(priv->dev); + free_page((unsigned long) priv->sndbuf); + kfree(priv); +} + +/** + * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID + * @filter: String containing a comma-separated list of z/VM user IDs + */ +static const char *hvc_iucv_parse_filter(const char *filter, char *dest) +{ + const char *nextdelim, *residual; + size_t len; + + nextdelim = strchr(filter, ','); + if (nextdelim) { + len = nextdelim - filter; + residual = nextdelim + 1; + } else { + len = strlen(filter); + residual = filter + len; + } + + if (len == 0) + return ERR_PTR(-EINVAL); + + /* check for '\n' (if called from sysfs) */ + if (filter[len - 1] == '\n') + len--; + + if (len > 8) + return ERR_PTR(-EINVAL); + + /* pad with blanks and save upper case version of user ID */ + memset(dest, ' ', 8); + while (len--) + dest[len] = toupper(filter[len]); + return residual; +} + +/** + * hvc_iucv_setup_filter() - Set up z/VM user ID filter + * @filter: String consisting of a comma-separated list of z/VM user IDs + * + * The function parses the @filter string and creates an array containing + * the list of z/VM user ID filter entries. + * Return code 0 means success, -EINVAL if the filter is syntactically + * incorrect, -ENOMEM if there was not enough memory to allocate the + * filter list array, or -ENOSPC if too many z/VM user IDs have been specified. + */ +static int hvc_iucv_setup_filter(const char *val) +{ + const char *residual; + int err; + size_t size, count; + void *array, *old_filter; + + count = strlen(val); + if (count == 0 || (count == 1 && val[0] == '\n')) { + size = 0; + array = NULL; + goto out_replace_filter; /* clear filter */ + } + + /* count user IDs in order to allocate sufficient memory */ + size = 1; + residual = val; + while ((residual = strchr(residual, ',')) != NULL) { + residual++; + size++; + } + + /* check if the specified list exceeds the filter limit */ + if (size > MAX_VMID_FILTER) + return -ENOSPC; + + array = kzalloc(size * 8, GFP_KERNEL); + if (!array) + return -ENOMEM; + + count = size; + residual = val; + while (*residual && count) { + residual = hvc_iucv_parse_filter(residual, + array + ((size - count) * 8)); + if (IS_ERR(residual)) { + err = PTR_ERR(residual); + kfree(array); + goto out_err; + } + count--; + } + +out_replace_filter: + write_lock_bh(&hvc_iucv_filter_lock); + old_filter = hvc_iucv_filter; + hvc_iucv_filter_size = size; + hvc_iucv_filter = array; + write_unlock_bh(&hvc_iucv_filter_lock); + kfree(old_filter); + + err = 0; +out_err: + return err; +} + +/** + * param_set_vmidfilter() - Set z/VM user ID filter parameter + * @val: String consisting of a comma-separated list of z/VM user IDs + * @kp: Kernel parameter pointing to hvc_iucv_filter array + * + * The function sets up the z/VM user ID filter specified as comma-separated + * list of user IDs in @val. + * Note: If it is called early in the boot process, @val is stored and + * parsed later in hvc_iucv_init(). + */ +static int param_set_vmidfilter(const char *val, const struct kernel_param *kp) +{ + int rc; + + if (!MACHINE_IS_VM || !hvc_iucv_devices) + return -ENODEV; + + if (!val) + return -EINVAL; + + rc = 0; + if (slab_is_available()) + rc = hvc_iucv_setup_filter(val); + else + hvc_iucv_filter_string = val; /* defer... */ + return rc; +} + +/** + * param_get_vmidfilter() - Get z/VM user ID filter + * @buffer: Buffer to store z/VM user ID filter, + * (buffer size assumption PAGE_SIZE) + * @kp: Kernel parameter pointing to the hvc_iucv_filter array + * + * The function stores the filter as a comma-separated list of z/VM user IDs + * in @buffer. Typically, sysfs routines call this function for attr show. + */ +static int param_get_vmidfilter(char *buffer, const struct kernel_param *kp) +{ + int rc; + size_t index, len; + void *start, *end; + + if (!MACHINE_IS_VM || !hvc_iucv_devices) + return -ENODEV; + + rc = 0; + read_lock_bh(&hvc_iucv_filter_lock); + for (index = 0; index < hvc_iucv_filter_size; index++) { + start = hvc_iucv_filter + (8 * index); + end = memchr(start, ' ', 8); + len = (end) ? end - start : 8; + memcpy(buffer + rc, start, len); + rc += len; + buffer[rc++] = ','; + } + read_unlock_bh(&hvc_iucv_filter_lock); + if (rc) + buffer[--rc] = '\0'; /* replace last comma and update rc */ + return rc; +} + +#define param_check_vmidfilter(name, p) __param_check(name, p, void) + +static struct kernel_param_ops param_ops_vmidfilter = { + .set = param_set_vmidfilter, + .get = param_get_vmidfilter, +}; + +/** + * hvc_iucv_init() - z/VM IUCV HVC device driver initialization + */ +static int __init hvc_iucv_init(void) +{ + int rc; + unsigned int i; + + if (!hvc_iucv_devices) + return -ENODEV; + + if (!MACHINE_IS_VM) { + pr_notice("The z/VM IUCV HVC device driver cannot " + "be used without z/VM\n"); + rc = -ENODEV; + goto out_error; + } + + if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) { + pr_err("%lu is not a valid value for the hvc_iucv= " + "kernel parameter\n", hvc_iucv_devices); + rc = -EINVAL; + goto out_error; + } + + /* register IUCV HVC device driver */ + rc = driver_register(&hvc_iucv_driver); + if (rc) + goto out_error; + + /* parse hvc_iucv_allow string and create z/VM user ID filter list */ + if (hvc_iucv_filter_string) { + rc = hvc_iucv_setup_filter(hvc_iucv_filter_string); + switch (rc) { + case 0: + break; + case -ENOMEM: + pr_err("Allocating memory failed with " + "reason code=%d\n", 3); + goto out_error; + case -EINVAL: + pr_err("hvc_iucv_allow= does not specify a valid " + "z/VM user ID list\n"); + goto out_error; + case -ENOSPC: + pr_err("hvc_iucv_allow= specifies too many " + "z/VM user IDs\n"); + goto out_error; + default: + goto out_error; + } + } + + hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, + sizeof(struct iucv_tty_buffer), + 0, 0, NULL); + if (!hvc_iucv_buffer_cache) { + pr_err("Allocating memory failed with reason code=%d\n", 1); + rc = -ENOMEM; + goto out_error; + } + + hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, + hvc_iucv_buffer_cache); + if (!hvc_iucv_mempool) { + pr_err("Allocating memory failed with reason code=%d\n", 2); + kmem_cache_destroy(hvc_iucv_buffer_cache); + rc = -ENOMEM; + goto out_error; + } + + /* register the first terminal device as console + * (must be done before allocating hvc terminal devices) */ + rc = hvc_instantiate(HVC_IUCV_MAGIC, IUCV_HVC_CON_IDX, &hvc_iucv_ops); + if (rc) { + pr_err("Registering HVC terminal device as " + "Linux console failed\n"); + goto out_error_memory; + } + + /* allocate hvc_iucv_private structs */ + for (i = 0; i < hvc_iucv_devices; i++) { + rc = hvc_iucv_alloc(i, (i == IUCV_HVC_CON_IDX) ? 1 : 0); + if (rc) { + pr_err("Creating a new HVC terminal device " + "failed with error code=%d\n", rc); + goto out_error_hvc; + } + } + + /* register IUCV callback handler */ + rc = iucv_register(&hvc_iucv_handler, 0); + if (rc) { + pr_err("Registering IUCV handlers failed with error code=%d\n", + rc); + goto out_error_hvc; + } + + return 0; + +out_error_hvc: + for (i = 0; i < hvc_iucv_devices; i++) + if (hvc_iucv_table[i]) + hvc_iucv_destroy(hvc_iucv_table[i]); +out_error_memory: + mempool_destroy(hvc_iucv_mempool); + kmem_cache_destroy(hvc_iucv_buffer_cache); +out_error: + if (hvc_iucv_filter) + kfree(hvc_iucv_filter); + hvc_iucv_devices = 0; /* ensure that we do not provide any device */ + return rc; +} + +/** + * hvc_iucv_config() - Parsing of hvc_iucv= kernel command line parameter + * @val: Parameter value (numeric) + */ +static int __init hvc_iucv_config(char *val) +{ + return strict_strtoul(val, 10, &hvc_iucv_devices); +} + + +device_initcall(hvc_iucv_init); +__setup("hvc_iucv=", hvc_iucv_config); +core_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640); diff --git a/drivers/tty/hvc/hvc_rtas.c b/drivers/tty/hvc/hvc_rtas.c new file mode 100644 index 000000000000..61c4a61558d9 --- /dev/null +++ b/drivers/tty/hvc/hvc_rtas.c @@ -0,0 +1,133 @@ +/* + * IBM RTAS driver interface to hvc_console.c + * + * (C) Copyright IBM Corporation 2001-2005 + * (C) Copyright Red Hat, Inc. 2005 + * + * Author(s): Maximino Augilar + * : Ryan S. Arnold + * : Utz Bacher + * : David Woodhouse + * + * inspired by drivers/char/hvc_console.c + * written by Anton Blanchard and Paul Mackerras + * + * 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 "hvc_console.h" + +#define hvc_rtas_cookie 0x67781e15 +struct hvc_struct *hvc_rtas_dev; + +static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE; +static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE; + +static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, + int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[i])) + break; + } + + return i; +} + +static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) +{ + int i, c; + + for (i = 0; i < count; i++) { + if (rtas_call(rtascons_get_char_token, 0, 2, &c)) + break; + + buf[i] = c; + } + + return i; +} + +static const struct hv_ops hvc_rtas_get_put_ops = { + .get_chars = hvc_rtas_read_console, + .put_chars = hvc_rtas_write_console, +}; + +static int __init hvc_rtas_init(void) +{ + struct hvc_struct *hp; + + if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) + rtascons_put_char_token = rtas_token("put-term-char"); + if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) + return -EIO; + + if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) + rtascons_get_char_token = rtas_token("get-term-char"); + if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) + return -EIO; + + BUG_ON(hvc_rtas_dev); + + /* Allocate an hvc_struct for the console device we instantiated + * earlier. Save off hp so that we can return it on exit */ + hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16); + if (IS_ERR(hp)) + return PTR_ERR(hp); + + hvc_rtas_dev = hp; + + return 0; +} +module_init(hvc_rtas_init); + +/* This will tear down the tty portion of the driver */ +static void __exit hvc_rtas_exit(void) +{ + /* Really the fun isn't over until the worker thread breaks down and + * the tty cleans up */ + if (hvc_rtas_dev) + hvc_remove(hvc_rtas_dev); +} +module_exit(hvc_rtas_exit); + +/* This will happen prior to module init. There is no tty at this time? */ +static int __init hvc_rtas_console_init(void) +{ + rtascons_put_char_token = rtas_token("put-term-char"); + if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) + return -EIO; + + rtascons_get_char_token = rtas_token("get-term-char"); + if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) + return -EIO; + + hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops); + add_preferred_console("hvc", 0, NULL); + + return 0; +} +console_initcall(hvc_rtas_console_init); diff --git a/drivers/tty/hvc/hvc_tile.c b/drivers/tty/hvc/hvc_tile.c new file mode 100644 index 000000000000..7a84a0595477 --- /dev/null +++ b/drivers/tty/hvc/hvc_tile.c @@ -0,0 +1,68 @@ +/* + * Copyright 2010 Tilera Corporation. All Rights Reserved. + * + * 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, version 2. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for + * more details. + * + * Tilera TILE Processor hypervisor console + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "hvc_console.h" + +static int hvc_tile_put_chars(uint32_t vt, const char *buf, int count) +{ + return hv_console_write((HV_VirtAddr)buf, count); +} + +static int hvc_tile_get_chars(uint32_t vt, char *buf, int count) +{ + int i, c; + + for (i = 0; i < count; ++i) { + c = hv_console_read_if_ready(); + if (c < 0) + break; + buf[i] = c; + } + + return i; +} + +static const struct hv_ops hvc_tile_get_put_ops = { + .get_chars = hvc_tile_get_chars, + .put_chars = hvc_tile_put_chars, +}; + +static int __init hvc_tile_console_init(void) +{ + extern void disable_early_printk(void); + hvc_instantiate(0, 0, &hvc_tile_get_put_ops); + add_preferred_console("hvc", 0, NULL); + disable_early_printk(); + return 0; +} +console_initcall(hvc_tile_console_init); + +static int __init hvc_tile_init(void) +{ + struct hvc_struct *s; + s = hvc_alloc(0, 0, &hvc_tile_get_put_ops, 128); + return IS_ERR(s) ? PTR_ERR(s) : 0; +} +device_initcall(hvc_tile_init); diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c new file mode 100644 index 000000000000..b0957e61a7be --- /dev/null +++ b/drivers/tty/hvc/hvc_udbg.c @@ -0,0 +1,96 @@ +/* + * udbg interface to hvc_console.c + * + * (C) Copyright David Gibson, IBM Corporation 2008. + * + * 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 "hvc_console.h" + +struct hvc_struct *hvc_udbg_dev; + +static int hvc_udbg_put(uint32_t vtermno, const char *buf, int count) +{ + int i; + + for (i = 0; i < count; i++) + udbg_putc(buf[i]); + + return i; +} + +static int hvc_udbg_get(uint32_t vtermno, char *buf, int count) +{ + int i, c; + + if (!udbg_getc_poll) + return 0; + + for (i = 0; i < count; i++) { + if ((c = udbg_getc_poll()) == -1) + break; + buf[i] = c; + } + + return i; +} + +static const struct hv_ops hvc_udbg_ops = { + .get_chars = hvc_udbg_get, + .put_chars = hvc_udbg_put, +}; + +static int __init hvc_udbg_init(void) +{ + struct hvc_struct *hp; + + BUG_ON(hvc_udbg_dev); + + hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16); + if (IS_ERR(hp)) + return PTR_ERR(hp); + + hvc_udbg_dev = hp; + + return 0; +} +module_init(hvc_udbg_init); + +static void __exit hvc_udbg_exit(void) +{ + if (hvc_udbg_dev) + hvc_remove(hvc_udbg_dev); +} +module_exit(hvc_udbg_exit); + +static int __init hvc_udbg_console_init(void) +{ + hvc_instantiate(0, 0, &hvc_udbg_ops); + add_preferred_console("hvc", 0, NULL); + + return 0; +} +console_initcall(hvc_udbg_console_init); diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c new file mode 100644 index 000000000000..5e2f52b33327 --- /dev/null +++ b/drivers/tty/hvc/hvc_vio.c @@ -0,0 +1,173 @@ +/* + * vio driver interface to hvc_console.c + * + * This code was moved here to allow the remaing code to be reused as a + * generic polling mode with semi-reliable transport driver core to the + * console and tty subsystems. + * + * + * Copyright (C) 2001 Anton Blanchard , IBM + * Copyright (C) 2001 Paul Mackerras , IBM + * Copyright (C) 2004 Benjamin Herrenschmidt , IBM Corp. + * Copyright (C) 2004 IBM Corporation + * + * Additional Author(s): + * Ryan S. Arnold + * + * 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 "hvc_console.h" + +static const char hvc_driver_name[] = "hvc_console"; + +static struct vio_device_id hvc_driver_table[] __devinitdata = { + {"serial", "hvterm1"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, hvc_driver_table); + +static int filtered_get_chars(uint32_t vtermno, char *buf, int count) +{ + unsigned long got; + int i; + + /* + * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion + * so we play safe and avoid the situation where got > count which could + * overload the flip buffer. + */ + if (count < SIZE_VIO_GET_CHARS) + return -EAGAIN; + + got = hvc_get_chars(vtermno, buf, count); + + /* + * Work around a HV bug where it gives us a null + * after every \r. -- paulus + */ + for (i = 1; i < got; ++i) { + if (buf[i] == 0 && buf[i-1] == '\r') { + --got; + if (i < got) + memmove(&buf[i], &buf[i+1], + got - i); + } + } + return got; +} + +static const struct hv_ops hvc_get_put_ops = { + .get_chars = filtered_get_chars, + .put_chars = hvc_put_chars, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int __devinit hvc_vio_probe(struct vio_dev *vdev, + const struct vio_device_id *id) +{ + struct hvc_struct *hp; + + /* probed with invalid parameters. */ + if (!vdev || !id) + return -EPERM; + + hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, + MAX_VIO_PUT_CHARS); + if (IS_ERR(hp)) + return PTR_ERR(hp); + dev_set_drvdata(&vdev->dev, hp); + + return 0; +} + +static int __devexit hvc_vio_remove(struct vio_dev *vdev) +{ + struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); + + return hvc_remove(hp); +} + +static struct vio_driver hvc_vio_driver = { + .id_table = hvc_driver_table, + .probe = hvc_vio_probe, + .remove = __devexit_p(hvc_vio_remove), + .driver = { + .name = hvc_driver_name, + .owner = THIS_MODULE, + } +}; + +static int __init hvc_vio_init(void) +{ + int rc; + + if (firmware_has_feature(FW_FEATURE_ISERIES)) + return -EIO; + + /* Register as a vio device to receive callbacks */ + rc = vio_register_driver(&hvc_vio_driver); + + return rc; +} +module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ + +static void __exit hvc_vio_exit(void) +{ + vio_unregister_driver(&hvc_vio_driver); +} +module_exit(hvc_vio_exit); + +/* the device tree order defines our numbering */ +static int hvc_find_vtys(void) +{ + struct device_node *vty; + int num_found = 0; + + for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; + vty = of_find_node_by_name(vty, "vty")) { + const uint32_t *vtermno; + + /* We have statically defined space for only a certain number + * of console adapters. + */ + if (num_found >= MAX_NR_HVC_CONSOLES) { + of_node_put(vty); + break; + } + + vtermno = of_get_property(vty, "reg", NULL); + if (!vtermno) + continue; + + if (of_device_is_compatible(vty, "hvterm1")) { + hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); + ++num_found; + } + } + + return num_found; +} +console_initcall(hvc_find_vtys); diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c new file mode 100644 index 000000000000..3740e327f180 --- /dev/null +++ b/drivers/tty/hvc/hvc_xen.c @@ -0,0 +1,271 @@ +/* + * xen console driver interface to hvc_console.c + * + * (c) 2007 Gerd Hoffmann + * + * 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 +#include + +#include "hvc_console.h" + +#define HVC_COOKIE 0x58656e /* "Xen" in hex */ + +static struct hvc_struct *hvc; +static int xencons_irq; + +/* ------------------------------------------------------------------ */ + +static unsigned long console_pfn = ~0ul; + +static inline struct xencons_interface *xencons_interface(void) +{ + if (console_pfn == ~0ul) + return mfn_to_virt(xen_start_info->console.domU.mfn); + else + return __va(console_pfn << PAGE_SHIFT); +} + +static inline void notify_daemon(void) +{ + /* Use evtchn: this is called early, before irq is set up. */ + notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); +} + +static int __write_console(const char *data, int len) +{ + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + int sent = 0; + + cons = intf->out_cons; + prod = intf->out_prod; + mb(); /* update queue values before going on */ + BUG_ON((prod - cons) > sizeof(intf->out)); + + while ((sent < len) && ((prod - cons) < sizeof(intf->out))) + intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; + + wmb(); /* write ring before updating pointer */ + intf->out_prod = prod; + + if (sent) + notify_daemon(); + return sent; +} + +static int domU_write_console(uint32_t vtermno, const char *data, int len) +{ + int ret = len; + + /* + * Make sure the whole buffer is emitted, polling if + * necessary. We don't ever want to rely on the hvc daemon + * because the most interesting console output is when the + * kernel is crippled. + */ + while (len) { + int sent = __write_console(data, len); + + data += sent; + len -= sent; + + if (unlikely(len)) + HYPERVISOR_sched_op(SCHEDOP_yield, NULL); + } + + return ret; +} + +static int domU_read_console(uint32_t vtermno, char *buf, int len) +{ + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + int recv = 0; + + cons = intf->in_cons; + prod = intf->in_prod; + mb(); /* get pointers before reading ring */ + BUG_ON((prod - cons) > sizeof(intf->in)); + + while (cons != prod && recv < len) + buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; + + mb(); /* read ring before consuming */ + intf->in_cons = cons; + + notify_daemon(); + return recv; +} + +static struct hv_ops domU_hvc_ops = { + .get_chars = domU_read_console, + .put_chars = domU_write_console, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int dom0_read_console(uint32_t vtermno, char *buf, int len) +{ + return HYPERVISOR_console_io(CONSOLEIO_read, len, buf); +} + +/* + * Either for a dom0 to write to the system console, or a domU with a + * debug version of Xen + */ +static int dom0_write_console(uint32_t vtermno, const char *str, int len) +{ + int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str); + if (rc < 0) + return 0; + + return len; +} + +static struct hv_ops dom0_hvc_ops = { + .get_chars = dom0_read_console, + .put_chars = dom0_write_console, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int __init xen_hvc_init(void) +{ + struct hvc_struct *hp; + struct hv_ops *ops; + + if (!xen_pv_domain()) + return -ENODEV; + + if (xen_initial_domain()) { + ops = &dom0_hvc_ops; + xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); + } else { + if (!xen_start_info->console.domU.evtchn) + return -ENODEV; + + ops = &domU_hvc_ops; + xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); + } + if (xencons_irq < 0) + xencons_irq = 0; /* NO_IRQ */ + + hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256); + if (IS_ERR(hp)) + return PTR_ERR(hp); + + hvc = hp; + + console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn); + + return 0; +} + +void xen_console_resume(void) +{ + if (xencons_irq) + rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq); +} + +static void __exit xen_hvc_fini(void) +{ + if (hvc) + hvc_remove(hvc); +} + +static int xen_cons_init(void) +{ + struct hv_ops *ops; + + if (!xen_pv_domain()) + return 0; + + if (xen_initial_domain()) + ops = &dom0_hvc_ops; + else + ops = &domU_hvc_ops; + + hvc_instantiate(HVC_COOKIE, 0, ops); + return 0; +} + +module_init(xen_hvc_init); +module_exit(xen_hvc_fini); +console_initcall(xen_cons_init); + +#ifdef CONFIG_EARLY_PRINTK +static void xenboot_write_console(struct console *console, const char *string, + unsigned len) +{ + unsigned int linelen, off = 0; + const char *pos; + + dom0_write_console(0, string, len); + + if (xen_initial_domain()) + return; + + domU_write_console(0, "(early) ", 8); + while (off < len && NULL != (pos = strchr(string+off, '\n'))) { + linelen = pos-string+off; + if (off + linelen > len) + break; + domU_write_console(0, string+off, linelen); + domU_write_console(0, "\r\n", 2); + off += linelen + 1; + } + if (off < len) + domU_write_console(0, string+off, len-off); +} + +struct console xenboot_console = { + .name = "xenboot", + .write = xenboot_write_console, + .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, +}; +#endif /* CONFIG_EARLY_PRINTK */ + +void xen_raw_console_write(const char *str) +{ + dom0_write_console(0, str, strlen(str)); +} + +void xen_raw_printk(const char *fmt, ...) +{ + static char buf[512]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + xen_raw_console_write(buf); +} diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c new file mode 100644 index 000000000000..bedc6c1b6fa5 --- /dev/null +++ b/drivers/tty/hvc/hvcs.c @@ -0,0 +1,1604 @@ +/* + * IBM eServer Hypervisor Virtual Console Server Device Driver + * Copyright (C) 2003, 2004 IBM Corp. + * Ryan S. Arnold (rsa@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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 + * + * Author(s) : Ryan S. Arnold + * + * This is the device driver for the IBM Hypervisor Virtual Console Server, + * "hvcs". The IBM hvcs provides a tty driver interface to allow Linux + * user space applications access to the system consoles of logically + * partitioned operating systems, e.g. Linux, running on the same partitioned + * Power5 ppc64 system. Physical hardware consoles per partition are not + * practical on this hardware so system consoles are accessed by this driver + * using inter-partition firmware interfaces to virtual terminal devices. + * + * A vty is known to the HMC as a "virtual serial server adapter". It is a + * virtual terminal device that is created by firmware upon partition creation + * to act as a partitioned OS's console device. + * + * Firmware dynamically (via hotplug) exposes vty-servers to a running ppc64 + * Linux system upon their creation by the HMC or their exposure during boot. + * The non-user interactive backend of this driver is implemented as a vio + * device driver so that it can receive notification of vty-server lifetimes + * after it registers with the vio bus to handle vty-server probe and remove + * callbacks. + * + * Many vty-servers can be configured to connect to one vty, but a vty can + * only be actively connected to by a single vty-server, in any manner, at one + * time. If the HMC is currently hosting the console for a target Linux + * partition; attempts to open the tty device to the partition's console using + * the hvcs on any partition will return -EBUSY with every open attempt until + * the HMC frees the connection between its vty-server and the desired + * partition's vty device. Conversely, a vty-server may only be connected to + * a single vty at one time even though it may have several configured vty + * partner possibilities. + * + * Firmware does not provide notification of vty partner changes to this + * driver. This means that an HMC Super Admin may add or remove partner vtys + * from a vty-server's partner list but the changes will not be signaled to + * the vty-server. Firmware only notifies the driver when a vty-server is + * added or removed from the system. To compensate for this deficiency, this + * driver implements a sysfs update attribute which provides a method for + * rescanning partner information upon a user's request. + * + * Each vty-server, prior to being exposed to this driver is reference counted + * using the 2.6 Linux kernel kref construct. + * + * For direction on installation and usage of this driver please reference + * Documentation/powerpc/hvcs.txt. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 1.3.0 -> 1.3.1 In hvcs_open memset(..,0x00,..) instead of memset(..,0x3F,00). + * Removed braces around single statements following conditionals. Removed '= + * 0' after static int declarations since these default to zero. Removed + * list_for_each_safe() and replaced with list_for_each_entry() in + * hvcs_get_by_index(). The 'safe' version is un-needed now that the driver is + * using spinlocks. Changed spin_lock_irqsave() to spin_lock() when locking + * hvcs_structs_lock and hvcs_pi_lock since these are not touched in an int + * handler. Initialized hvcs_structs_lock and hvcs_pi_lock to + * SPIN_LOCK_UNLOCKED at declaration time rather than in hvcs_module_init(). + * Added spin_lock around list_del() in destroy_hvcs_struct() to protect the + * list traversals from a deletion. Removed '= NULL' from pointer declaration + * statements since they are initialized NULL by default. Removed wmb() + * instances from hvcs_try_write(). They probably aren't needed with locking in + * place. Added check and cleanup for hvcs_pi_buff = kmalloc() in + * hvcs_module_init(). Exposed hvcs_struct.index via a sysfs attribute so that + * the coupling between /dev/hvcs* and a vty-server can be automatically + * determined. Moved kobject_put() in hvcs_open outside of the + * spin_unlock_irqrestore(). + * + * 1.3.1 -> 1.3.2 Changed method for determining hvcs_struct->index and had it + * align with how the tty layer always assigns the lowest index available. This + * change resulted in a list of ints that denotes which indexes are available. + * Device additions and removals use the new hvcs_get_index() and + * hvcs_return_index() helper functions. The list is created with + * hvsc_alloc_index_list() and it is destroyed with hvcs_free_index_list(). + * Without these fixes hotplug vty-server adapter support goes crazy with this + * driver if the user removes a vty-server adapter. Moved free_irq() outside of + * the hvcs_final_close() function in order to get it out of the spinlock. + * Rearranged hvcs_close(). Cleaned up some printks and did some housekeeping + * on the changelog. Removed local CLC_LENGTH and used HVCS_CLC_LENGTH from + * arch/powerepc/include/asm/hvcserver.h + * + * 1.3.2 -> 1.3.3 Replaced yield() in hvcs_close() with tty_wait_until_sent() to + * prevent possible lockup with realtime scheduling as similarily pointed out by + * akpm in hvc_console. Changed resulted in the removal of hvcs_final_close() + * to reorder cleanup operations and prevent discarding of pending data during + * an hvcs_close(). Removed spinlock protection of hvcs_struct data members in + * hvcs_write_room() and hvcs_chars_in_buffer() because they aren't needed. + */ + +#define HVCS_DRIVER_VERSION "1.3.3" + +MODULE_AUTHOR("Ryan S. Arnold "); +MODULE_DESCRIPTION("IBM hvcs (Hypervisor Virtual Console Server) Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(HVCS_DRIVER_VERSION); + +/* + * Wait this long per iteration while trying to push buffered data to the + * hypervisor before allowing the tty to complete a close operation. + */ +#define HVCS_CLOSE_WAIT (HZ/100) /* 1/10 of a second */ + +/* + * Since the Linux TTY code does not currently (2-04-2004) support dynamic + * addition of tty derived devices and we shouldn't allocate thousands of + * tty_device pointers when the number of vty-server & vty partner connections + * will most often be much lower than this, we'll arbitrarily allocate + * HVCS_DEFAULT_SERVER_ADAPTERS tty_structs and cdev's by default when we + * register the tty_driver. This can be overridden using an insmod parameter. + */ +#define HVCS_DEFAULT_SERVER_ADAPTERS 64 + +/* + * The user can't insmod with more than HVCS_MAX_SERVER_ADAPTERS hvcs device + * nodes as a sanity check. Theoretically there can be over 1 Billion + * vty-server & vty partner connections. + */ +#define HVCS_MAX_SERVER_ADAPTERS 1024 + +/* + * We let Linux assign us a major number and we start the minors at zero. There + * is no intuitive mapping between minor number and the target vty-server + * adapter except that each new vty-server adapter is always assigned to the + * smallest minor number available. + */ +#define HVCS_MINOR_START 0 + +/* + * The hcall interface involves putting 8 chars into each of two registers. + * We load up those 2 registers (in arch/powerpc/platforms/pseries/hvconsole.c) + * by casting char[16] to long[2]. It would work without __ALIGNED__, but a + * little (tiny) bit slower because an unaligned load is slower than aligned + * load. + */ +#define __ALIGNED__ __attribute__((__aligned__(8))) + +/* + * How much data can firmware send with each hvc_put_chars()? Maybe this + * should be moved into an architecture specific area. + */ +#define HVCS_BUFF_LEN 16 + +/* + * This is the maximum amount of data we'll let the user send us (hvcs_write) at + * once in a chunk as a sanity check. + */ +#define HVCS_MAX_FROM_USER 4096 + +/* + * Be careful when adding flags to this line discipline. Don't add anything + * that will cause echoing or we'll go into recursive loop echoing chars back + * and forth with the console drivers. + */ +static struct ktermios hvcs_tty_termios = { + .c_iflag = IGNBRK | IGNPAR, + .c_oflag = OPOST, + .c_cflag = B38400 | CS8 | CREAD | HUPCL, + .c_cc = INIT_C_CC, + .c_ispeed = 38400, + .c_ospeed = 38400 +}; + +/* + * This value is used to take the place of a command line parameter when the + * module is inserted. It starts as -1 and stays as such if the user doesn't + * specify a module insmod parameter. If they DO specify one then it is set to + * the value of the integer passed in. + */ +static int hvcs_parm_num_devs = -1; +module_param(hvcs_parm_num_devs, int, 0); + +static const char hvcs_driver_name[] = "hvcs"; +static const char hvcs_device_node[] = "hvcs"; +static const char hvcs_driver_string[] + = "IBM hvcs (Hypervisor Virtual Console Server) Driver"; + +/* Status of partner info rescan triggered via sysfs. */ +static int hvcs_rescan_status; + +static struct tty_driver *hvcs_tty_driver; + +/* + * In order to be somewhat sane this driver always associates the hvcs_struct + * index element with the numerically equal tty->index. This means that a + * hotplugged vty-server adapter will always map to the lowest index valued + * device node. If vty-servers were hotplug removed from the system and then + * new ones added the new vty-server may have the largest slot number of all + * the vty-server adapters in the partition but it may have the lowest dev node + * index of all the adapters due to the hole left by the hotplug removed + * adapter. There are a set of functions provided to get the lowest index for + * a new device as well as return the index to the list. This list is allocated + * with a number of elements equal to the number of device nodes requested when + * the module was inserted. + */ +static int *hvcs_index_list; + +/* + * How large is the list? This is kept for traversal since the list is + * dynamically created. + */ +static int hvcs_index_count; + +/* + * Used by the khvcsd to pick up I/O operations when the kernel_thread is + * already awake but potentially shifted to TASK_INTERRUPTIBLE state. + */ +static int hvcs_kicked; + +/* + * Use by the kthread construct for task operations like waking the sleeping + * thread and stopping the kthread. + */ +static struct task_struct *hvcs_task; + +/* + * We allocate this for the use of all of the hvcs_structs when they fetch + * partner info. + */ +static unsigned long *hvcs_pi_buff; + +/* Only allow one hvcs_struct to use the hvcs_pi_buff at a time. */ +static DEFINE_SPINLOCK(hvcs_pi_lock); + +/* One vty-server per hvcs_struct */ +struct hvcs_struct { + spinlock_t lock; + + /* + * This index identifies this hvcs device as the complement to a + * specific tty index. + */ + unsigned int index; + + struct tty_struct *tty; + int open_count; + + /* + * Used to tell the driver kernel_thread what operations need to take + * place upon this hvcs_struct instance. + */ + int todo_mask; + + /* + * This buffer is required so that when hvcs_write_room() reports that + * it can send HVCS_BUFF_LEN characters that it will buffer the full + * HVCS_BUFF_LEN characters if need be. This is essential for opost + * writes since they do not do high level buffering and expect to be + * able to send what the driver commits to sending buffering + * [e.g. tab to space conversions in n_tty.c opost()]. + */ + char buffer[HVCS_BUFF_LEN]; + int chars_in_buffer; + + /* + * Any variable below the kref is valid before a tty is connected and + * stays valid after the tty is disconnected. These shouldn't be + * whacked until the koject refcount reaches zero though some entries + * may be changed via sysfs initiatives. + */ + struct kref kref; /* ref count & hvcs_struct lifetime */ + int connected; /* is the vty-server currently connected to a vty? */ + uint32_t p_unit_address; /* partner unit address */ + uint32_t p_partition_ID; /* partner partition ID */ + char p_location_code[HVCS_CLC_LENGTH + 1]; /* CLC + Null Term */ + struct list_head next; /* list management */ + struct vio_dev *vdev; +}; + +/* Required to back map a kref to its containing object */ +#define from_kref(k) container_of(k, struct hvcs_struct, kref) + +static LIST_HEAD(hvcs_structs); +static DEFINE_SPINLOCK(hvcs_structs_lock); + +static void hvcs_unthrottle(struct tty_struct *tty); +static void hvcs_throttle(struct tty_struct *tty); +static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance); + +static int hvcs_write(struct tty_struct *tty, + const unsigned char *buf, int count); +static int hvcs_write_room(struct tty_struct *tty); +static int hvcs_chars_in_buffer(struct tty_struct *tty); + +static int hvcs_has_pi(struct hvcs_struct *hvcsd); +static void hvcs_set_pi(struct hvcs_partner_info *pi, + struct hvcs_struct *hvcsd); +static int hvcs_get_pi(struct hvcs_struct *hvcsd); +static int hvcs_rescan_devices_list(void); + +static int hvcs_partner_connect(struct hvcs_struct *hvcsd); +static void hvcs_partner_free(struct hvcs_struct *hvcsd); + +static int hvcs_enable_device(struct hvcs_struct *hvcsd, + uint32_t unit_address, unsigned int irq, struct vio_dev *dev); + +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, + 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); + +#define HVCS_SCHED_READ 0x00000001 +#define HVCS_QUICK_READ 0x00000002 +#define HVCS_TRY_WRITE 0x00000004 +#define HVCS_READ_MASK (HVCS_SCHED_READ | HVCS_QUICK_READ) + +static inline struct hvcs_struct *from_vio_dev(struct vio_dev *viod) +{ + return dev_get_drvdata(&viod->dev); +} +/* The sysfs interface for the driver and devices */ + +static ssize_t hvcs_partner_vtys_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + int retval; + + spin_lock_irqsave(&hvcsd->lock, flags); + retval = sprintf(buf, "%X\n", hvcsd->p_unit_address); + spin_unlock_irqrestore(&hvcsd->lock, flags); + return retval; +} +static DEVICE_ATTR(partner_vtys, S_IRUGO, hvcs_partner_vtys_show, NULL); + +static ssize_t hvcs_partner_clcs_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + int retval; + + spin_lock_irqsave(&hvcsd->lock, flags); + retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]); + spin_unlock_irqrestore(&hvcsd->lock, flags); + return retval; +} +static DEVICE_ATTR(partner_clcs, S_IRUGO, hvcs_partner_clcs_show, NULL); + +static ssize_t hvcs_current_vty_store(struct device *dev, struct device_attribute *attr, const char * buf, + size_t count) +{ + /* + * Don't need this feature at the present time because firmware doesn't + * yet support multiple partners. + */ + printk(KERN_INFO "HVCS: Denied current_vty change: -EPERM.\n"); + return -EPERM; +} + +static ssize_t hvcs_current_vty_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + int retval; + + spin_lock_irqsave(&hvcsd->lock, flags); + retval = sprintf(buf, "%s\n", &hvcsd->p_location_code[0]); + spin_unlock_irqrestore(&hvcsd->lock, flags); + return retval; +} + +static DEVICE_ATTR(current_vty, + S_IRUGO | S_IWUSR, hvcs_current_vty_show, hvcs_current_vty_store); + +static ssize_t hvcs_vterm_state_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + + /* writing a '0' to this sysfs entry will result in the disconnect. */ + if (simple_strtol(buf, NULL, 0) != 0) + return -EINVAL; + + spin_lock_irqsave(&hvcsd->lock, flags); + + if (hvcsd->open_count > 0) { + spin_unlock_irqrestore(&hvcsd->lock, flags); + printk(KERN_INFO "HVCS: vterm state unchanged. " + "The hvcs device node is still in use.\n"); + return -EPERM; + } + + if (hvcsd->connected == 0) { + spin_unlock_irqrestore(&hvcsd->lock, flags); + printk(KERN_INFO "HVCS: vterm state unchanged. The" + " vty-server is not connected to a vty.\n"); + return -EPERM; + } + + hvcs_partner_free(hvcsd); + printk(KERN_INFO "HVCS: Closed vty-server@%X and" + " partner vty@%X:%d connection.\n", + hvcsd->vdev->unit_address, + hvcsd->p_unit_address, + (uint32_t)hvcsd->p_partition_ID); + + spin_unlock_irqrestore(&hvcsd->lock, flags); + return count; +} + +static ssize_t hvcs_vterm_state_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + int retval; + + spin_lock_irqsave(&hvcsd->lock, flags); + retval = sprintf(buf, "%d\n", hvcsd->connected); + spin_unlock_irqrestore(&hvcsd->lock, flags); + return retval; +} +static DEVICE_ATTR(vterm_state, S_IRUGO | S_IWUSR, + hvcs_vterm_state_show, hvcs_vterm_state_store); + +static ssize_t hvcs_index_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct vio_dev *viod = to_vio_dev(dev); + struct hvcs_struct *hvcsd = from_vio_dev(viod); + unsigned long flags; + int retval; + + spin_lock_irqsave(&hvcsd->lock, flags); + retval = sprintf(buf, "%d\n", hvcsd->index); + spin_unlock_irqrestore(&hvcsd->lock, flags); + return retval; +} + +static DEVICE_ATTR(index, S_IRUGO, hvcs_index_show, NULL); + +static struct attribute *hvcs_attrs[] = { + &dev_attr_partner_vtys.attr, + &dev_attr_partner_clcs.attr, + &dev_attr_current_vty.attr, + &dev_attr_vterm_state.attr, + &dev_attr_index.attr, + NULL, +}; + +static struct attribute_group hvcs_attr_group = { + .attrs = hvcs_attrs, +}; + +static ssize_t hvcs_rescan_show(struct device_driver *ddp, char *buf) +{ + /* A 1 means it is updating, a 0 means it is done updating */ + return snprintf(buf, PAGE_SIZE, "%d\n", hvcs_rescan_status); +} + +static ssize_t hvcs_rescan_store(struct device_driver *ddp, const char * buf, + size_t count) +{ + if ((simple_strtol(buf, NULL, 0) != 1) + && (hvcs_rescan_status != 0)) + return -EINVAL; + + hvcs_rescan_status = 1; + printk(KERN_INFO "HVCS: rescanning partner info for all" + " vty-servers.\n"); + hvcs_rescan_devices_list(); + hvcs_rescan_status = 0; + return count; +} + +static DRIVER_ATTR(rescan, + S_IRUGO | S_IWUSR, hvcs_rescan_show, hvcs_rescan_store); + +static void hvcs_kick(void) +{ + hvcs_kicked = 1; + wmb(); + wake_up_process(hvcs_task); +} + +static void hvcs_unthrottle(struct tty_struct *tty) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&hvcsd->lock, flags); + hvcsd->todo_mask |= HVCS_SCHED_READ; + spin_unlock_irqrestore(&hvcsd->lock, flags); + hvcs_kick(); +} + +static void hvcs_throttle(struct tty_struct *tty) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&hvcsd->lock, flags); + vio_disable_interrupts(hvcsd->vdev); + spin_unlock_irqrestore(&hvcsd->lock, flags); +} + +/* + * If the device is being removed we don't have to worry about this interrupt + * handler taking any further interrupts because they are disabled which means + * the hvcs_struct will always be valid in this handler. + */ +static irqreturn_t hvcs_handle_interrupt(int irq, void *dev_instance) +{ + struct hvcs_struct *hvcsd = dev_instance; + + spin_lock(&hvcsd->lock); + vio_disable_interrupts(hvcsd->vdev); + hvcsd->todo_mask |= HVCS_SCHED_READ; + spin_unlock(&hvcsd->lock); + hvcs_kick(); + + return IRQ_HANDLED; +} + +/* This function must be called with the hvcsd->lock held */ +static void hvcs_try_write(struct hvcs_struct *hvcsd) +{ + uint32_t unit_address = hvcsd->vdev->unit_address; + struct tty_struct *tty = hvcsd->tty; + int sent; + + if (hvcsd->todo_mask & HVCS_TRY_WRITE) { + /* won't send partial writes */ + sent = hvc_put_chars(unit_address, + &hvcsd->buffer[0], + hvcsd->chars_in_buffer ); + if (sent > 0) { + hvcsd->chars_in_buffer = 0; + /* wmb(); */ + hvcsd->todo_mask &= ~(HVCS_TRY_WRITE); + /* wmb(); */ + + /* + * We are still obligated to deliver the data to the + * hypervisor even if the tty has been closed because + * we commited to delivering it. But don't try to wake + * a non-existent tty. + */ + if (tty) { + tty_wakeup(tty); + } + } + } +} + +static int hvcs_io(struct hvcs_struct *hvcsd) +{ + uint32_t unit_address; + struct tty_struct *tty; + char buf[HVCS_BUFF_LEN] __ALIGNED__; + unsigned long flags; + int got = 0; + + spin_lock_irqsave(&hvcsd->lock, flags); + + unit_address = hvcsd->vdev->unit_address; + tty = hvcsd->tty; + + hvcs_try_write(hvcsd); + + if (!tty || test_bit(TTY_THROTTLED, &tty->flags)) { + hvcsd->todo_mask &= ~(HVCS_READ_MASK); + goto bail; + } else if (!(hvcsd->todo_mask & (HVCS_READ_MASK))) + goto bail; + + /* remove the read masks */ + hvcsd->todo_mask &= ~(HVCS_READ_MASK); + + if (tty_buffer_request_room(tty, HVCS_BUFF_LEN) >= HVCS_BUFF_LEN) { + got = hvc_get_chars(unit_address, + &buf[0], + HVCS_BUFF_LEN); + tty_insert_flip_string(tty, buf, got); + } + + /* Give the TTY time to process the data we just sent. */ + if (got) + hvcsd->todo_mask |= HVCS_QUICK_READ; + + spin_unlock_irqrestore(&hvcsd->lock, flags); + /* This is synch because tty->low_latency == 1 */ + if(got) + tty_flip_buffer_push(tty); + + if (!got) { + /* Do this _after_ the flip_buffer_push */ + spin_lock_irqsave(&hvcsd->lock, flags); + vio_enable_interrupts(hvcsd->vdev); + spin_unlock_irqrestore(&hvcsd->lock, flags); + } + + return hvcsd->todo_mask; + + bail: + spin_unlock_irqrestore(&hvcsd->lock, flags); + return hvcsd->todo_mask; +} + +static int khvcsd(void *unused) +{ + struct hvcs_struct *hvcsd; + int hvcs_todo_mask; + + __set_current_state(TASK_RUNNING); + + do { + hvcs_todo_mask = 0; + hvcs_kicked = 0; + wmb(); + + spin_lock(&hvcs_structs_lock); + list_for_each_entry(hvcsd, &hvcs_structs, next) { + hvcs_todo_mask |= hvcs_io(hvcsd); + } + spin_unlock(&hvcs_structs_lock); + + /* + * If any of the hvcs adapters want to try a write or quick read + * don't schedule(), yield a smidgen then execute the hvcs_io + * thread again for those that want the write. + */ + if (hvcs_todo_mask & (HVCS_TRY_WRITE | HVCS_QUICK_READ)) { + yield(); + continue; + } + + set_current_state(TASK_INTERRUPTIBLE); + if (!hvcs_kicked) + schedule(); + __set_current_state(TASK_RUNNING); + } while (!kthread_should_stop()); + + return 0; +} + +static struct vio_device_id hvcs_driver_table[] __devinitdata= { + {"serial-server", "hvterm2"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, hvcs_driver_table); + +static void hvcs_return_index(int index) +{ + /* Paranoia check */ + if (!hvcs_index_list) + return; + if (index < 0 || index >= hvcs_index_count) + return; + if (hvcs_index_list[index] == -1) + return; + else + hvcs_index_list[index] = -1; +} + +/* callback when the kref ref count reaches zero */ +static void destroy_hvcs_struct(struct kref *kref) +{ + struct hvcs_struct *hvcsd = from_kref(kref); + struct vio_dev *vdev; + unsigned long flags; + + spin_lock(&hvcs_structs_lock); + spin_lock_irqsave(&hvcsd->lock, flags); + + /* the list_del poisons the pointers */ + list_del(&(hvcsd->next)); + + if (hvcsd->connected == 1) { + hvcs_partner_free(hvcsd); + printk(KERN_INFO "HVCS: Closed vty-server@%X and" + " partner vty@%X:%d connection.\n", + hvcsd->vdev->unit_address, + hvcsd->p_unit_address, + (uint32_t)hvcsd->p_partition_ID); + } + printk(KERN_INFO "HVCS: Destroyed hvcs_struct for vty-server@%X.\n", + hvcsd->vdev->unit_address); + + vdev = hvcsd->vdev; + hvcsd->vdev = NULL; + + hvcsd->p_unit_address = 0; + hvcsd->p_partition_ID = 0; + hvcs_return_index(hvcsd->index); + memset(&hvcsd->p_location_code[0], 0x00, HVCS_CLC_LENGTH + 1); + + spin_unlock_irqrestore(&hvcsd->lock, flags); + spin_unlock(&hvcs_structs_lock); + + sysfs_remove_group(&vdev->dev.kobj, &hvcs_attr_group); + + kfree(hvcsd); +} + +static int hvcs_get_index(void) +{ + int i; + /* Paranoia check */ + if (!hvcs_index_list) { + printk(KERN_ERR "HVCS: hvcs_index_list NOT valid!.\n"); + return -EFAULT; + } + /* Find the numerically lowest first free index. */ + for(i = 0; i < hvcs_index_count; i++) { + if (hvcs_index_list[i] == -1) { + hvcs_index_list[i] = 0; + return i; + } + } + return -1; +} + +static int __devinit hvcs_probe( + struct vio_dev *dev, + const struct vio_device_id *id) +{ + struct hvcs_struct *hvcsd; + int index; + int retval; + + if (!dev || !id) { + printk(KERN_ERR "HVCS: probed with invalid parameter.\n"); + return -EPERM; + } + + /* early to avoid cleanup on failure */ + index = hvcs_get_index(); + if (index < 0) { + return -EFAULT; + } + + hvcsd = kzalloc(sizeof(*hvcsd), GFP_KERNEL); + if (!hvcsd) + return -ENODEV; + + + spin_lock_init(&hvcsd->lock); + /* Automatically incs the refcount the first time */ + kref_init(&hvcsd->kref); + + hvcsd->vdev = dev; + dev_set_drvdata(&dev->dev, hvcsd); + + hvcsd->index = index; + + /* hvcsd->index = ++hvcs_struct_count; */ + hvcsd->chars_in_buffer = 0; + hvcsd->todo_mask = 0; + hvcsd->connected = 0; + + /* + * This will populate the hvcs_struct's partner info fields for the + * first time. + */ + if (hvcs_get_pi(hvcsd)) { + printk(KERN_ERR "HVCS: Failed to fetch partner" + " info for vty-server@%X on device probe.\n", + hvcsd->vdev->unit_address); + } + + /* + * If a user app opens a tty that corresponds to this vty-server before + * the hvcs_struct has been added to the devices list then the user app + * will get -ENODEV. + */ + spin_lock(&hvcs_structs_lock); + list_add_tail(&(hvcsd->next), &hvcs_structs); + spin_unlock(&hvcs_structs_lock); + + retval = sysfs_create_group(&dev->dev.kobj, &hvcs_attr_group); + if (retval) { + printk(KERN_ERR "HVCS: Can't create sysfs attrs for vty-server@%X\n", + hvcsd->vdev->unit_address); + return retval; + } + + printk(KERN_INFO "HVCS: vty-server@%X added to the vio bus.\n", dev->unit_address); + + /* + * DON'T enable interrupts here because there is no user to receive the + * data. + */ + return 0; +} + +static int __devexit hvcs_remove(struct vio_dev *dev) +{ + struct hvcs_struct *hvcsd = dev_get_drvdata(&dev->dev); + unsigned long flags; + struct tty_struct *tty; + + if (!hvcsd) + return -ENODEV; + + /* By this time the vty-server won't be getting any more interrupts */ + + spin_lock_irqsave(&hvcsd->lock, flags); + + tty = hvcsd->tty; + + spin_unlock_irqrestore(&hvcsd->lock, flags); + + /* + * Let the last holder of this object cause it to be removed, which + * would probably be tty_hangup below. + */ + kref_put(&hvcsd->kref, destroy_hvcs_struct); + + /* + * The hangup is a scheduled function which will auto chain call + * hvcs_hangup. The tty should always be valid at this time unless a + * simultaneous tty close already cleaned up the hvcs_struct. + */ + if (tty) + tty_hangup(tty); + + printk(KERN_INFO "HVCS: vty-server@%X removed from the" + " vio bus.\n", dev->unit_address); + return 0; +}; + +static struct vio_driver hvcs_vio_driver = { + .id_table = hvcs_driver_table, + .probe = hvcs_probe, + .remove = __devexit_p(hvcs_remove), + .driver = { + .name = hvcs_driver_name, + .owner = THIS_MODULE, + } +}; + +/* Only called from hvcs_get_pi please */ +static void hvcs_set_pi(struct hvcs_partner_info *pi, struct hvcs_struct *hvcsd) +{ + int clclength; + + hvcsd->p_unit_address = pi->unit_address; + hvcsd->p_partition_ID = pi->partition_ID; + clclength = strlen(&pi->location_code[0]); + if (clclength > HVCS_CLC_LENGTH) + clclength = HVCS_CLC_LENGTH; + + /* copy the null-term char too */ + strncpy(&hvcsd->p_location_code[0], + &pi->location_code[0], clclength + 1); +} + +/* + * Traverse the list and add the partner info that is found to the hvcs_struct + * struct entry. NOTE: At this time I know that partner info will return a + * single entry but in the future there may be multiple partner info entries per + * vty-server and you'll want to zero out that list and reset it. If for some + * reason you have an old version of this driver but there IS more than one + * partner info then hvcsd->p_* will hold the last partner info data from the + * firmware query. A good way to update this code would be to replace the three + * partner info fields in hvcs_struct with a list of hvcs_partner_info + * instances. + * + * This function must be called with the hvcsd->lock held. + */ +static int hvcs_get_pi(struct hvcs_struct *hvcsd) +{ + struct hvcs_partner_info *pi; + uint32_t unit_address = hvcsd->vdev->unit_address; + struct list_head head; + int retval; + + spin_lock(&hvcs_pi_lock); + if (!hvcs_pi_buff) { + spin_unlock(&hvcs_pi_lock); + return -EFAULT; + } + retval = hvcs_get_partner_info(unit_address, &head, hvcs_pi_buff); + spin_unlock(&hvcs_pi_lock); + if (retval) { + printk(KERN_ERR "HVCS: Failed to fetch partner" + " info for vty-server@%x.\n", unit_address); + return retval; + } + + /* nixes the values if the partner vty went away */ + hvcsd->p_unit_address = 0; + hvcsd->p_partition_ID = 0; + + list_for_each_entry(pi, &head, node) + hvcs_set_pi(pi, hvcsd); + + hvcs_free_partner_info(&head); + return 0; +} + +/* + * This function is executed by the driver "rescan" sysfs entry. It shouldn't + * be executed elsewhere, in order to prevent deadlock issues. + */ +static int hvcs_rescan_devices_list(void) +{ + struct hvcs_struct *hvcsd; + unsigned long flags; + + spin_lock(&hvcs_structs_lock); + + list_for_each_entry(hvcsd, &hvcs_structs, next) { + spin_lock_irqsave(&hvcsd->lock, flags); + hvcs_get_pi(hvcsd); + spin_unlock_irqrestore(&hvcsd->lock, flags); + } + + spin_unlock(&hvcs_structs_lock); + + return 0; +} + +/* + * Farm this off into its own function because it could be more complex once + * multiple partners support is added. This function should be called with + * the hvcsd->lock held. + */ +static int hvcs_has_pi(struct hvcs_struct *hvcsd) +{ + if ((!hvcsd->p_unit_address) || (!hvcsd->p_partition_ID)) + return 0; + return 1; +} + +/* + * NOTE: It is possible that the super admin removed a partner vty and then + * added a different vty as the new partner. + * + * This function must be called with the hvcsd->lock held. + */ +static int hvcs_partner_connect(struct hvcs_struct *hvcsd) +{ + int retval; + unsigned int unit_address = hvcsd->vdev->unit_address; + + /* + * If there wasn't any pi when the device was added it doesn't meant + * there isn't any now. This driver isn't notified when a new partner + * vty is added to a vty-server so we discover changes on our own. + * Please see comments in hvcs_register_connection() for justification + * of this bizarre code. + */ + retval = hvcs_register_connection(unit_address, + hvcsd->p_partition_ID, + hvcsd->p_unit_address); + if (!retval) { + hvcsd->connected = 1; + return 0; + } else if (retval != -EINVAL) + return retval; + + /* + * As per the spec re-get the pi and try again if -EINVAL after the + * first connection attempt. + */ + if (hvcs_get_pi(hvcsd)) + return -ENOMEM; + + if (!hvcs_has_pi(hvcsd)) + return -ENODEV; + + retval = hvcs_register_connection(unit_address, + hvcsd->p_partition_ID, + hvcsd->p_unit_address); + if (retval != -EINVAL) { + hvcsd->connected = 1; + return retval; + } + + /* + * EBUSY is the most likely scenario though the vty could have been + * removed or there really could be an hcall error due to the parameter + * data but thanks to ambiguous firmware return codes we can't really + * tell. + */ + printk(KERN_INFO "HVCS: vty-server or partner" + " vty is busy. Try again later.\n"); + return -EBUSY; +} + +/* This function must be called with the hvcsd->lock held */ +static void hvcs_partner_free(struct hvcs_struct *hvcsd) +{ + int retval; + do { + retval = hvcs_free_connection(hvcsd->vdev->unit_address); + } while (retval == -EBUSY); + hvcsd->connected = 0; +} + +/* This helper function must be called WITHOUT the hvcsd->lock held */ +static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, + unsigned int irq, struct vio_dev *vdev) +{ + unsigned long flags; + int rc; + + /* + * It is possible that the vty-server was removed between the time that + * the conn was registered and now. + */ + if (!(rc = request_irq(irq, &hvcs_handle_interrupt, + IRQF_DISABLED, "ibmhvcs", hvcsd))) { + /* + * It is possible the vty-server was removed after the irq was + * requested but before we have time to enable interrupts. + */ + if (vio_enable_interrupts(vdev) == H_SUCCESS) + return 0; + else { + printk(KERN_ERR "HVCS: int enable failed for" + " vty-server@%X.\n", unit_address); + free_irq(irq, hvcsd); + } + } else + printk(KERN_ERR "HVCS: irq req failed for" + " vty-server@%X.\n", unit_address); + + spin_lock_irqsave(&hvcsd->lock, flags); + hvcs_partner_free(hvcsd); + spin_unlock_irqrestore(&hvcsd->lock, flags); + + return rc; + +} + +/* + * This always increments the kref ref count if the call is successful. + * Please remember to dec when you are done with the instance. + * + * NOTICE: Do NOT hold either the hvcs_struct.lock or hvcs_structs_lock when + * calling this function or you will get deadlock. + */ +static struct hvcs_struct *hvcs_get_by_index(int index) +{ + struct hvcs_struct *hvcsd = NULL; + unsigned long flags; + + spin_lock(&hvcs_structs_lock); + /* We can immediately discard OOB requests */ + if (index >= 0 && index < HVCS_MAX_SERVER_ADAPTERS) { + list_for_each_entry(hvcsd, &hvcs_structs, next) { + spin_lock_irqsave(&hvcsd->lock, flags); + if (hvcsd->index == index) { + kref_get(&hvcsd->kref); + spin_unlock_irqrestore(&hvcsd->lock, flags); + spin_unlock(&hvcs_structs_lock); + return hvcsd; + } + spin_unlock_irqrestore(&hvcsd->lock, flags); + } + hvcsd = NULL; + } + + spin_unlock(&hvcs_structs_lock); + return hvcsd; +} + +/* + * This is invoked via the tty_open interface when a user app connects to the + * /dev node. + */ +static int hvcs_open(struct tty_struct *tty, struct file *filp) +{ + struct hvcs_struct *hvcsd; + int rc, retval = 0; + unsigned long flags; + unsigned int irq; + struct vio_dev *vdev; + unsigned long unit_address; + + if (tty->driver_data) + goto fast_open; + + /* + * Is there a vty-server that shares the same index? + * This function increments the kref index. + */ + if (!(hvcsd = hvcs_get_by_index(tty->index))) { + printk(KERN_WARNING "HVCS: open failed, no device associated" + " with tty->index %d.\n", tty->index); + return -ENODEV; + } + + spin_lock_irqsave(&hvcsd->lock, flags); + + if (hvcsd->connected == 0) + if ((retval = hvcs_partner_connect(hvcsd))) + goto error_release; + + hvcsd->open_count = 1; + hvcsd->tty = tty; + tty->driver_data = hvcsd; + + memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); + + /* + * Save these in the spinlock for the enable operations that need them + * outside of the spinlock. + */ + irq = hvcsd->vdev->irq; + vdev = hvcsd->vdev; + unit_address = hvcsd->vdev->unit_address; + + hvcsd->todo_mask |= HVCS_SCHED_READ; + spin_unlock_irqrestore(&hvcsd->lock, flags); + + /* + * This must be done outside of the spinlock because it requests irqs + * and will grab the spinlock and free the connection if it fails. + */ + if (((rc = hvcs_enable_device(hvcsd, unit_address, irq, vdev)))) { + kref_put(&hvcsd->kref, destroy_hvcs_struct); + printk(KERN_WARNING "HVCS: enable device failed.\n"); + return rc; + } + + goto open_success; + +fast_open: + hvcsd = tty->driver_data; + + spin_lock_irqsave(&hvcsd->lock, flags); + kref_get(&hvcsd->kref); + hvcsd->open_count++; + hvcsd->todo_mask |= HVCS_SCHED_READ; + spin_unlock_irqrestore(&hvcsd->lock, flags); + +open_success: + hvcs_kick(); + + printk(KERN_INFO "HVCS: vty-server@%X connection opened.\n", + hvcsd->vdev->unit_address ); + + return 0; + +error_release: + spin_unlock_irqrestore(&hvcsd->lock, flags); + kref_put(&hvcsd->kref, destroy_hvcs_struct); + + printk(KERN_WARNING "HVCS: partner connect failed.\n"); + return retval; +} + +static void hvcs_close(struct tty_struct *tty, struct file *filp) +{ + struct hvcs_struct *hvcsd; + unsigned long flags; + int irq = NO_IRQ; + + /* + * Is someone trying to close the file associated with this device after + * we have hung up? If so tty->driver_data wouldn't be valid. + */ + if (tty_hung_up_p(filp)) + return; + + /* + * No driver_data means that this close was probably issued after a + * failed hvcs_open by the tty layer's release_dev() api and we can just + * exit cleanly. + */ + if (!tty->driver_data) + return; + + hvcsd = tty->driver_data; + + spin_lock_irqsave(&hvcsd->lock, flags); + if (--hvcsd->open_count == 0) { + + vio_disable_interrupts(hvcsd->vdev); + + /* + * NULL this early so that the kernel_thread doesn't try to + * execute any operations on the TTY even though it is obligated + * to deliver any pending I/O to the hypervisor. + */ + hvcsd->tty = NULL; + + irq = hvcsd->vdev->irq; + spin_unlock_irqrestore(&hvcsd->lock, flags); + + tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); + + /* + * This line is important because it tells hvcs_open that this + * device needs to be re-configured the next time hvcs_open is + * called. + */ + tty->driver_data = NULL; + + free_irq(irq, hvcsd); + kref_put(&hvcsd->kref, destroy_hvcs_struct); + return; + } else if (hvcsd->open_count < 0) { + printk(KERN_ERR "HVCS: vty-server@%X open_count: %d" + " is missmanaged.\n", + hvcsd->vdev->unit_address, hvcsd->open_count); + } + + spin_unlock_irqrestore(&hvcsd->lock, flags); + kref_put(&hvcsd->kref, destroy_hvcs_struct); +} + +static void hvcs_hangup(struct tty_struct * tty) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + unsigned long flags; + int temp_open_count; + int irq = NO_IRQ; + + spin_lock_irqsave(&hvcsd->lock, flags); + /* Preserve this so that we know how many kref refs to put */ + temp_open_count = hvcsd->open_count; + + /* + * Don't kref put inside the spinlock because the destruction + * callback may use the spinlock and it may get called before the + * spinlock has been released. + */ + vio_disable_interrupts(hvcsd->vdev); + + hvcsd->todo_mask = 0; + + /* I don't think the tty needs the hvcs_struct pointer after a hangup */ + hvcsd->tty->driver_data = NULL; + hvcsd->tty = NULL; + + hvcsd->open_count = 0; + + /* This will drop any buffered data on the floor which is OK in a hangup + * scenario. */ + memset(&hvcsd->buffer[0], 0x00, HVCS_BUFF_LEN); + hvcsd->chars_in_buffer = 0; + + irq = hvcsd->vdev->irq; + + spin_unlock_irqrestore(&hvcsd->lock, flags); + + free_irq(irq, hvcsd); + + /* + * We need to kref_put() for every open_count we have since the + * tty_hangup() function doesn't invoke a close per open connection on a + * non-console device. + */ + while(temp_open_count) { + --temp_open_count; + /* + * The final put will trigger destruction of the hvcs_struct. + * NOTE: If this hangup was signaled from user space then the + * final put will never happen. + */ + kref_put(&hvcsd->kref, destroy_hvcs_struct); + } +} + +/* + * NOTE: This is almost always from_user since user level apps interact with the + * /dev nodes. I'm trusting that if hvcs_write gets called and interrupted by + * hvcs_remove (which removes the target device and executes tty_hangup()) that + * tty_hangup will allow hvcs_write time to complete execution before it + * terminates our device. + */ +static int hvcs_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + unsigned int unit_address; + const unsigned char *charbuf; + unsigned long flags; + int total_sent = 0; + int tosend = 0; + int result = 0; + + /* + * If they don't check the return code off of their open they may + * attempt this even if there is no connected device. + */ + if (!hvcsd) + return -ENODEV; + + /* Reasonable size to prevent user level flooding */ + if (count > HVCS_MAX_FROM_USER) { + printk(KERN_WARNING "HVCS write: count being truncated to" + " HVCS_MAX_FROM_USER.\n"); + count = HVCS_MAX_FROM_USER; + } + + charbuf = buf; + + spin_lock_irqsave(&hvcsd->lock, flags); + + /* + * Somehow an open succedded but the device was removed or the + * connection terminated between the vty-server and partner vty during + * the middle of a write operation? This is a crummy place to do this + * but we want to keep it all in the spinlock. + */ + if (hvcsd->open_count <= 0) { + spin_unlock_irqrestore(&hvcsd->lock, flags); + return -ENODEV; + } + + unit_address = hvcsd->vdev->unit_address; + + while (count > 0) { + tosend = min(count, (HVCS_BUFF_LEN - hvcsd->chars_in_buffer)); + /* + * No more space, this probably means that the last call to + * hvcs_write() didn't succeed and the buffer was filled up. + */ + if (!tosend) + break; + + memcpy(&hvcsd->buffer[hvcsd->chars_in_buffer], + &charbuf[total_sent], + tosend); + + hvcsd->chars_in_buffer += tosend; + + result = 0; + + /* + * If this is true then we don't want to try writing to the + * hypervisor because that is the kernel_threads job now. We'll + * just add to the buffer. + */ + if (!(hvcsd->todo_mask & HVCS_TRY_WRITE)) + /* won't send partial writes */ + result = hvc_put_chars(unit_address, + &hvcsd->buffer[0], + hvcsd->chars_in_buffer); + + /* + * Since we know we have enough room in hvcsd->buffer for + * tosend we record that it was sent regardless of whether the + * hypervisor actually took it because we have it buffered. + */ + total_sent+=tosend; + count-=tosend; + if (result == 0) { + hvcsd->todo_mask |= HVCS_TRY_WRITE; + hvcs_kick(); + break; + } + + hvcsd->chars_in_buffer = 0; + /* + * Test after the chars_in_buffer reset otherwise this could + * deadlock our writes if hvc_put_chars fails. + */ + if (result < 0) + break; + } + + spin_unlock_irqrestore(&hvcsd->lock, flags); + + if (result == -1) + return -EIO; + else + return total_sent; +} + +/* + * This is really asking how much can we guarentee that we can send or that we + * absolutely WILL BUFFER if we can't send it. This driver MUST honor the + * return value, hence the reason for hvcs_struct buffering. + */ +static int hvcs_write_room(struct tty_struct *tty) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + + if (!hvcsd || hvcsd->open_count <= 0) + return 0; + + return HVCS_BUFF_LEN - hvcsd->chars_in_buffer; +} + +static int hvcs_chars_in_buffer(struct tty_struct *tty) +{ + struct hvcs_struct *hvcsd = tty->driver_data; + + return hvcsd->chars_in_buffer; +} + +static const struct tty_operations hvcs_ops = { + .open = hvcs_open, + .close = hvcs_close, + .hangup = hvcs_hangup, + .write = hvcs_write, + .write_room = hvcs_write_room, + .chars_in_buffer = hvcs_chars_in_buffer, + .unthrottle = hvcs_unthrottle, + .throttle = hvcs_throttle, +}; + +static int hvcs_alloc_index_list(int n) +{ + int i; + + hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL); + if (!hvcs_index_list) + return -ENOMEM; + hvcs_index_count = n; + for (i = 0; i < hvcs_index_count; i++) + hvcs_index_list[i] = -1; + return 0; +} + +static void hvcs_free_index_list(void) +{ + /* Paranoia check to be thorough. */ + kfree(hvcs_index_list); + hvcs_index_list = NULL; + hvcs_index_count = 0; +} + +static int __init hvcs_module_init(void) +{ + int rc; + int num_ttys_to_alloc; + + printk(KERN_INFO "Initializing %s\n", hvcs_driver_string); + + /* Has the user specified an overload with an insmod param? */ + if (hvcs_parm_num_devs <= 0 || + (hvcs_parm_num_devs > HVCS_MAX_SERVER_ADAPTERS)) { + num_ttys_to_alloc = HVCS_DEFAULT_SERVER_ADAPTERS; + } else + num_ttys_to_alloc = hvcs_parm_num_devs; + + hvcs_tty_driver = alloc_tty_driver(num_ttys_to_alloc); + if (!hvcs_tty_driver) + return -ENOMEM; + + if (hvcs_alloc_index_list(num_ttys_to_alloc)) { + rc = -ENOMEM; + goto index_fail; + } + + hvcs_tty_driver->owner = THIS_MODULE; + + hvcs_tty_driver->driver_name = hvcs_driver_name; + hvcs_tty_driver->name = hvcs_device_node; + + /* + * We'll let the system assign us a major number, indicated by leaving + * it blank. + */ + + hvcs_tty_driver->minor_start = HVCS_MINOR_START; + hvcs_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; + + /* + * We role our own so that we DONT ECHO. We can't echo because the + * device we are connecting to already echoes by default and this would + * throw us into a horrible recursive echo-echo-echo loop. + */ + hvcs_tty_driver->init_termios = hvcs_tty_termios; + hvcs_tty_driver->flags = TTY_DRIVER_REAL_RAW; + + tty_set_operations(hvcs_tty_driver, &hvcs_ops); + + /* + * The following call will result in sysfs entries that denote the + * dynamically assigned major and minor numbers for our devices. + */ + if (tty_register_driver(hvcs_tty_driver)) { + printk(KERN_ERR "HVCS: registration as a tty driver failed.\n"); + rc = -EIO; + goto register_fail; + } + + hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!hvcs_pi_buff) { + rc = -ENOMEM; + goto buff_alloc_fail; + } + + hvcs_task = kthread_run(khvcsd, NULL, "khvcsd"); + if (IS_ERR(hvcs_task)) { + printk(KERN_ERR "HVCS: khvcsd creation failed. Driver not loaded.\n"); + rc = -EIO; + goto kthread_fail; + } + + rc = vio_register_driver(&hvcs_vio_driver); + if (rc) { + printk(KERN_ERR "HVCS: can't register vio driver\n"); + goto vio_fail; + } + + /* + * This needs to be done AFTER the vio_register_driver() call or else + * the kobjects won't be initialized properly. + */ + rc = driver_create_file(&(hvcs_vio_driver.driver), &driver_attr_rescan); + if (rc) { + printk(KERN_ERR "HVCS: sysfs attr create failed\n"); + goto attr_fail; + } + + printk(KERN_INFO "HVCS: driver module inserted.\n"); + + return 0; + +attr_fail: + vio_unregister_driver(&hvcs_vio_driver); +vio_fail: + kthread_stop(hvcs_task); +kthread_fail: + kfree(hvcs_pi_buff); +buff_alloc_fail: + tty_unregister_driver(hvcs_tty_driver); +register_fail: + hvcs_free_index_list(); +index_fail: + put_tty_driver(hvcs_tty_driver); + hvcs_tty_driver = NULL; + return rc; +} + +static void __exit hvcs_module_exit(void) +{ + /* + * This driver receives hvcs_remove callbacks for each device upon + * module removal. + */ + + /* + * This synchronous operation will wake the khvcsd kthread if it is + * asleep and will return when khvcsd has terminated. + */ + kthread_stop(hvcs_task); + + spin_lock(&hvcs_pi_lock); + kfree(hvcs_pi_buff); + hvcs_pi_buff = NULL; + spin_unlock(&hvcs_pi_lock); + + driver_remove_file(&hvcs_vio_driver.driver, &driver_attr_rescan); + + vio_unregister_driver(&hvcs_vio_driver); + + tty_unregister_driver(hvcs_tty_driver); + + hvcs_free_index_list(); + + put_tty_driver(hvcs_tty_driver); + + printk(KERN_INFO "HVCS: driver module removed.\n"); +} + +module_init(hvcs_module_init); +module_exit(hvcs_module_exit); diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c new file mode 100644 index 000000000000..67a75a502c01 --- /dev/null +++ b/drivers/tty/hvc/hvsi.c @@ -0,0 +1,1314 @@ +/* + * Copyright (C) 2004 Hollis Blanchard , IBM + * + * 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 + */ + +/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS + * and the service processor on IBM pSeries servers. On these servers, there + * are no serial ports under the OS's control, and sometimes there is no other + * console available either. However, the service processor has two standard + * serial ports, so this over-complicated protocol allows the OS to control + * those ports by proxy. + * + * Besides data, the procotol supports the reading/writing of the serial + * port's DTR line, and the reading of the CD line. This is to allow the OS to + * control a modem attached to the service processor's serial port. Note that + * the OS cannot change the speed of the port through this protocol. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HVSI_MAJOR 229 +#define HVSI_MINOR 128 +#define MAX_NR_HVSI_CONSOLES 4 + +#define HVSI_TIMEOUT (5*HZ) +#define HVSI_VERSION 1 +#define HVSI_MAX_PACKET 256 +#define HVSI_MAX_READ 16 +#define HVSI_MAX_OUTGOING_DATA 12 +#define N_OUTBUF 12 + +/* + * we pass data via two 8-byte registers, so we would like our char arrays + * properly aligned for those loads. + */ +#define __ALIGNED__ __attribute__((__aligned__(sizeof(long)))) + +struct hvsi_struct { + struct delayed_work writer; + struct work_struct handshaker; + wait_queue_head_t emptyq; /* woken when outbuf is emptied */ + wait_queue_head_t stateq; /* woken when HVSI state changes */ + spinlock_t lock; + int index; + struct tty_struct *tty; + int count; + uint8_t throttle_buf[128]; + uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ + /* inbuf is for packet reassembly. leave a little room for leftovers. */ + uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ]; + uint8_t *inbuf_end; + int n_throttle; + int n_outbuf; + uint32_t vtermno; + uint32_t virq; + atomic_t seqno; /* HVSI packet sequence number */ + uint16_t mctrl; + uint8_t state; /* HVSI protocol state */ + uint8_t flags; +#ifdef CONFIG_MAGIC_SYSRQ + uint8_t sysrq; +#endif /* CONFIG_MAGIC_SYSRQ */ +}; +static struct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES]; + +static struct tty_driver *hvsi_driver; +static int hvsi_count; +static int (*hvsi_wait)(struct hvsi_struct *hp, int state); + +enum HVSI_PROTOCOL_STATE { + HVSI_CLOSED, + HVSI_WAIT_FOR_VER_RESPONSE, + HVSI_WAIT_FOR_VER_QUERY, + HVSI_OPEN, + HVSI_WAIT_FOR_MCTRL_RESPONSE, + HVSI_FSP_DIED, +}; +#define HVSI_CONSOLE 0x1 + +#define VS_DATA_PACKET_HEADER 0xff +#define VS_CONTROL_PACKET_HEADER 0xfe +#define VS_QUERY_PACKET_HEADER 0xfd +#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc + +/* control verbs */ +#define VSV_SET_MODEM_CTL 1 /* to service processor only */ +#define VSV_MODEM_CTL_UPDATE 2 /* from service processor only */ +#define VSV_CLOSE_PROTOCOL 3 + +/* query verbs */ +#define VSV_SEND_VERSION_NUMBER 1 +#define VSV_SEND_MODEM_CTL_STATUS 2 + +/* yes, these masks are not consecutive. */ +#define HVSI_TSDTR 0x01 +#define HVSI_TSCD 0x20 + +struct hvsi_header { + uint8_t type; + uint8_t len; + uint16_t seqno; +} __attribute__((packed)); + +struct hvsi_data { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint8_t data[HVSI_MAX_OUTGOING_DATA]; +} __attribute__((packed)); + +struct hvsi_control { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; + /* optional depending on verb: */ + uint32_t word; + uint32_t mask; +} __attribute__((packed)); + +struct hvsi_query { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; +} __attribute__((packed)); + +struct hvsi_query_response { + uint8_t type; + uint8_t len; + uint16_t seqno; + uint16_t verb; + uint16_t query_seqno; + union { + uint8_t version; + uint32_t mctrl_word; + } u; +} __attribute__((packed)); + + + +static inline int is_console(struct hvsi_struct *hp) +{ + return hp->flags & HVSI_CONSOLE; +} + +static inline int is_open(struct hvsi_struct *hp) +{ + /* if we're waiting for an mctrl then we're already open */ + return (hp->state == HVSI_OPEN) + || (hp->state == HVSI_WAIT_FOR_MCTRL_RESPONSE); +} + +static inline void print_state(struct hvsi_struct *hp) +{ +#ifdef DEBUG + static const char *state_names[] = { + "HVSI_CLOSED", + "HVSI_WAIT_FOR_VER_RESPONSE", + "HVSI_WAIT_FOR_VER_QUERY", + "HVSI_OPEN", + "HVSI_WAIT_FOR_MCTRL_RESPONSE", + "HVSI_FSP_DIED", + }; + const char *name = (hp->state < ARRAY_SIZE(state_names)) + ? state_names[hp->state] : "UNKNOWN"; + + pr_debug("hvsi%i: state = %s\n", hp->index, name); +#endif /* DEBUG */ +} + +static inline void __set_state(struct hvsi_struct *hp, int state) +{ + hp->state = state; + print_state(hp); + wake_up_all(&hp->stateq); +} + +static inline void set_state(struct hvsi_struct *hp, int state) +{ + unsigned long flags; + + spin_lock_irqsave(&hp->lock, flags); + __set_state(hp, state); + spin_unlock_irqrestore(&hp->lock, flags); +} + +static inline int len_packet(const uint8_t *packet) +{ + return (int)((struct hvsi_header *)packet)->len; +} + +static inline int is_header(const uint8_t *packet) +{ + struct hvsi_header *header = (struct hvsi_header *)packet; + return header->type >= VS_QUERY_RESPONSE_PACKET_HEADER; +} + +static inline int got_packet(const struct hvsi_struct *hp, uint8_t *packet) +{ + if (hp->inbuf_end < packet + sizeof(struct hvsi_header)) + return 0; /* don't even have the packet header */ + + if (hp->inbuf_end < (packet + len_packet(packet))) + return 0; /* don't have the rest of the packet */ + + return 1; +} + +/* shift remaining bytes in packetbuf down */ +static void compact_inbuf(struct hvsi_struct *hp, uint8_t *read_to) +{ + int remaining = (int)(hp->inbuf_end - read_to); + + pr_debug("%s: %i chars remain\n", __func__, remaining); + + if (read_to != hp->inbuf) + memmove(hp->inbuf, read_to, remaining); + + hp->inbuf_end = hp->inbuf + remaining; +} + +#ifdef DEBUG +#define dbg_dump_packet(packet) dump_packet(packet) +#define dbg_dump_hex(data, len) dump_hex(data, len) +#else +#define dbg_dump_packet(packet) do { } while (0) +#define dbg_dump_hex(data, len) do { } while (0) +#endif + +static void dump_hex(const uint8_t *data, int len) +{ + int i; + + printk(" "); + for (i=0; i < len; i++) + printk("%.2x", data[i]); + + printk("\n "); + for (i=0; i < len; i++) { + if (isprint(data[i])) + printk("%c", data[i]); + else + printk("."); + } + printk("\n"); +} + +static void dump_packet(uint8_t *packet) +{ + struct hvsi_header *header = (struct hvsi_header *)packet; + + printk("type 0x%x, len %i, seqno %i:\n", header->type, header->len, + header->seqno); + + dump_hex(packet, header->len); +} + +static int hvsi_read(struct hvsi_struct *hp, char *buf, int count) +{ + unsigned long got; + + got = hvc_get_chars(hp->vtermno, buf, count); + + return got; +} + +static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, + struct tty_struct **to_hangup, struct hvsi_struct **to_handshake) +{ + struct hvsi_control *header = (struct hvsi_control *)packet; + + switch (header->verb) { + case VSV_MODEM_CTL_UPDATE: + if ((header->word & HVSI_TSCD) == 0) { + /* CD went away; no more connection */ + pr_debug("hvsi%i: CD dropped\n", hp->index); + hp->mctrl &= TIOCM_CD; + /* If userland hasn't done an open(2) yet, hp->tty is NULL. */ + if (hp->tty && !(hp->tty->flags & CLOCAL)) + *to_hangup = hp->tty; + } + break; + case VSV_CLOSE_PROTOCOL: + pr_debug("hvsi%i: service processor came back\n", hp->index); + if (hp->state != HVSI_CLOSED) { + *to_handshake = hp; + } + break; + default: + printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ", + hp->index); + dump_packet(packet); + break; + } +} + +static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) +{ + struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; + + switch (hp->state) { + case HVSI_WAIT_FOR_VER_RESPONSE: + __set_state(hp, HVSI_WAIT_FOR_VER_QUERY); + break; + case HVSI_WAIT_FOR_MCTRL_RESPONSE: + hp->mctrl = 0; + if (resp->u.mctrl_word & HVSI_TSDTR) + hp->mctrl |= TIOCM_DTR; + if (resp->u.mctrl_word & HVSI_TSCD) + hp->mctrl |= TIOCM_CD; + __set_state(hp, HVSI_OPEN); + break; + default: + printk(KERN_ERR "hvsi%i: unexpected query response: ", hp->index); + dump_packet(packet); + break; + } +} + +/* respond to service processor's version query */ +static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) +{ + struct hvsi_query_response packet __ALIGNED__; + int wrote; + + packet.type = VS_QUERY_RESPONSE_PACKET_HEADER; + packet.len = sizeof(struct hvsi_query_response); + packet.seqno = atomic_inc_return(&hp->seqno); + packet.verb = VSV_SEND_VERSION_NUMBER; + packet.u.version = HVSI_VERSION; + packet.query_seqno = query_seqno+1; + + pr_debug("%s: sending %i bytes\n", __func__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (wrote != packet.len) { + printk(KERN_ERR "hvsi%i: couldn't send query response!\n", + hp->index); + return -EIO; + } + + return 0; +} + +static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) +{ + struct hvsi_query *query = (struct hvsi_query *)packet; + + switch (hp->state) { + case HVSI_WAIT_FOR_VER_QUERY: + hvsi_version_respond(hp, query->seqno); + __set_state(hp, HVSI_OPEN); + break; + default: + printk(KERN_ERR "hvsi%i: unexpected query: ", hp->index); + dump_packet(packet); + break; + } +} + +static void hvsi_insert_chars(struct hvsi_struct *hp, const char *buf, int len) +{ + int i; + + for (i=0; i < len; i++) { + char c = buf[i]; +#ifdef CONFIG_MAGIC_SYSRQ + if (c == '\0') { + hp->sysrq = 1; + continue; + } else if (hp->sysrq) { + handle_sysrq(c); + hp->sysrq = 0; + continue; + } +#endif /* CONFIG_MAGIC_SYSRQ */ + tty_insert_flip_char(hp->tty, c, 0); + } +} + +/* + * We could get 252 bytes of data at once here. But the tty layer only + * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow + * it. Accordingly we won't send more than 128 bytes at a time to the flip + * buffer, which will give the tty buffer a chance to throttle us. Should the + * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be + * revisited. + */ +#define TTY_THRESHOLD_THROTTLE 128 +static struct tty_struct *hvsi_recv_data(struct hvsi_struct *hp, + const uint8_t *packet) +{ + const struct hvsi_header *header = (const struct hvsi_header *)packet; + const uint8_t *data = packet + sizeof(struct hvsi_header); + int datalen = header->len - sizeof(struct hvsi_header); + int overflow = datalen - TTY_THRESHOLD_THROTTLE; + + pr_debug("queueing %i chars '%.*s'\n", datalen, datalen, data); + + if (datalen == 0) + return NULL; + + if (overflow > 0) { + pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __func__); + datalen = TTY_THRESHOLD_THROTTLE; + } + + hvsi_insert_chars(hp, data, datalen); + + if (overflow > 0) { + /* + * we still have more data to deliver, so we need to save off the + * overflow and send it later + */ + pr_debug("%s: deferring overflow\n", __func__); + memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow); + hp->n_throttle = overflow; + } + + return hp->tty; +} + +/* + * Returns true/false indicating data successfully read from hypervisor. + * Used both to get packets for tty connections and to advance the state + * machine during console handshaking (in which case tty = NULL and we ignore + * incoming data). + */ +static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip, + struct tty_struct **hangup, struct hvsi_struct **handshake) +{ + uint8_t *packet = hp->inbuf; + int chunklen; + + *flip = NULL; + *hangup = NULL; + *handshake = NULL; + + chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ); + if (chunklen == 0) { + pr_debug("%s: 0-length read\n", __func__); + return 0; + } + + pr_debug("%s: got %i bytes\n", __func__, chunklen); + dbg_dump_hex(hp->inbuf_end, chunklen); + + hp->inbuf_end += chunklen; + + /* handle all completed packets */ + while ((packet < hp->inbuf_end) && got_packet(hp, packet)) { + struct hvsi_header *header = (struct hvsi_header *)packet; + + if (!is_header(packet)) { + printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); + /* skip bytes until we find a header or run out of data */ + while ((packet < hp->inbuf_end) && (!is_header(packet))) + packet++; + continue; + } + + pr_debug("%s: handling %i-byte packet\n", __func__, + len_packet(packet)); + dbg_dump_packet(packet); + + switch (header->type) { + case VS_DATA_PACKET_HEADER: + if (!is_open(hp)) + break; + if (hp->tty == NULL) + break; /* no tty buffer to put data in */ + *flip = hvsi_recv_data(hp, packet); + break; + case VS_CONTROL_PACKET_HEADER: + hvsi_recv_control(hp, packet, hangup, handshake); + break; + case VS_QUERY_RESPONSE_PACKET_HEADER: + hvsi_recv_response(hp, packet); + break; + case VS_QUERY_PACKET_HEADER: + hvsi_recv_query(hp, packet); + break; + default: + printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n", + hp->index, header->type); + dump_packet(packet); + break; + } + + packet += len_packet(packet); + + if (*hangup || *handshake) { + pr_debug("%s: hangup or handshake\n", __func__); + /* + * we need to send the hangup now before receiving any more data. + * If we get "data, hangup, data", we can't deliver the second + * data before the hangup. + */ + break; + } + } + + compact_inbuf(hp, packet); + + return 1; +} + +static void hvsi_send_overflow(struct hvsi_struct *hp) +{ + pr_debug("%s: delivering %i bytes overflow\n", __func__, + hp->n_throttle); + + hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle); + hp->n_throttle = 0; +} + +/* + * must get all pending data because we only get an irq on empty->non-empty + * transition + */ +static irqreturn_t hvsi_interrupt(int irq, void *arg) +{ + struct hvsi_struct *hp = (struct hvsi_struct *)arg; + struct tty_struct *flip; + struct tty_struct *hangup; + struct hvsi_struct *handshake; + unsigned long flags; + int again = 1; + + pr_debug("%s\n", __func__); + + while (again) { + spin_lock_irqsave(&hp->lock, flags); + again = hvsi_load_chunk(hp, &flip, &hangup, &handshake); + spin_unlock_irqrestore(&hp->lock, flags); + + /* + * we have to call tty_flip_buffer_push() and tty_hangup() outside our + * spinlock. But we also have to keep going until we've read all the + * available data. + */ + + if (flip) { + /* there was data put in the tty flip buffer */ + tty_flip_buffer_push(flip); + flip = NULL; + } + + if (hangup) { + tty_hangup(hangup); + } + + if (handshake) { + pr_debug("hvsi%i: attempting re-handshake\n", handshake->index); + schedule_work(&handshake->handshaker); + } + } + + spin_lock_irqsave(&hp->lock, flags); + if (hp->tty && hp->n_throttle + && (!test_bit(TTY_THROTTLED, &hp->tty->flags))) { + /* we weren't hung up and we weren't throttled, so we can deliver the + * rest now */ + flip = hp->tty; + hvsi_send_overflow(hp); + } + spin_unlock_irqrestore(&hp->lock, flags); + + if (flip) { + tty_flip_buffer_push(flip); + } + + return IRQ_HANDLED; +} + +/* for boot console, before the irq handler is running */ +static int __init poll_for_state(struct hvsi_struct *hp, int state) +{ + unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; + + for (;;) { + hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */ + + if (hp->state == state) + return 0; + + mdelay(5); + if (time_after(jiffies, end_jiffies)) + return -EIO; + } +} + +/* wait for irq handler to change our state */ +static int wait_for_state(struct hvsi_struct *hp, int state) +{ + int ret = 0; + + if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT)) + ret = -EIO; + + return ret; +} + +static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) +{ + struct hvsi_query packet __ALIGNED__; + int wrote; + + packet.type = VS_QUERY_PACKET_HEADER; + packet.len = sizeof(struct hvsi_query); + packet.seqno = atomic_inc_return(&hp->seqno); + packet.verb = verb; + + pr_debug("%s: sending %i bytes\n", __func__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (wrote != packet.len) { + printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index, + wrote); + return -EIO; + } + + return 0; +} + +static int hvsi_get_mctrl(struct hvsi_struct *hp) +{ + int ret; + + set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE); + hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS); + + ret = hvsi_wait(hp, HVSI_OPEN); + if (ret < 0) { + printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index); + set_state(hp, HVSI_OPEN); + return ret; + } + + pr_debug("%s: mctrl 0x%x\n", __func__, hp->mctrl); + + return 0; +} + +/* note that we can only set DTR */ +static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) +{ + struct hvsi_control packet __ALIGNED__; + int wrote; + + packet.type = VS_CONTROL_PACKET_HEADER, + packet.seqno = atomic_inc_return(&hp->seqno); + packet.len = sizeof(struct hvsi_control); + packet.verb = VSV_SET_MODEM_CTL; + packet.mask = HVSI_TSDTR; + + if (mctrl & TIOCM_DTR) + packet.word = HVSI_TSDTR; + + pr_debug("%s: sending %i bytes\n", __func__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (wrote != packet.len) { + printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index); + return -EIO; + } + + return 0; +} + +static void hvsi_drain_input(struct hvsi_struct *hp) +{ + uint8_t buf[HVSI_MAX_READ] __ALIGNED__; + unsigned long end_jiffies = jiffies + HVSI_TIMEOUT; + + while (time_before(end_jiffies, jiffies)) + if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) + break; +} + +static int hvsi_handshake(struct hvsi_struct *hp) +{ + int ret; + + /* + * We could have a CLOSE or other data waiting for us before we even try + * to open; try to throw it all away so we don't get confused. (CLOSE + * is the first message sent up the pipe when the FSP comes online. We + * need to distinguish between "it came up a while ago and we're the first + * user" and "it was just reset before it saw our handshake packet".) + */ + hvsi_drain_input(hp); + + set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE); + ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); + if (ret < 0) { + printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); + return ret; + } + + ret = hvsi_wait(hp, HVSI_OPEN); + if (ret < 0) + return ret; + + return 0; +} + +static void hvsi_handshaker(struct work_struct *work) +{ + struct hvsi_struct *hp = + container_of(work, struct hvsi_struct, handshaker); + + if (hvsi_handshake(hp) >= 0) + return; + + printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); + if (is_console(hp)) { + /* + * ttys will re-attempt the handshake via hvsi_open, but + * the console will not. + */ + printk(KERN_ERR "hvsi%i: lost console!\n", hp->index); + } +} + +static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) +{ + struct hvsi_data packet __ALIGNED__; + int ret; + + BUG_ON(count > HVSI_MAX_OUTGOING_DATA); + + packet.type = VS_DATA_PACKET_HEADER; + packet.seqno = atomic_inc_return(&hp->seqno); + packet.len = count + sizeof(struct hvsi_header); + memcpy(&packet.data, buf, count); + + ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); + if (ret == packet.len) { + /* return the number of chars written, not the packet length */ + return count; + } + return ret; /* return any errors */ +} + +static void hvsi_close_protocol(struct hvsi_struct *hp) +{ + struct hvsi_control packet __ALIGNED__; + + packet.type = VS_CONTROL_PACKET_HEADER; + packet.seqno = atomic_inc_return(&hp->seqno); + packet.len = 6; + packet.verb = VSV_CLOSE_PROTOCOL; + + pr_debug("%s: sending %i bytes\n", __func__, packet.len); + dbg_dump_hex((uint8_t*)&packet, packet.len); + + hvc_put_chars(hp->vtermno, (char *)&packet, packet.len); +} + +static int hvsi_open(struct tty_struct *tty, struct file *filp) +{ + struct hvsi_struct *hp; + unsigned long flags; + int line = tty->index; + int ret; + + pr_debug("%s\n", __func__); + + if (line < 0 || line >= hvsi_count) + return -ENODEV; + hp = &hvsi_ports[line]; + + tty->driver_data = hp; + + mb(); + if (hp->state == HVSI_FSP_DIED) + return -EIO; + + spin_lock_irqsave(&hp->lock, flags); + hp->tty = tty; + hp->count++; + atomic_set(&hp->seqno, 0); + h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); + spin_unlock_irqrestore(&hp->lock, flags); + + if (is_console(hp)) + return 0; /* this has already been handshaked as the console */ + + ret = hvsi_handshake(hp); + if (ret < 0) { + printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name); + return ret; + } + + ret = hvsi_get_mctrl(hp); + if (ret < 0) { + printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name); + return ret; + } + + ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); + if (ret < 0) { + printk(KERN_ERR "%s: couldn't set DTR\n", tty->name); + return ret; + } + + return 0; +} + +/* wait for hvsi_write_worker to empty hp->outbuf */ +static void hvsi_flush_output(struct hvsi_struct *hp) +{ + wait_event_timeout(hp->emptyq, (hp->n_outbuf <= 0), HVSI_TIMEOUT); + + /* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */ + cancel_delayed_work_sync(&hp->writer); + flush_work_sync(&hp->handshaker); + + /* + * it's also possible that our timeout expired and hvsi_write_worker + * didn't manage to push outbuf. poof. + */ + hp->n_outbuf = 0; +} + +static void hvsi_close(struct tty_struct *tty, struct file *filp) +{ + struct hvsi_struct *hp = tty->driver_data; + unsigned long flags; + + pr_debug("%s\n", __func__); + + if (tty_hung_up_p(filp)) + return; + + spin_lock_irqsave(&hp->lock, flags); + + if (--hp->count == 0) { + hp->tty = NULL; + hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ + + /* only close down connection if it is not the console */ + if (!is_console(hp)) { + h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ + __set_state(hp, HVSI_CLOSED); + /* + * any data delivered to the tty layer after this will be + * discarded (except for XON/XOFF) + */ + tty->closing = 1; + + spin_unlock_irqrestore(&hp->lock, flags); + + /* let any existing irq handlers finish. no more will start. */ + synchronize_irq(hp->virq); + + /* hvsi_write_worker will re-schedule until outbuf is empty. */ + hvsi_flush_output(hp); + + /* tell FSP to stop sending data */ + hvsi_close_protocol(hp); + + /* + * drain anything FSP is still in the middle of sending, and let + * hvsi_handshake drain the rest on the next open. + */ + hvsi_drain_input(hp); + + spin_lock_irqsave(&hp->lock, flags); + } + } else if (hp->count < 0) + printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", + hp - hvsi_ports, hp->count); + + spin_unlock_irqrestore(&hp->lock, flags); +} + +static void hvsi_hangup(struct tty_struct *tty) +{ + struct hvsi_struct *hp = tty->driver_data; + unsigned long flags; + + pr_debug("%s\n", __func__); + + spin_lock_irqsave(&hp->lock, flags); + + hp->count = 0; + hp->n_outbuf = 0; + hp->tty = NULL; + + spin_unlock_irqrestore(&hp->lock, flags); +} + +/* called with hp->lock held */ +static void hvsi_push(struct hvsi_struct *hp) +{ + int n; + + if (hp->n_outbuf <= 0) + return; + + n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); + if (n > 0) { + /* success */ + pr_debug("%s: wrote %i chars\n", __func__, n); + hp->n_outbuf = 0; + } else if (n == -EIO) { + __set_state(hp, HVSI_FSP_DIED); + printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); + } +} + +/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */ +static void hvsi_write_worker(struct work_struct *work) +{ + struct hvsi_struct *hp = + container_of(work, struct hvsi_struct, writer.work); + unsigned long flags; +#ifdef DEBUG + static long start_j = 0; + + if (start_j == 0) + start_j = jiffies; +#endif /* DEBUG */ + + spin_lock_irqsave(&hp->lock, flags); + + pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); + + if (!is_open(hp)) { + /* + * We could have a non-open connection if the service processor died + * while we were busily scheduling ourselves. In that case, it could + * be minutes before the service processor comes back, so only try + * again once a second. + */ + schedule_delayed_work(&hp->writer, HZ); + goto out; + } + + hvsi_push(hp); + if (hp->n_outbuf > 0) + schedule_delayed_work(&hp->writer, 10); + else { +#ifdef DEBUG + pr_debug("%s: outbuf emptied after %li jiffies\n", __func__, + jiffies - start_j); + start_j = 0; +#endif /* DEBUG */ + wake_up_all(&hp->emptyq); + tty_wakeup(hp->tty); + } + +out: + spin_unlock_irqrestore(&hp->lock, flags); +} + +static int hvsi_write_room(struct tty_struct *tty) +{ + struct hvsi_struct *hp = tty->driver_data; + + return N_OUTBUF - hp->n_outbuf; +} + +static int hvsi_chars_in_buffer(struct tty_struct *tty) +{ + struct hvsi_struct *hp = tty->driver_data; + + return hp->n_outbuf; +} + +static int hvsi_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct hvsi_struct *hp = tty->driver_data; + const char *source = buf; + unsigned long flags; + int total = 0; + int origcount = count; + + spin_lock_irqsave(&hp->lock, flags); + + pr_debug("%s: %i chars in buffer\n", __func__, hp->n_outbuf); + + if (!is_open(hp)) { + /* we're either closing or not yet open; don't accept data */ + pr_debug("%s: not open\n", __func__); + goto out; + } + + /* + * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf + * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls + * will see there is no room in outbuf and return. + */ + while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { + int chunksize = min(count, hvsi_write_room(hp->tty)); + + BUG_ON(hp->n_outbuf < 0); + memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); + hp->n_outbuf += chunksize; + + total += chunksize; + source += chunksize; + count -= chunksize; + hvsi_push(hp); + } + + if (hp->n_outbuf > 0) { + /* + * we weren't able to write it all to the hypervisor. + * schedule another push attempt. + */ + schedule_delayed_work(&hp->writer, 10); + } + +out: + spin_unlock_irqrestore(&hp->lock, flags); + + if (total != origcount) + pr_debug("%s: wanted %i, only wrote %i\n", __func__, origcount, + total); + + return total; +} + +/* + * I have never seen throttle or unthrottle called, so this little throttle + * buffering scheme may or may not work. + */ +static void hvsi_throttle(struct tty_struct *tty) +{ + struct hvsi_struct *hp = tty->driver_data; + + pr_debug("%s\n", __func__); + + h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); +} + +static void hvsi_unthrottle(struct tty_struct *tty) +{ + struct hvsi_struct *hp = tty->driver_data; + unsigned long flags; + int shouldflip = 0; + + pr_debug("%s\n", __func__); + + spin_lock_irqsave(&hp->lock, flags); + if (hp->n_throttle) { + hvsi_send_overflow(hp); + shouldflip = 1; + } + spin_unlock_irqrestore(&hp->lock, flags); + + if (shouldflip) + tty_flip_buffer_push(hp->tty); + + h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE); +} + +static int hvsi_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct hvsi_struct *hp = tty->driver_data; + + hvsi_get_mctrl(hp); + return hp->mctrl; +} + +static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct hvsi_struct *hp = tty->driver_data; + unsigned long flags; + uint16_t new_mctrl; + + /* we can only alter DTR */ + clear &= TIOCM_DTR; + set &= TIOCM_DTR; + + spin_lock_irqsave(&hp->lock, flags); + + new_mctrl = (hp->mctrl & ~clear) | set; + + if (hp->mctrl != new_mctrl) { + hvsi_set_mctrl(hp, new_mctrl); + hp->mctrl = new_mctrl; + } + spin_unlock_irqrestore(&hp->lock, flags); + + return 0; +} + + +static const struct tty_operations hvsi_ops = { + .open = hvsi_open, + .close = hvsi_close, + .write = hvsi_write, + .hangup = hvsi_hangup, + .write_room = hvsi_write_room, + .chars_in_buffer = hvsi_chars_in_buffer, + .throttle = hvsi_throttle, + .unthrottle = hvsi_unthrottle, + .tiocmget = hvsi_tiocmget, + .tiocmset = hvsi_tiocmset, +}; + +static int __init hvsi_init(void) +{ + int i; + + hvsi_driver = alloc_tty_driver(hvsi_count); + if (!hvsi_driver) + return -ENOMEM; + + hvsi_driver->owner = THIS_MODULE; + hvsi_driver->driver_name = "hvsi"; + hvsi_driver->name = "hvsi"; + hvsi_driver->major = HVSI_MAJOR; + hvsi_driver->minor_start = HVSI_MINOR; + hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; + hvsi_driver->init_termios = tty_std_termios; + hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + hvsi_driver->init_termios.c_ispeed = 9600; + hvsi_driver->init_termios.c_ospeed = 9600; + hvsi_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(hvsi_driver, &hvsi_ops); + + for (i=0; i < hvsi_count; i++) { + struct hvsi_struct *hp = &hvsi_ports[i]; + int ret = 1; + + ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); + if (ret) + printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", + hp->virq, ret); + } + hvsi_wait = wait_for_state; /* irqs active now */ + + if (tty_register_driver(hvsi_driver)) + panic("Couldn't register hvsi console driver\n"); + + printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); + + return 0; +} +device_initcall(hvsi_init); + +/***** console (not tty) code: *****/ + +static void hvsi_console_print(struct console *console, const char *buf, + unsigned int count) +{ + struct hvsi_struct *hp = &hvsi_ports[console->index]; + char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; + unsigned int i = 0, n = 0; + int ret, donecr = 0; + + mb(); + if (!is_open(hp)) + return; + + /* + * ugh, we have to translate LF -> CRLF ourselves, in place. + * copied from hvc_console.c: + */ + while (count > 0 || i > 0) { + if (count > 0 && i < sizeof(c)) { + if (buf[n] == '\n' && !donecr) { + c[i++] = '\r'; + donecr = 1; + } else { + c[i++] = buf[n++]; + donecr = 0; + --count; + } + } else { + ret = hvsi_put_chars(hp, c, i); + if (ret < 0) + i = 0; + i -= ret; + } + } +} + +static struct tty_driver *hvsi_console_device(struct console *console, + int *index) +{ + *index = console->index; + return hvsi_driver; +} + +static int __init hvsi_console_setup(struct console *console, char *options) +{ + struct hvsi_struct *hp; + int ret; + + if (console->index < 0 || console->index >= hvsi_count) + return -1; + hp = &hvsi_ports[console->index]; + + /* give the FSP a chance to change the baud rate when we re-open */ + hvsi_close_protocol(hp); + + ret = hvsi_handshake(hp); + if (ret < 0) + return ret; + + ret = hvsi_get_mctrl(hp); + if (ret < 0) + return ret; + + ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); + if (ret < 0) + return ret; + + hp->flags |= HVSI_CONSOLE; + + return 0; +} + +static struct console hvsi_console = { + .name = "hvsi", + .write = hvsi_console_print, + .device = hvsi_console_device, + .setup = hvsi_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static int __init hvsi_console_init(void) +{ + struct device_node *vty; + + hvsi_wait = poll_for_state; /* no irqs yet; must poll */ + + /* search device tree for vty nodes */ + for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); + vty != NULL; + vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { + struct hvsi_struct *hp; + const uint32_t *vtermno, *irq; + + vtermno = of_get_property(vty, "reg", NULL); + irq = of_get_property(vty, "interrupts", NULL); + if (!vtermno || !irq) + continue; + + if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { + of_node_put(vty); + break; + } + + hp = &hvsi_ports[hvsi_count]; + INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); + INIT_WORK(&hp->handshaker, hvsi_handshaker); + init_waitqueue_head(&hp->emptyq); + init_waitqueue_head(&hp->stateq); + spin_lock_init(&hp->lock); + hp->index = hvsi_count; + hp->inbuf_end = hp->inbuf; + hp->state = HVSI_CLOSED; + hp->vtermno = *vtermno; + hp->virq = irq_create_mapping(NULL, irq[0]); + if (hp->virq == NO_IRQ) { + printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", + __func__, irq[0]); + continue; + } + + hvsi_count++; + } + + if (hvsi_count) + register_console(&hvsi_console); + return 0; +} +console_initcall(hvsi_console_init); diff --git a/drivers/tty/hvc/virtio_console.c b/drivers/tty/hvc/virtio_console.c new file mode 100644 index 000000000000..896a2ced1d27 --- /dev/null +++ b/drivers/tty/hvc/virtio_console.c @@ -0,0 +1,1838 @@ +/* + * Copyright (C) 2006, 2007, 2009 Rusty Russell, IBM Corporation + * Copyright (C) 2009, 2010 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include "hvc_console.h" + +/* + * This is a global struct for storing common data for all the devices + * this driver handles. + * + * Mainly, it has a linked list for all the consoles in one place so + * that callbacks from hvc for get_chars(), put_chars() work properly + * across multiple devices and multiple ports per device. + */ +struct ports_driver_data { + /* Used for registering chardevs */ + struct class *class; + + /* Used for exporting per-port information to debugfs */ + struct dentry *debugfs_dir; + + /* List of all the devices we're handling */ + struct list_head portdevs; + + /* Number of devices this driver is handling */ + unsigned int index; + + /* + * This is used to keep track of the number of hvc consoles + * spawned by this driver. This number is given as the first + * argument to hvc_alloc(). To correctly map an initial + * console spawned via hvc_instantiate to the console being + * hooked up via hvc_alloc, we need to pass the same vtermno. + * + * We also just assume the first console being initialised was + * the first one that got used as the initial console. + */ + unsigned int next_vtermno; + + /* All the console devices handled by this driver */ + struct list_head consoles; +}; +static struct ports_driver_data pdrvdata; + +DEFINE_SPINLOCK(pdrvdata_lock); + +/* This struct holds information that's relevant only for console ports */ +struct console { + /* We'll place all consoles in a list in the pdrvdata struct */ + struct list_head list; + + /* The hvc device associated with this console port */ + struct hvc_struct *hvc; + + /* The size of the console */ + struct winsize ws; + + /* + * This number identifies the number that we used to register + * with hvc in hvc_instantiate() and hvc_alloc(); this is the + * number passed on by the hvc callbacks to us to + * differentiate between the other console ports handled by + * this driver + */ + u32 vtermno; +}; + +struct port_buffer { + char *buf; + + /* size of the buffer in *buf above */ + size_t size; + + /* used length of the buffer */ + size_t len; + /* offset in the buf from which to consume data */ + size_t offset; +}; + +/* + * This is a per-device struct that stores data common to all the + * ports for that device (vdev->priv). + */ +struct ports_device { + /* Next portdev in the list, head is in the pdrvdata struct */ + struct list_head list; + + /* + * Workqueue handlers where we process deferred work after + * notification + */ + struct work_struct control_work; + + struct list_head ports; + + /* To protect the list of ports */ + spinlock_t ports_lock; + + /* To protect the vq operations for the control channel */ + spinlock_t cvq_lock; + + /* The current config space is stored here */ + struct virtio_console_config config; + + /* The virtio device we're associated with */ + struct virtio_device *vdev; + + /* + * A couple of virtqueues for the control channel: one for + * guest->host transfers, one for host->guest transfers + */ + struct virtqueue *c_ivq, *c_ovq; + + /* Array of per-port IO virtqueues */ + struct virtqueue **in_vqs, **out_vqs; + + /* Used for numbering devices for sysfs and debugfs */ + unsigned int drv_index; + + /* Major number for this device. Ports will be created as minors. */ + int chr_major; +}; + +/* This struct holds the per-port data */ +struct port { + /* Next port in the list, head is in the ports_device */ + struct list_head list; + + /* Pointer to the parent virtio_console device */ + struct ports_device *portdev; + + /* The current buffer from which data has to be fed to readers */ + struct port_buffer *inbuf; + + /* + * To protect the operations on the in_vq associated with this + * port. Has to be a spinlock because it can be called from + * interrupt context (get_char()). + */ + spinlock_t inbuf_lock; + + /* Protect the operations on the out_vq. */ + spinlock_t outvq_lock; + + /* The IO vqs for this port */ + struct virtqueue *in_vq, *out_vq; + + /* File in the debugfs directory that exposes this port's information */ + struct dentry *debugfs_file; + + /* + * The entries in this struct will be valid if this port is + * hooked up to an hvc console + */ + struct console cons; + + /* Each port associates with a separate char device */ + struct cdev *cdev; + struct device *dev; + + /* Reference-counting to handle port hot-unplugs and file operations */ + struct kref kref; + + /* A waitqueue for poll() or blocking read operations */ + wait_queue_head_t waitqueue; + + /* The 'name' of the port that we expose via sysfs properties */ + char *name; + + /* We can notify apps of host connect / disconnect events via SIGIO */ + struct fasync_struct *async_queue; + + /* The 'id' to identify the port with the Host */ + u32 id; + + bool outvq_full; + + /* Is the host device open */ + bool host_connected; + + /* We should allow only one process to open a port */ + bool guest_connected; +}; + +/* This is the very early arch-specified put chars function. */ +static int (*early_put_chars)(u32, const char *, int); + +static struct port *find_port_by_vtermno(u32 vtermno) +{ + struct port *port; + struct console *cons; + unsigned long flags; + + spin_lock_irqsave(&pdrvdata_lock, flags); + list_for_each_entry(cons, &pdrvdata.consoles, list) { + if (cons->vtermno == vtermno) { + port = container_of(cons, struct port, cons); + goto out; + } + } + port = NULL; +out: + spin_unlock_irqrestore(&pdrvdata_lock, flags); + return port; +} + +static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev, + dev_t dev) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) + if (port->cdev->dev == dev) + goto out; + port = NULL; +out: + spin_unlock_irqrestore(&portdev->ports_lock, flags); + + return port; +} + +static struct port *find_port_by_devt(dev_t dev) +{ + struct ports_device *portdev; + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&pdrvdata_lock, flags); + list_for_each_entry(portdev, &pdrvdata.portdevs, list) { + port = find_port_by_devt_in_portdev(portdev, dev); + if (port) + goto out; + } + port = NULL; +out: + spin_unlock_irqrestore(&pdrvdata_lock, flags); + return port; +} + +static struct port *find_port_by_id(struct ports_device *portdev, u32 id) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) + if (port->id == id) + goto out; + port = NULL; +out: + spin_unlock_irqrestore(&portdev->ports_lock, flags); + + return port; +} + +static struct port *find_port_by_vq(struct ports_device *portdev, + struct virtqueue *vq) +{ + struct port *port; + unsigned long flags; + + spin_lock_irqsave(&portdev->ports_lock, flags); + list_for_each_entry(port, &portdev->ports, list) + if (port->in_vq == vq || port->out_vq == vq) + goto out; + port = NULL; +out: + spin_unlock_irqrestore(&portdev->ports_lock, flags); + return port; +} + +static bool is_console_port(struct port *port) +{ + if (port->cons.hvc) + return true; + return false; +} + +static inline bool use_multiport(struct ports_device *portdev) +{ + /* + * This condition can be true when put_chars is called from + * early_init + */ + if (!portdev->vdev) + return 0; + return portdev->vdev->features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); +} + +static void free_buf(struct port_buffer *buf) +{ + kfree(buf->buf); + kfree(buf); +} + +static struct port_buffer *alloc_buf(size_t buf_size) +{ + struct port_buffer *buf; + + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + goto fail; + buf->buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf->buf) + goto free_buf; + buf->len = 0; + buf->offset = 0; + buf->size = buf_size; + return buf; + +free_buf: + kfree(buf); +fail: + return NULL; +} + +/* Callers should take appropriate locks */ +static void *get_inbuf(struct port *port) +{ + struct port_buffer *buf; + struct virtqueue *vq; + unsigned int len; + + vq = port->in_vq; + buf = virtqueue_get_buf(vq, &len); + if (buf) { + buf->len = len; + buf->offset = 0; + } + return buf; +} + +/* + * Create a scatter-gather list representing our input buffer and put + * it in the queue. + * + * Callers should take appropriate locks. + */ +static int add_inbuf(struct virtqueue *vq, struct port_buffer *buf) +{ + struct scatterlist sg[1]; + int ret; + + sg_init_one(sg, buf->buf, buf->size); + + ret = virtqueue_add_buf(vq, sg, 0, 1, buf); + virtqueue_kick(vq); + return ret; +} + +/* Discard any unread data this port has. Callers lockers. */ +static void discard_port_data(struct port *port) +{ + struct port_buffer *buf; + struct virtqueue *vq; + unsigned int len; + int ret; + + vq = port->in_vq; + if (port->inbuf) + buf = port->inbuf; + else + buf = virtqueue_get_buf(vq, &len); + + ret = 0; + while (buf) { + if (add_inbuf(vq, buf) < 0) { + ret++; + free_buf(buf); + } + buf = virtqueue_get_buf(vq, &len); + } + port->inbuf = NULL; + if (ret) + dev_warn(port->dev, "Errors adding %d buffers back to vq\n", + ret); +} + +static bool port_has_data(struct port *port) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&port->inbuf_lock, flags); + if (port->inbuf) { + ret = true; + goto out; + } + port->inbuf = get_inbuf(port); + if (port->inbuf) { + ret = true; + goto out; + } + ret = false; +out: + spin_unlock_irqrestore(&port->inbuf_lock, flags); + return ret; +} + +static ssize_t __send_control_msg(struct ports_device *portdev, u32 port_id, + unsigned int event, unsigned int value) +{ + struct scatterlist sg[1]; + struct virtio_console_control cpkt; + struct virtqueue *vq; + unsigned int len; + + if (!use_multiport(portdev)) + return 0; + + cpkt.id = port_id; + cpkt.event = event; + cpkt.value = value; + + vq = portdev->c_ovq; + + sg_init_one(sg, &cpkt, sizeof(cpkt)); + if (virtqueue_add_buf(vq, sg, 1, 0, &cpkt) >= 0) { + virtqueue_kick(vq); + while (!virtqueue_get_buf(vq, &len)) + cpu_relax(); + } + return 0; +} + +static ssize_t send_control_msg(struct port *port, unsigned int event, + unsigned int value) +{ + /* Did the port get unplugged before userspace closed it? */ + if (port->portdev) + return __send_control_msg(port->portdev, port->id, event, value); + return 0; +} + +/* Callers must take the port->outvq_lock */ +static void reclaim_consumed_buffers(struct port *port) +{ + void *buf; + unsigned int len; + + while ((buf = virtqueue_get_buf(port->out_vq, &len))) { + kfree(buf); + port->outvq_full = false; + } +} + +static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count, + bool nonblock) +{ + struct scatterlist sg[1]; + struct virtqueue *out_vq; + ssize_t ret; + unsigned long flags; + unsigned int len; + + out_vq = port->out_vq; + + spin_lock_irqsave(&port->outvq_lock, flags); + + reclaim_consumed_buffers(port); + + sg_init_one(sg, in_buf, in_count); + ret = virtqueue_add_buf(out_vq, sg, 1, 0, in_buf); + + /* Tell Host to go! */ + virtqueue_kick(out_vq); + + if (ret < 0) { + in_count = 0; + goto done; + } + + if (ret == 0) + port->outvq_full = true; + + if (nonblock) + goto done; + + /* + * Wait till the host acknowledges it pushed out the data we + * sent. This is done for data from the hvc_console; the tty + * operations are performed with spinlocks held so we can't + * sleep here. An alternative would be to copy the data to a + * buffer and relax the spinning requirement. The downside is + * we need to kmalloc a GFP_ATOMIC buffer each time the + * console driver writes something out. + */ + while (!virtqueue_get_buf(out_vq, &len)) + cpu_relax(); +done: + spin_unlock_irqrestore(&port->outvq_lock, flags); + /* + * We're expected to return the amount of data we wrote -- all + * of it + */ + return in_count; +} + +/* + * Give out the data that's requested from the buffer that we have + * queued up. + */ +static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, + bool to_user) +{ + struct port_buffer *buf; + unsigned long flags; + + if (!out_count || !port_has_data(port)) + return 0; + + buf = port->inbuf; + out_count = min(out_count, buf->len - buf->offset); + + if (to_user) { + ssize_t ret; + + ret = copy_to_user(out_buf, buf->buf + buf->offset, out_count); + if (ret) + return -EFAULT; + } else { + memcpy(out_buf, buf->buf + buf->offset, out_count); + } + + buf->offset += out_count; + + if (buf->offset == buf->len) { + /* + * We're done using all the data in this buffer. + * Re-queue so that the Host can send us more data. + */ + spin_lock_irqsave(&port->inbuf_lock, flags); + port->inbuf = NULL; + + if (add_inbuf(port->in_vq, buf) < 0) + dev_warn(port->dev, "failed add_buf\n"); + + spin_unlock_irqrestore(&port->inbuf_lock, flags); + } + /* Return the number of bytes actually copied */ + return out_count; +} + +/* The condition that must be true for polling to end */ +static bool will_read_block(struct port *port) +{ + if (!port->guest_connected) { + /* Port got hot-unplugged. Let's exit. */ + return false; + } + return !port_has_data(port) && port->host_connected; +} + +static bool will_write_block(struct port *port) +{ + bool ret; + + if (!port->guest_connected) { + /* Port got hot-unplugged. Let's exit. */ + return false; + } + if (!port->host_connected) + return true; + + spin_lock_irq(&port->outvq_lock); + /* + * Check if the Host has consumed any buffers since we last + * sent data (this is only applicable for nonblocking ports). + */ + reclaim_consumed_buffers(port); + ret = port->outvq_full; + spin_unlock_irq(&port->outvq_lock); + + return ret; +} + +static ssize_t port_fops_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct port *port; + ssize_t ret; + + port = filp->private_data; + + if (!port_has_data(port)) { + /* + * If nothing's connected on the host just return 0 in + * case of list_empty; this tells the userspace app + * that there's no connection + */ + if (!port->host_connected) + return 0; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(port->waitqueue, + !will_read_block(port)); + if (ret < 0) + return ret; + } + /* Port got hot-unplugged. */ + if (!port->guest_connected) + return -ENODEV; + /* + * We could've received a disconnection message while we were + * waiting for more data. + * + * This check is not clubbed in the if() statement above as we + * might receive some data as well as the host could get + * disconnected after we got woken up from our wait. So we + * really want to give off whatever data we have and only then + * check for host_connected. + */ + if (!port_has_data(port) && !port->host_connected) + return 0; + + return fill_readbuf(port, ubuf, count, true); +} + +static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, + size_t count, loff_t *offp) +{ + struct port *port; + char *buf; + ssize_t ret; + bool nonblock; + + /* Userspace could be out to fool us */ + if (!count) + return 0; + + port = filp->private_data; + + nonblock = filp->f_flags & O_NONBLOCK; + + if (will_write_block(port)) { + if (nonblock) + return -EAGAIN; + + ret = wait_event_interruptible(port->waitqueue, + !will_write_block(port)); + if (ret < 0) + return ret; + } + /* Port got hot-unplugged. */ + if (!port->guest_connected) + return -ENODEV; + + count = min((size_t)(32 * 1024), count); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = copy_from_user(buf, ubuf, count); + if (ret) { + ret = -EFAULT; + goto free_buf; + } + + /* + * We now ask send_buf() to not spin for generic ports -- we + * can re-use the same code path that non-blocking file + * descriptors take for blocking file descriptors since the + * wait is already done and we're certain the write will go + * through to the host. + */ + nonblock = true; + ret = send_buf(port, buf, count, nonblock); + + if (nonblock && ret > 0) + goto out; + +free_buf: + kfree(buf); +out: + return ret; +} + +static unsigned int port_fops_poll(struct file *filp, poll_table *wait) +{ + struct port *port; + unsigned int ret; + + port = filp->private_data; + poll_wait(filp, &port->waitqueue, wait); + + if (!port->guest_connected) { + /* Port got unplugged */ + return POLLHUP; + } + ret = 0; + if (!will_read_block(port)) + ret |= POLLIN | POLLRDNORM; + if (!will_write_block(port)) + ret |= POLLOUT; + if (!port->host_connected) + ret |= POLLHUP; + + return ret; +} + +static void remove_port(struct kref *kref); + +static int port_fops_release(struct inode *inode, struct file *filp) +{ + struct port *port; + + port = filp->private_data; + + /* Notify host of port being closed */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + + spin_lock_irq(&port->inbuf_lock); + port->guest_connected = false; + + discard_port_data(port); + + spin_unlock_irq(&port->inbuf_lock); + + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + + /* + * Locks aren't necessary here as a port can't be opened after + * unplug, and if a port isn't unplugged, a kref would already + * exist for the port. Plus, taking ports_lock here would + * create a dependency on other locks taken by functions + * inside remove_port if we're the last holder of the port, + * creating many problems. + */ + kref_put(&port->kref, remove_port); + + return 0; +} + +static int port_fops_open(struct inode *inode, struct file *filp) +{ + struct cdev *cdev = inode->i_cdev; + struct port *port; + int ret; + + port = find_port_by_devt(cdev->dev); + filp->private_data = port; + + /* Prevent against a port getting hot-unplugged at the same time */ + spin_lock_irq(&port->portdev->ports_lock); + kref_get(&port->kref); + spin_unlock_irq(&port->portdev->ports_lock); + + /* + * Don't allow opening of console port devices -- that's done + * via /dev/hvc + */ + if (is_console_port(port)) { + ret = -ENXIO; + goto out; + } + + /* Allow only one process to open a particular port at a time */ + spin_lock_irq(&port->inbuf_lock); + if (port->guest_connected) { + spin_unlock_irq(&port->inbuf_lock); + ret = -EMFILE; + goto out; + } + + port->guest_connected = true; + spin_unlock_irq(&port->inbuf_lock); + + spin_lock_irq(&port->outvq_lock); + /* + * There might be a chance that we missed reclaiming a few + * buffers in the window of the port getting previously closed + * and opening now. + */ + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + + nonseekable_open(inode, filp); + + /* Notify host of port being opened */ + send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); + + return 0; +out: + kref_put(&port->kref, remove_port); + return ret; +} + +static int port_fops_fasync(int fd, struct file *filp, int mode) +{ + struct port *port; + + port = filp->private_data; + return fasync_helper(fd, filp, mode, &port->async_queue); +} + +/* + * The file operations that we support: programs in the guest can open + * a console device, read from it, write to it, poll for data and + * close it. The devices are at + * /dev/vportp + */ +static const struct file_operations port_fops = { + .owner = THIS_MODULE, + .open = port_fops_open, + .read = port_fops_read, + .write = port_fops_write, + .poll = port_fops_poll, + .release = port_fops_release, + .fasync = port_fops_fasync, + .llseek = no_llseek, +}; + +/* + * The put_chars() callback is pretty straightforward. + * + * We turn the characters into a scatter-gather list, add it to the + * output queue and then kick the Host. Then we sit here waiting for + * it to finish: inefficient in theory, but in practice + * implementations will do it immediately (lguest's Launcher does). + */ +static int put_chars(u32 vtermno, const char *buf, int count) +{ + struct port *port; + + if (unlikely(early_put_chars)) + return early_put_chars(vtermno, buf, count); + + port = find_port_by_vtermno(vtermno); + if (!port) + return -EPIPE; + + return send_buf(port, (void *)buf, count, false); +} + +/* + * get_chars() is the callback from the hvc_console infrastructure + * when an interrupt is received. + * + * We call out to fill_readbuf that gets us the required data from the + * buffers that are queued up. + */ +static int get_chars(u32 vtermno, char *buf, int count) +{ + struct port *port; + + /* If we've not set up the port yet, we have no input to give. */ + if (unlikely(early_put_chars)) + return 0; + + port = find_port_by_vtermno(vtermno); + if (!port) + return -EPIPE; + + /* If we don't have an input queue yet, we can't get input. */ + BUG_ON(!port->in_vq); + + return fill_readbuf(port, buf, count, false); +} + +static void resize_console(struct port *port) +{ + struct virtio_device *vdev; + + /* The port could have been hot-unplugged */ + if (!port || !is_console_port(port)) + return; + + vdev = port->portdev->vdev; + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) + hvc_resize(port->cons.hvc, port->cons.ws); +} + +/* We set the configuration at this point, since we now have a tty */ +static int notifier_add_vio(struct hvc_struct *hp, int data) +{ + struct port *port; + + port = find_port_by_vtermno(hp->vtermno); + if (!port) + return -EINVAL; + + hp->irq_requested = 1; + resize_console(port); + + return 0; +} + +static void notifier_del_vio(struct hvc_struct *hp, int data) +{ + hp->irq_requested = 0; +} + +/* The operations for console ports. */ +static const struct hv_ops hv_ops = { + .get_chars = get_chars, + .put_chars = put_chars, + .notifier_add = notifier_add_vio, + .notifier_del = notifier_del_vio, + .notifier_hangup = notifier_del_vio, +}; + +/* + * Console drivers are initialized very early so boot messages can go + * out, so we do things slightly differently from the generic virtio + * initialization of the net and block drivers. + * + * At this stage, the console is output-only. It's too early to set + * up a virtqueue, so we let the drivers do some boutique early-output + * thing. + */ +int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)) +{ + early_put_chars = put_chars; + return hvc_instantiate(0, 0, &hv_ops); +} + +int init_port_console(struct port *port) +{ + int ret; + + /* + * The Host's telling us this port is a console port. Hook it + * up with an hvc console. + * + * To set up and manage our virtual console, we call + * hvc_alloc(). + * + * The first argument of hvc_alloc() is the virtual console + * number. The second argument is the parameter for the + * notification mechanism (like irq number). We currently + * leave this as zero, virtqueues have implicit notifications. + * + * The third argument is a "struct hv_ops" containing the + * put_chars() get_chars(), notifier_add() and notifier_del() + * pointers. The final argument is the output buffer size: we + * can do any size, so we put PAGE_SIZE here. + */ + port->cons.vtermno = pdrvdata.next_vtermno; + + port->cons.hvc = hvc_alloc(port->cons.vtermno, 0, &hv_ops, PAGE_SIZE); + if (IS_ERR(port->cons.hvc)) { + ret = PTR_ERR(port->cons.hvc); + dev_err(port->dev, + "error %d allocating hvc for port\n", ret); + port->cons.hvc = NULL; + return ret; + } + spin_lock_irq(&pdrvdata_lock); + pdrvdata.next_vtermno++; + list_add_tail(&port->cons.list, &pdrvdata.consoles); + spin_unlock_irq(&pdrvdata_lock); + port->guest_connected = true; + + /* + * Start using the new console output if this is the first + * console to come up. + */ + if (early_put_chars) + early_put_chars = NULL; + + /* Notify host of port being opened */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); + + return 0; +} + +static ssize_t show_port_name(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + struct port *port; + + port = dev_get_drvdata(dev); + + return sprintf(buffer, "%s\n", port->name); +} + +static DEVICE_ATTR(name, S_IRUGO, show_port_name, NULL); + +static struct attribute *port_sysfs_entries[] = { + &dev_attr_name.attr, + NULL +}; + +static struct attribute_group port_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = port_sysfs_entries, +}; + +static int debugfs_open(struct inode *inode, struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static ssize_t debugfs_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *offp) +{ + struct port *port; + char *buf; + ssize_t ret, out_offset, out_count; + + out_count = 1024; + buf = kmalloc(out_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + port = filp->private_data; + out_offset = 0; + out_offset += snprintf(buf + out_offset, out_count, + "name: %s\n", port->name ? port->name : ""); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "guest_connected: %d\n", port->guest_connected); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "host_connected: %d\n", port->host_connected); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "outvq_full: %d\n", port->outvq_full); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "is_console: %s\n", + is_console_port(port) ? "yes" : "no"); + out_offset += snprintf(buf + out_offset, out_count - out_offset, + "console_vtermno: %u\n", port->cons.vtermno); + + ret = simple_read_from_buffer(ubuf, count, offp, buf, out_offset); + kfree(buf); + return ret; +} + +static const struct file_operations port_debugfs_ops = { + .owner = THIS_MODULE, + .open = debugfs_open, + .read = debugfs_read, +}; + +static void set_console_size(struct port *port, u16 rows, u16 cols) +{ + if (!port || !is_console_port(port)) + return; + + port->cons.ws.ws_row = rows; + port->cons.ws.ws_col = cols; +} + +static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) +{ + struct port_buffer *buf; + unsigned int nr_added_bufs; + int ret; + + nr_added_bufs = 0; + do { + buf = alloc_buf(PAGE_SIZE); + if (!buf) + break; + + spin_lock_irq(lock); + ret = add_inbuf(vq, buf); + if (ret < 0) { + spin_unlock_irq(lock); + free_buf(buf); + break; + } + nr_added_bufs++; + spin_unlock_irq(lock); + } while (ret > 0); + + return nr_added_bufs; +} + +static void send_sigio_to_port(struct port *port) +{ + if (port->async_queue && port->guest_connected) + kill_fasync(&port->async_queue, SIGIO, POLL_OUT); +} + +static int add_port(struct ports_device *portdev, u32 id) +{ + char debugfs_name[16]; + struct port *port; + struct port_buffer *buf; + dev_t devt; + unsigned int nr_added_bufs; + int err; + + port = kmalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + err = -ENOMEM; + goto fail; + } + kref_init(&port->kref); + + port->portdev = portdev; + port->id = id; + + port->name = NULL; + port->inbuf = NULL; + port->cons.hvc = NULL; + port->async_queue = NULL; + + port->cons.ws.ws_row = port->cons.ws.ws_col = 0; + + port->host_connected = port->guest_connected = false; + + port->outvq_full = false; + + port->in_vq = portdev->in_vqs[port->id]; + port->out_vq = portdev->out_vqs[port->id]; + + port->cdev = cdev_alloc(); + if (!port->cdev) { + dev_err(&port->portdev->vdev->dev, "Error allocating cdev\n"); + err = -ENOMEM; + goto free_port; + } + port->cdev->ops = &port_fops; + + devt = MKDEV(portdev->chr_major, id); + err = cdev_add(port->cdev, devt, 1); + if (err < 0) { + dev_err(&port->portdev->vdev->dev, + "Error %d adding cdev for port %u\n", err, id); + goto free_cdev; + } + port->dev = device_create(pdrvdata.class, &port->portdev->vdev->dev, + devt, port, "vport%up%u", + port->portdev->drv_index, id); + if (IS_ERR(port->dev)) { + err = PTR_ERR(port->dev); + dev_err(&port->portdev->vdev->dev, + "Error %d creating device for port %u\n", + err, id); + goto free_cdev; + } + + spin_lock_init(&port->inbuf_lock); + spin_lock_init(&port->outvq_lock); + init_waitqueue_head(&port->waitqueue); + + /* Fill the in_vq with buffers so the host can send us data. */ + nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); + if (!nr_added_bufs) { + dev_err(port->dev, "Error allocating inbufs\n"); + err = -ENOMEM; + goto free_device; + } + + /* + * If we're not using multiport support, this has to be a console port + */ + if (!use_multiport(port->portdev)) { + err = init_port_console(port); + if (err) + goto free_inbufs; + } + + spin_lock_irq(&portdev->ports_lock); + list_add_tail(&port->list, &port->portdev->ports); + spin_unlock_irq(&portdev->ports_lock); + + /* + * Tell the Host we're set so that it can send us various + * configuration parameters for this port (eg, port name, + * caching, whether this is a console port, etc.) + */ + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + + if (pdrvdata.debugfs_dir) { + /* + * Finally, create the debugfs file that we can use to + * inspect a port's state at any time + */ + sprintf(debugfs_name, "vport%up%u", + port->portdev->drv_index, id); + port->debugfs_file = debugfs_create_file(debugfs_name, 0444, + pdrvdata.debugfs_dir, + port, + &port_debugfs_ops); + } + return 0; + +free_inbufs: + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) + free_buf(buf); +free_device: + device_destroy(pdrvdata.class, port->dev->devt); +free_cdev: + cdev_del(port->cdev); +free_port: + kfree(port); +fail: + /* The host might want to notify management sw about port add failure */ + __send_control_msg(portdev, id, VIRTIO_CONSOLE_PORT_READY, 0); + return err; +} + +/* No users remain, remove all port-specific data. */ +static void remove_port(struct kref *kref) +{ + struct port *port; + + port = container_of(kref, struct port, kref); + + sysfs_remove_group(&port->dev->kobj, &port_attribute_group); + device_destroy(pdrvdata.class, port->dev->devt); + cdev_del(port->cdev); + + kfree(port->name); + + debugfs_remove(port->debugfs_file); + + kfree(port); +} + +/* + * Port got unplugged. Remove port from portdev's list and drop the + * kref reference. If no userspace has this port opened, it will + * result in immediate removal the port. + */ +static void unplug_port(struct port *port) +{ + struct port_buffer *buf; + + spin_lock_irq(&port->portdev->ports_lock); + list_del(&port->list); + spin_unlock_irq(&port->portdev->ports_lock); + + if (port->guest_connected) { + port->guest_connected = false; + port->host_connected = false; + wake_up_interruptible(&port->waitqueue); + + /* Let the app know the port is going down. */ + send_sigio_to_port(port); + } + + if (is_console_port(port)) { + spin_lock_irq(&pdrvdata_lock); + list_del(&port->cons.list); + spin_unlock_irq(&pdrvdata_lock); +#if 0 + /* + * hvc_remove() not called as removing one hvc port + * results in other hvc ports getting frozen. + * + * Once this is resolved in hvc, this functionality + * will be enabled. Till that is done, the -EPIPE + * return from get_chars() above will help + * hvc_console.c to clean up on ports we remove here. + */ + hvc_remove(port->cons.hvc); +#endif + } + + /* Remove unused data this port might have received. */ + discard_port_data(port); + + reclaim_consumed_buffers(port); + + /* Remove buffers we queued up for the Host to send us data in. */ + while ((buf = virtqueue_detach_unused_buf(port->in_vq))) + free_buf(buf); + + /* + * We should just assume the device itself has gone off -- + * else a close on an open port later will try to send out a + * control message. + */ + port->portdev = NULL; + + /* + * Locks around here are not necessary - a port can't be + * opened after we removed the port struct from ports_list + * above. + */ + kref_put(&port->kref, remove_port); +} + +/* Any private messages that the Host and Guest want to share */ +static void handle_control_message(struct ports_device *portdev, + struct port_buffer *buf) +{ + struct virtio_console_control *cpkt; + struct port *port; + size_t name_size; + int err; + + cpkt = (struct virtio_console_control *)(buf->buf + buf->offset); + + port = find_port_by_id(portdev, cpkt->id); + if (!port && cpkt->event != VIRTIO_CONSOLE_PORT_ADD) { + /* No valid header at start of buffer. Drop it. */ + dev_dbg(&portdev->vdev->dev, + "Invalid index %u in control packet\n", cpkt->id); + return; + } + + switch (cpkt->event) { + case VIRTIO_CONSOLE_PORT_ADD: + if (port) { + dev_dbg(&portdev->vdev->dev, + "Port %u already added\n", port->id); + send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1); + break; + } + if (cpkt->id >= portdev->config.max_nr_ports) { + dev_warn(&portdev->vdev->dev, + "Request for adding port with out-of-bound id %u, max. supported id: %u\n", + cpkt->id, portdev->config.max_nr_ports - 1); + break; + } + add_port(portdev, cpkt->id); + break; + case VIRTIO_CONSOLE_PORT_REMOVE: + unplug_port(port); + break; + case VIRTIO_CONSOLE_CONSOLE_PORT: + if (!cpkt->value) + break; + if (is_console_port(port)) + break; + + init_port_console(port); + /* + * Could remove the port here in case init fails - but + * have to notify the host first. + */ + break; + case VIRTIO_CONSOLE_RESIZE: { + struct { + __u16 rows; + __u16 cols; + } size; + + if (!is_console_port(port)) + break; + + memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt), + sizeof(size)); + set_console_size(port, size.rows, size.cols); + + port->cons.hvc->irq_requested = 1; + resize_console(port); + break; + } + case VIRTIO_CONSOLE_PORT_OPEN: + port->host_connected = cpkt->value; + wake_up_interruptible(&port->waitqueue); + /* + * If the host port got closed and the host had any + * unconsumed buffers, we'll be able to reclaim them + * now. + */ + spin_lock_irq(&port->outvq_lock); + reclaim_consumed_buffers(port); + spin_unlock_irq(&port->outvq_lock); + + /* + * If the guest is connected, it'll be interested in + * knowing the host connection state changed. + */ + send_sigio_to_port(port); + break; + case VIRTIO_CONSOLE_PORT_NAME: + /* + * Skip the size of the header and the cpkt to get the size + * of the name that was sent + */ + name_size = buf->len - buf->offset - sizeof(*cpkt) + 1; + + port->name = kmalloc(name_size, GFP_KERNEL); + if (!port->name) { + dev_err(port->dev, + "Not enough space to store port name\n"); + break; + } + strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), + name_size - 1); + port->name[name_size - 1] = 0; + + /* + * Since we only have one sysfs attribute, 'name', + * create it only if we have a name for the port. + */ + err = sysfs_create_group(&port->dev->kobj, + &port_attribute_group); + if (err) { + dev_err(port->dev, + "Error %d creating sysfs device attributes\n", + err); + } else { + /* + * Generate a udev event so that appropriate + * symlinks can be created based on udev + * rules. + */ + kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); + } + break; + } +} + +static void control_work_handler(struct work_struct *work) +{ + struct ports_device *portdev; + struct virtqueue *vq; + struct port_buffer *buf; + unsigned int len; + + portdev = container_of(work, struct ports_device, control_work); + vq = portdev->c_ivq; + + spin_lock(&portdev->cvq_lock); + while ((buf = virtqueue_get_buf(vq, &len))) { + spin_unlock(&portdev->cvq_lock); + + buf->len = len; + buf->offset = 0; + + handle_control_message(portdev, buf); + + spin_lock(&portdev->cvq_lock); + if (add_inbuf(portdev->c_ivq, buf) < 0) { + dev_warn(&portdev->vdev->dev, + "Error adding buffer to queue\n"); + free_buf(buf); + } + } + spin_unlock(&portdev->cvq_lock); +} + +static void in_intr(struct virtqueue *vq) +{ + struct port *port; + unsigned long flags; + + port = find_port_by_vq(vq->vdev->priv, vq); + if (!port) + return; + + spin_lock_irqsave(&port->inbuf_lock, flags); + if (!port->inbuf) + port->inbuf = get_inbuf(port); + + /* + * Don't queue up data when port is closed. This condition + * can be reached when a console port is not yet connected (no + * tty is spawned) and the host sends out data to console + * ports. For generic serial ports, the host won't + * (shouldn't) send data till the guest is connected. + */ + if (!port->guest_connected) + discard_port_data(port); + + spin_unlock_irqrestore(&port->inbuf_lock, flags); + + wake_up_interruptible(&port->waitqueue); + + /* Send a SIGIO indicating new data in case the process asked for it */ + send_sigio_to_port(port); + + if (is_console_port(port) && hvc_poll(port->cons.hvc)) + hvc_kick(); +} + +static void control_intr(struct virtqueue *vq) +{ + struct ports_device *portdev; + + portdev = vq->vdev->priv; + schedule_work(&portdev->control_work); +} + +static void config_intr(struct virtio_device *vdev) +{ + struct ports_device *portdev; + + portdev = vdev->priv; + + if (!use_multiport(portdev)) { + struct port *port; + u16 rows, cols; + + vdev->config->get(vdev, + offsetof(struct virtio_console_config, cols), + &cols, sizeof(u16)); + vdev->config->get(vdev, + offsetof(struct virtio_console_config, rows), + &rows, sizeof(u16)); + + port = find_port_by_id(portdev, 0); + set_console_size(port, rows, cols); + + /* + * We'll use this way of resizing only for legacy + * support. For newer userspace + * (VIRTIO_CONSOLE_F_MULTPORT+), use control messages + * to indicate console size changes so that it can be + * done per-port. + */ + resize_console(port); + } +} + +static int init_vqs(struct ports_device *portdev) +{ + vq_callback_t **io_callbacks; + char **io_names; + struct virtqueue **vqs; + u32 i, j, nr_ports, nr_queues; + int err; + + nr_ports = portdev->config.max_nr_ports; + nr_queues = use_multiport(portdev) ? (nr_ports + 1) * 2 : 2; + + vqs = kmalloc(nr_queues * sizeof(struct virtqueue *), GFP_KERNEL); + io_callbacks = kmalloc(nr_queues * sizeof(vq_callback_t *), GFP_KERNEL); + io_names = kmalloc(nr_queues * sizeof(char *), GFP_KERNEL); + portdev->in_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), + GFP_KERNEL); + portdev->out_vqs = kmalloc(nr_ports * sizeof(struct virtqueue *), + GFP_KERNEL); + if (!vqs || !io_callbacks || !io_names || !portdev->in_vqs || + !portdev->out_vqs) { + err = -ENOMEM; + goto free; + } + + /* + * For backward compat (newer host but older guest), the host + * spawns a console port first and also inits the vqs for port + * 0 before others. + */ + j = 0; + io_callbacks[j] = in_intr; + io_callbacks[j + 1] = NULL; + io_names[j] = "input"; + io_names[j + 1] = "output"; + j += 2; + + if (use_multiport(portdev)) { + io_callbacks[j] = control_intr; + io_callbacks[j + 1] = NULL; + io_names[j] = "control-i"; + io_names[j + 1] = "control-o"; + + for (i = 1; i < nr_ports; i++) { + j += 2; + io_callbacks[j] = in_intr; + io_callbacks[j + 1] = NULL; + io_names[j] = "input"; + io_names[j + 1] = "output"; + } + } + /* Find the queues. */ + err = portdev->vdev->config->find_vqs(portdev->vdev, nr_queues, vqs, + io_callbacks, + (const char **)io_names); + if (err) + goto free; + + j = 0; + portdev->in_vqs[0] = vqs[0]; + portdev->out_vqs[0] = vqs[1]; + j += 2; + if (use_multiport(portdev)) { + portdev->c_ivq = vqs[j]; + portdev->c_ovq = vqs[j + 1]; + + for (i = 1; i < nr_ports; i++) { + j += 2; + portdev->in_vqs[i] = vqs[j]; + portdev->out_vqs[i] = vqs[j + 1]; + } + } + kfree(io_names); + kfree(io_callbacks); + kfree(vqs); + + return 0; + +free: + kfree(portdev->out_vqs); + kfree(portdev->in_vqs); + kfree(io_names); + kfree(io_callbacks); + kfree(vqs); + + return err; +} + +static const struct file_operations portdev_fops = { + .owner = THIS_MODULE, +}; + +/* + * Once we're further in boot, we get probed like any other virtio + * device. + * + * If the host also supports multiple console ports, we check the + * config space to see how many ports the host has spawned. We + * initialize each port found. + */ +static int __devinit virtcons_probe(struct virtio_device *vdev) +{ + struct ports_device *portdev; + int err; + bool multiport; + + portdev = kmalloc(sizeof(*portdev), GFP_KERNEL); + if (!portdev) { + err = -ENOMEM; + goto fail; + } + + /* Attach this portdev to this virtio_device, and vice-versa. */ + portdev->vdev = vdev; + vdev->priv = portdev; + + spin_lock_irq(&pdrvdata_lock); + portdev->drv_index = pdrvdata.index++; + spin_unlock_irq(&pdrvdata_lock); + + portdev->chr_major = register_chrdev(0, "virtio-portsdev", + &portdev_fops); + if (portdev->chr_major < 0) { + dev_err(&vdev->dev, + "Error %d registering chrdev for device %u\n", + portdev->chr_major, portdev->drv_index); + err = portdev->chr_major; + goto free; + } + + multiport = false; + portdev->config.max_nr_ports = 1; + if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { + multiport = true; + vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; + + vdev->config->get(vdev, offsetof(struct virtio_console_config, + max_nr_ports), + &portdev->config.max_nr_ports, + sizeof(portdev->config.max_nr_ports)); + } + + /* Let the Host know we support multiple ports.*/ + vdev->config->finalize_features(vdev); + + err = init_vqs(portdev); + if (err < 0) { + dev_err(&vdev->dev, "Error %d initializing vqs\n", err); + goto free_chrdev; + } + + spin_lock_init(&portdev->ports_lock); + INIT_LIST_HEAD(&portdev->ports); + + if (multiport) { + unsigned int nr_added_bufs; + + spin_lock_init(&portdev->cvq_lock); + INIT_WORK(&portdev->control_work, &control_work_handler); + + nr_added_bufs = fill_queue(portdev->c_ivq, &portdev->cvq_lock); + if (!nr_added_bufs) { + dev_err(&vdev->dev, + "Error allocating buffers for control queue\n"); + err = -ENOMEM; + goto free_vqs; + } + } else { + /* + * For backward compatibility: Create a console port + * if we're running on older host. + */ + add_port(portdev, 0); + } + + spin_lock_irq(&pdrvdata_lock); + list_add_tail(&portdev->list, &pdrvdata.portdevs); + spin_unlock_irq(&pdrvdata_lock); + + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 1); + return 0; + +free_vqs: + /* The host might want to notify mgmt sw about device add failure */ + __send_control_msg(portdev, VIRTIO_CONSOLE_BAD_ID, + VIRTIO_CONSOLE_DEVICE_READY, 0); + vdev->config->del_vqs(vdev); + kfree(portdev->in_vqs); + kfree(portdev->out_vqs); +free_chrdev: + unregister_chrdev(portdev->chr_major, "virtio-portsdev"); +free: + kfree(portdev); +fail: + return err; +} + +static void virtcons_remove(struct virtio_device *vdev) +{ + struct ports_device *portdev; + struct port *port, *port2; + + portdev = vdev->priv; + + spin_lock_irq(&pdrvdata_lock); + list_del(&portdev->list); + spin_unlock_irq(&pdrvdata_lock); + + /* Disable interrupts for vqs */ + vdev->config->reset(vdev); + /* Finish up work that's lined up */ + cancel_work_sync(&portdev->control_work); + + list_for_each_entry_safe(port, port2, &portdev->ports, list) + unplug_port(port); + + unregister_chrdev(portdev->chr_major, "virtio-portsdev"); + + /* + * When yanking out a device, we immediately lose the + * (device-side) queues. So there's no point in keeping the + * guest side around till we drop our final reference. This + * also means that any ports which are in an open state will + * have to just stop using the port, as the vqs are going + * away. + */ + if (use_multiport(portdev)) { + struct port_buffer *buf; + unsigned int len; + + while ((buf = virtqueue_get_buf(portdev->c_ivq, &len))) + free_buf(buf); + + while ((buf = virtqueue_detach_unused_buf(portdev->c_ivq))) + free_buf(buf); + } + + vdev->config->del_vqs(vdev); + kfree(portdev->in_vqs); + kfree(portdev->out_vqs); + + kfree(portdev); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_CONSOLE, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { + VIRTIO_CONSOLE_F_SIZE, + VIRTIO_CONSOLE_F_MULTIPORT, +}; + +static struct virtio_driver virtio_console = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = virtcons_probe, + .remove = virtcons_remove, + .config_changed = config_intr, +}; + +static int __init init(void) +{ + int err; + + pdrvdata.class = class_create(THIS_MODULE, "virtio-ports"); + if (IS_ERR(pdrvdata.class)) { + err = PTR_ERR(pdrvdata.class); + pr_err("Error %d creating virtio-ports class\n", err); + return err; + } + + pdrvdata.debugfs_dir = debugfs_create_dir("virtio-ports", NULL); + if (!pdrvdata.debugfs_dir) { + pr_warning("Error %ld creating debugfs dir for virtio-ports\n", + PTR_ERR(pdrvdata.debugfs_dir)); + } + INIT_LIST_HEAD(&pdrvdata.consoles); + INIT_LIST_HEAD(&pdrvdata.portdevs); + + return register_virtio_driver(&virtio_console); +} + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtio_console); + + class_destroy(pdrvdata.class); + if (pdrvdata.debugfs_dir) + debugfs_remove_recursive(pdrvdata.debugfs_dir); +} +module_init(init); +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio console driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ab4382d27412e7e3e7c936e8d50d8888dfac3df8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Jan 2011 12:10:18 -0800 Subject: tty: move drivers/serial/ to drivers/tty/serial/ The serial drivers are really just tty drivers, so move them to drivers/tty/ to make things a bit neater overall. This is part of the tty/serial driver movement proceedure as proposed by Arnd Bergmann and approved by everyone involved a number of months ago. Cc: Arnd Bergmann Cc: Alan Cox Cc: Geert Uytterhoeven Cc: Rogier Wolff Cc: Michael H. Warfield Signed-off-by: Greg Kroah-Hartman --- drivers/Makefile | 3 +- drivers/char/Kconfig | 2 +- drivers/serial/21285.c | 513 --- drivers/serial/68328serial.c | 1472 --------- drivers/serial/68328serial.h | 188 -- drivers/serial/68360serial.c | 2978 ----------------- drivers/serial/8250.c | 3377 -------------------- drivers/serial/8250.h | 80 - drivers/serial/8250_accent.c | 47 - drivers/serial/8250_acorn.c | 141 - drivers/serial/8250_boca.c | 61 - drivers/serial/8250_early.c | 287 -- drivers/serial/8250_exar_st16c554.c | 52 - drivers/serial/8250_fourport.c | 53 - drivers/serial/8250_gsc.c | 122 - drivers/serial/8250_hp300.c | 327 -- drivers/serial/8250_hub6.c | 58 - drivers/serial/8250_mca.c | 63 - drivers/serial/8250_pci.c | 3850 ---------------------- drivers/serial/8250_pnp.c | 523 --- drivers/serial/Kconfig | 1598 ---------- drivers/serial/Makefile | 94 - drivers/serial/altera_jtaguart.c | 504 --- drivers/serial/altera_uart.c | 608 ---- drivers/serial/amba-pl010.c | 825 ----- drivers/serial/amba-pl011.c | 1519 --------- drivers/serial/apbuart.c | 709 ----- drivers/serial/apbuart.h | 64 - drivers/serial/atmel_serial.c | 1808 ----------- drivers/serial/bcm63xx_uart.c | 891 ------ drivers/serial/bfin_5xx.c | 1600 ---------- drivers/serial/bfin_sport_uart.c | 935 ------ drivers/serial/bfin_sport_uart.h | 86 - drivers/serial/clps711x.c | 579 ---- drivers/serial/cpm_uart/Makefile | 11 - drivers/serial/cpm_uart/cpm_uart.h | 147 - drivers/serial/cpm_uart/cpm_uart_core.c | 1443 --------- drivers/serial/cpm_uart/cpm_uart_cpm1.c | 138 - drivers/serial/cpm_uart/cpm_uart_cpm1.h | 34 - drivers/serial/cpm_uart/cpm_uart_cpm2.c | 174 - drivers/serial/cpm_uart/cpm_uart_cpm2.h | 34 - drivers/serial/crisv10.c | 4573 --------------------------- drivers/serial/crisv10.h | 147 - drivers/serial/dz.c | 955 ------ drivers/serial/dz.h | 129 - drivers/serial/icom.c | 1658 ---------- drivers/serial/icom.h | 287 -- drivers/serial/ifx6x60.c | 1406 -------- drivers/serial/ifx6x60.h | 129 - drivers/serial/imx.c | 1380 -------- drivers/serial/ioc3_serial.c | 2199 ------------- drivers/serial/ioc4_serial.c | 2953 ----------------- drivers/serial/ip22zilog.c | 1221 ------- drivers/serial/ip22zilog.h | 281 -- drivers/serial/jsm/Makefile | 8 - drivers/serial/jsm/jsm.h | 388 --- drivers/serial/jsm/jsm_driver.c | 297 -- drivers/serial/jsm/jsm_neo.c | 1412 --------- drivers/serial/jsm/jsm_tty.c | 910 ------ drivers/serial/kgdboc.c | 328 -- drivers/serial/m32r_sio.c | 1192 ------- drivers/serial/m32r_sio.h | 48 - drivers/serial/m32r_sio_reg.h | 152 - drivers/serial/max3100.c | 926 ------ drivers/serial/max3107-aava.c | 344 -- drivers/serial/max3107.c | 1213 ------- drivers/serial/max3107.h | 441 --- drivers/serial/mcf.c | 662 ---- drivers/serial/mfd.c | 1513 --------- drivers/serial/mpc52xx_uart.c | 1527 --------- drivers/serial/mpsc.c | 2159 ------------- drivers/serial/mrst_max3110.c | 919 ------ drivers/serial/mrst_max3110.h | 60 - drivers/serial/msm_serial.c | 758 ----- drivers/serial/msm_serial.h | 173 - drivers/serial/mux.c | 633 ---- drivers/serial/netx-serial.c | 750 ----- drivers/serial/nwpserial.c | 477 --- drivers/serial/of_serial.c | 201 -- drivers/serial/omap-serial.c | 1359 -------- drivers/serial/pch_uart.c | 1451 --------- drivers/serial/pmac_zilog.c | 2208 ------------- drivers/serial/pmac_zilog.h | 396 --- drivers/serial/pnx8xxx_uart.c | 854 ----- drivers/serial/pxa.c | 879 ----- drivers/serial/s3c2400.c | 106 - drivers/serial/s3c2410.c | 118 - drivers/serial/s3c2412.c | 152 - drivers/serial/s3c2440.c | 181 -- drivers/serial/s3c24a0.c | 118 - drivers/serial/s3c6400.c | 152 - drivers/serial/s5pv210.c | 162 - drivers/serial/sa1100.c | 918 ------ drivers/serial/samsung.c | 1487 --------- drivers/serial/samsung.h | 120 - drivers/serial/sb1250-duart.c | 976 ------ drivers/serial/sc26xx.c | 757 ----- drivers/serial/serial_core.c | 2578 --------------- drivers/serial/serial_cs.c | 869 ----- drivers/serial/serial_ks8695.c | 705 ----- drivers/serial/serial_lh7a40x.c | 682 ---- drivers/serial/serial_txx9.c | 1344 -------- drivers/serial/sh-sci.c | 2027 ------------ drivers/serial/sh-sci.h | 660 ---- drivers/serial/sn_console.c | 1085 ------- drivers/serial/suncore.c | 247 -- drivers/serial/suncore.h | 33 - drivers/serial/sunhv.c | 661 ---- drivers/serial/sunsab.c | 1152 ------- drivers/serial/sunsab.h | 322 -- drivers/serial/sunsu.c | 1608 ---------- drivers/serial/sunzilog.c | 1655 ---------- drivers/serial/sunzilog.h | 289 -- drivers/serial/timbuart.c | 531 ---- drivers/serial/timbuart.h | 58 - drivers/serial/uartlite.c | 709 ----- drivers/serial/ucc_uart.c | 1537 --------- drivers/serial/vr41xx_siu.c | 978 ------ drivers/serial/vt8500_serial.c | 648 ---- drivers/serial/zs.c | 1304 -------- drivers/serial/zs.h | 284 -- drivers/tty/Makefile | 1 + drivers/tty/serial/21285.c | 513 +++ drivers/tty/serial/68328serial.c | 1472 +++++++++ drivers/tty/serial/68328serial.h | 188 ++ drivers/tty/serial/68360serial.c | 2978 +++++++++++++++++ drivers/tty/serial/8250.c | 3377 ++++++++++++++++++++ drivers/tty/serial/8250.h | 80 + drivers/tty/serial/8250_accent.c | 47 + drivers/tty/serial/8250_acorn.c | 141 + drivers/tty/serial/8250_boca.c | 61 + drivers/tty/serial/8250_early.c | 287 ++ drivers/tty/serial/8250_exar_st16c554.c | 52 + drivers/tty/serial/8250_fourport.c | 53 + drivers/tty/serial/8250_gsc.c | 122 + drivers/tty/serial/8250_hp300.c | 327 ++ drivers/tty/serial/8250_hub6.c | 58 + drivers/tty/serial/8250_mca.c | 63 + drivers/tty/serial/8250_pci.c | 3850 ++++++++++++++++++++++ drivers/tty/serial/8250_pnp.c | 523 +++ drivers/tty/serial/Kconfig | 1598 ++++++++++ drivers/tty/serial/Makefile | 94 + drivers/tty/serial/altera_jtaguart.c | 504 +++ drivers/tty/serial/altera_uart.c | 608 ++++ drivers/tty/serial/amba-pl010.c | 825 +++++ drivers/tty/serial/amba-pl011.c | 1519 +++++++++ drivers/tty/serial/apbuart.c | 709 +++++ drivers/tty/serial/apbuart.h | 64 + drivers/tty/serial/atmel_serial.c | 1808 +++++++++++ drivers/tty/serial/bcm63xx_uart.c | 891 ++++++ drivers/tty/serial/bfin_5xx.c | 1600 ++++++++++ drivers/tty/serial/bfin_sport_uart.c | 935 ++++++ drivers/tty/serial/bfin_sport_uart.h | 86 + drivers/tty/serial/clps711x.c | 579 ++++ drivers/tty/serial/cpm_uart/Makefile | 11 + drivers/tty/serial/cpm_uart/cpm_uart.h | 147 + drivers/tty/serial/cpm_uart/cpm_uart_core.c | 1443 +++++++++ drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c | 138 + drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h | 34 + drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c | 174 + drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h | 34 + drivers/tty/serial/crisv10.c | 4573 +++++++++++++++++++++++++++ drivers/tty/serial/crisv10.h | 147 + drivers/tty/serial/dz.c | 955 ++++++ drivers/tty/serial/dz.h | 129 + drivers/tty/serial/icom.c | 1658 ++++++++++ drivers/tty/serial/icom.h | 287 ++ drivers/tty/serial/ifx6x60.c | 1406 ++++++++ drivers/tty/serial/ifx6x60.h | 129 + drivers/tty/serial/imx.c | 1380 ++++++++ drivers/tty/serial/ioc3_serial.c | 2199 +++++++++++++ drivers/tty/serial/ioc4_serial.c | 2953 +++++++++++++++++ drivers/tty/serial/ip22zilog.c | 1221 +++++++ drivers/tty/serial/ip22zilog.h | 281 ++ drivers/tty/serial/jsm/Makefile | 8 + drivers/tty/serial/jsm/jsm.h | 388 +++ drivers/tty/serial/jsm/jsm_driver.c | 297 ++ drivers/tty/serial/jsm/jsm_neo.c | 1412 +++++++++ drivers/tty/serial/jsm/jsm_tty.c | 910 ++++++ drivers/tty/serial/kgdboc.c | 328 ++ drivers/tty/serial/m32r_sio.c | 1192 +++++++ drivers/tty/serial/m32r_sio.h | 48 + drivers/tty/serial/m32r_sio_reg.h | 152 + drivers/tty/serial/max3100.c | 926 ++++++ drivers/tty/serial/max3107-aava.c | 344 ++ drivers/tty/serial/max3107.c | 1213 +++++++ drivers/tty/serial/max3107.h | 441 +++ drivers/tty/serial/mcf.c | 662 ++++ drivers/tty/serial/mfd.c | 1513 +++++++++ drivers/tty/serial/mpc52xx_uart.c | 1527 +++++++++ drivers/tty/serial/mpsc.c | 2159 +++++++++++++ drivers/tty/serial/mrst_max3110.c | 919 ++++++ drivers/tty/serial/mrst_max3110.h | 60 + drivers/tty/serial/msm_serial.c | 758 +++++ drivers/tty/serial/msm_serial.h | 173 + drivers/tty/serial/mux.c | 633 ++++ drivers/tty/serial/netx-serial.c | 750 +++++ drivers/tty/serial/nwpserial.c | 477 +++ drivers/tty/serial/of_serial.c | 201 ++ drivers/tty/serial/omap-serial.c | 1359 ++++++++ drivers/tty/serial/pch_uart.c | 1451 +++++++++ drivers/tty/serial/pmac_zilog.c | 2208 +++++++++++++ drivers/tty/serial/pmac_zilog.h | 396 +++ drivers/tty/serial/pnx8xxx_uart.c | 854 +++++ drivers/tty/serial/pxa.c | 879 +++++ drivers/tty/serial/s3c2400.c | 106 + drivers/tty/serial/s3c2410.c | 118 + drivers/tty/serial/s3c2412.c | 152 + drivers/tty/serial/s3c2440.c | 181 ++ drivers/tty/serial/s3c24a0.c | 118 + drivers/tty/serial/s3c6400.c | 152 + drivers/tty/serial/s5pv210.c | 162 + drivers/tty/serial/sa1100.c | 918 ++++++ drivers/tty/serial/samsung.c | 1487 +++++++++ drivers/tty/serial/samsung.h | 120 + drivers/tty/serial/sb1250-duart.c | 976 ++++++ drivers/tty/serial/sc26xx.c | 757 +++++ drivers/tty/serial/serial_core.c | 2578 +++++++++++++++ drivers/tty/serial/serial_cs.c | 869 +++++ drivers/tty/serial/serial_ks8695.c | 705 +++++ drivers/tty/serial/serial_lh7a40x.c | 682 ++++ drivers/tty/serial/serial_txx9.c | 1344 ++++++++ drivers/tty/serial/sh-sci.c | 2027 ++++++++++++ drivers/tty/serial/sh-sci.h | 660 ++++ drivers/tty/serial/sn_console.c | 1085 +++++++ drivers/tty/serial/suncore.c | 247 ++ drivers/tty/serial/suncore.h | 33 + drivers/tty/serial/sunhv.c | 661 ++++ drivers/tty/serial/sunsab.c | 1152 +++++++ drivers/tty/serial/sunsab.h | 322 ++ drivers/tty/serial/sunsu.c | 1608 ++++++++++ drivers/tty/serial/sunzilog.c | 1655 ++++++++++ drivers/tty/serial/sunzilog.h | 289 ++ drivers/tty/serial/timbuart.c | 531 ++++ drivers/tty/serial/timbuart.h | 58 + drivers/tty/serial/uartlite.c | 709 +++++ drivers/tty/serial/ucc_uart.c | 1537 +++++++++ drivers/tty/serial/vr41xx_siu.c | 978 ++++++ drivers/tty/serial/vt8500_serial.c | 648 ++++ drivers/tty/serial/zs.c | 1304 ++++++++ drivers/tty/serial/zs.h | 284 ++ 241 files changed, 97165 insertions(+), 97165 deletions(-) delete mode 100644 drivers/serial/21285.c delete mode 100644 drivers/serial/68328serial.c delete mode 100644 drivers/serial/68328serial.h delete mode 100644 drivers/serial/68360serial.c delete mode 100644 drivers/serial/8250.c delete mode 100644 drivers/serial/8250.h delete mode 100644 drivers/serial/8250_accent.c delete mode 100644 drivers/serial/8250_acorn.c delete mode 100644 drivers/serial/8250_boca.c delete mode 100644 drivers/serial/8250_early.c delete mode 100644 drivers/serial/8250_exar_st16c554.c delete mode 100644 drivers/serial/8250_fourport.c delete mode 100644 drivers/serial/8250_gsc.c delete mode 100644 drivers/serial/8250_hp300.c delete mode 100644 drivers/serial/8250_hub6.c delete mode 100644 drivers/serial/8250_mca.c delete mode 100644 drivers/serial/8250_pci.c delete mode 100644 drivers/serial/8250_pnp.c delete mode 100644 drivers/serial/Kconfig delete mode 100644 drivers/serial/Makefile delete mode 100644 drivers/serial/altera_jtaguart.c delete mode 100644 drivers/serial/altera_uart.c delete mode 100644 drivers/serial/amba-pl010.c delete mode 100644 drivers/serial/amba-pl011.c delete mode 100644 drivers/serial/apbuart.c delete mode 100644 drivers/serial/apbuart.h delete mode 100644 drivers/serial/atmel_serial.c delete mode 100644 drivers/serial/bcm63xx_uart.c delete mode 100644 drivers/serial/bfin_5xx.c delete mode 100644 drivers/serial/bfin_sport_uart.c delete mode 100644 drivers/serial/bfin_sport_uart.h delete mode 100644 drivers/serial/clps711x.c delete mode 100644 drivers/serial/cpm_uart/Makefile delete mode 100644 drivers/serial/cpm_uart/cpm_uart.h delete mode 100644 drivers/serial/cpm_uart/cpm_uart_core.c delete mode 100644 drivers/serial/cpm_uart/cpm_uart_cpm1.c delete mode 100644 drivers/serial/cpm_uart/cpm_uart_cpm1.h delete mode 100644 drivers/serial/cpm_uart/cpm_uart_cpm2.c delete mode 100644 drivers/serial/cpm_uart/cpm_uart_cpm2.h delete mode 100644 drivers/serial/crisv10.c delete mode 100644 drivers/serial/crisv10.h delete mode 100644 drivers/serial/dz.c delete mode 100644 drivers/serial/dz.h delete mode 100644 drivers/serial/icom.c delete mode 100644 drivers/serial/icom.h delete mode 100644 drivers/serial/ifx6x60.c delete mode 100644 drivers/serial/ifx6x60.h delete mode 100644 drivers/serial/imx.c delete mode 100644 drivers/serial/ioc3_serial.c delete mode 100644 drivers/serial/ioc4_serial.c delete mode 100644 drivers/serial/ip22zilog.c delete mode 100644 drivers/serial/ip22zilog.h delete mode 100644 drivers/serial/jsm/Makefile delete mode 100644 drivers/serial/jsm/jsm.h delete mode 100644 drivers/serial/jsm/jsm_driver.c delete mode 100644 drivers/serial/jsm/jsm_neo.c delete mode 100644 drivers/serial/jsm/jsm_tty.c delete mode 100644 drivers/serial/kgdboc.c delete mode 100644 drivers/serial/m32r_sio.c delete mode 100644 drivers/serial/m32r_sio.h delete mode 100644 drivers/serial/m32r_sio_reg.h delete mode 100644 drivers/serial/max3100.c delete mode 100644 drivers/serial/max3107-aava.c delete mode 100644 drivers/serial/max3107.c delete mode 100644 drivers/serial/max3107.h delete mode 100644 drivers/serial/mcf.c delete mode 100644 drivers/serial/mfd.c delete mode 100644 drivers/serial/mpc52xx_uart.c delete mode 100644 drivers/serial/mpsc.c delete mode 100644 drivers/serial/mrst_max3110.c delete mode 100644 drivers/serial/mrst_max3110.h delete mode 100644 drivers/serial/msm_serial.c delete mode 100644 drivers/serial/msm_serial.h delete mode 100644 drivers/serial/mux.c delete mode 100644 drivers/serial/netx-serial.c delete mode 100644 drivers/serial/nwpserial.c delete mode 100644 drivers/serial/of_serial.c delete mode 100644 drivers/serial/omap-serial.c delete mode 100644 drivers/serial/pch_uart.c delete mode 100644 drivers/serial/pmac_zilog.c delete mode 100644 drivers/serial/pmac_zilog.h delete mode 100644 drivers/serial/pnx8xxx_uart.c delete mode 100644 drivers/serial/pxa.c delete mode 100644 drivers/serial/s3c2400.c delete mode 100644 drivers/serial/s3c2410.c delete mode 100644 drivers/serial/s3c2412.c delete mode 100644 drivers/serial/s3c2440.c delete mode 100644 drivers/serial/s3c24a0.c delete mode 100644 drivers/serial/s3c6400.c delete mode 100644 drivers/serial/s5pv210.c delete mode 100644 drivers/serial/sa1100.c delete mode 100644 drivers/serial/samsung.c delete mode 100644 drivers/serial/samsung.h delete mode 100644 drivers/serial/sb1250-duart.c delete mode 100644 drivers/serial/sc26xx.c delete mode 100644 drivers/serial/serial_core.c delete mode 100644 drivers/serial/serial_cs.c delete mode 100644 drivers/serial/serial_ks8695.c delete mode 100644 drivers/serial/serial_lh7a40x.c delete mode 100644 drivers/serial/serial_txx9.c delete mode 100644 drivers/serial/sh-sci.c delete mode 100644 drivers/serial/sh-sci.h delete mode 100644 drivers/serial/sn_console.c delete mode 100644 drivers/serial/suncore.c delete mode 100644 drivers/serial/suncore.h delete mode 100644 drivers/serial/sunhv.c delete mode 100644 drivers/serial/sunsab.c delete mode 100644 drivers/serial/sunsab.h delete mode 100644 drivers/serial/sunsu.c delete mode 100644 drivers/serial/sunzilog.c delete mode 100644 drivers/serial/sunzilog.h delete mode 100644 drivers/serial/timbuart.c delete mode 100644 drivers/serial/timbuart.h delete mode 100644 drivers/serial/uartlite.c delete mode 100644 drivers/serial/ucc_uart.c delete mode 100644 drivers/serial/vr41xx_siu.c delete mode 100644 drivers/serial/vt8500_serial.c delete mode 100644 drivers/serial/zs.c delete mode 100644 drivers/serial/zs.h create mode 100644 drivers/tty/serial/21285.c create mode 100644 drivers/tty/serial/68328serial.c create mode 100644 drivers/tty/serial/68328serial.h create mode 100644 drivers/tty/serial/68360serial.c create mode 100644 drivers/tty/serial/8250.c create mode 100644 drivers/tty/serial/8250.h create mode 100644 drivers/tty/serial/8250_accent.c create mode 100644 drivers/tty/serial/8250_acorn.c create mode 100644 drivers/tty/serial/8250_boca.c create mode 100644 drivers/tty/serial/8250_early.c create mode 100644 drivers/tty/serial/8250_exar_st16c554.c create mode 100644 drivers/tty/serial/8250_fourport.c create mode 100644 drivers/tty/serial/8250_gsc.c create mode 100644 drivers/tty/serial/8250_hp300.c create mode 100644 drivers/tty/serial/8250_hub6.c create mode 100644 drivers/tty/serial/8250_mca.c create mode 100644 drivers/tty/serial/8250_pci.c create mode 100644 drivers/tty/serial/8250_pnp.c create mode 100644 drivers/tty/serial/Kconfig create mode 100644 drivers/tty/serial/Makefile create mode 100644 drivers/tty/serial/altera_jtaguart.c create mode 100644 drivers/tty/serial/altera_uart.c create mode 100644 drivers/tty/serial/amba-pl010.c create mode 100644 drivers/tty/serial/amba-pl011.c create mode 100644 drivers/tty/serial/apbuart.c create mode 100644 drivers/tty/serial/apbuart.h create mode 100644 drivers/tty/serial/atmel_serial.c create mode 100644 drivers/tty/serial/bcm63xx_uart.c create mode 100644 drivers/tty/serial/bfin_5xx.c create mode 100644 drivers/tty/serial/bfin_sport_uart.c create mode 100644 drivers/tty/serial/bfin_sport_uart.h create mode 100644 drivers/tty/serial/clps711x.c create mode 100644 drivers/tty/serial/cpm_uart/Makefile create mode 100644 drivers/tty/serial/cpm_uart/cpm_uart.h create mode 100644 drivers/tty/serial/cpm_uart/cpm_uart_core.c create mode 100644 drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c create mode 100644 drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h create mode 100644 drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c create mode 100644 drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h create mode 100644 drivers/tty/serial/crisv10.c create mode 100644 drivers/tty/serial/crisv10.h create mode 100644 drivers/tty/serial/dz.c create mode 100644 drivers/tty/serial/dz.h create mode 100644 drivers/tty/serial/icom.c create mode 100644 drivers/tty/serial/icom.h create mode 100644 drivers/tty/serial/ifx6x60.c create mode 100644 drivers/tty/serial/ifx6x60.h create mode 100644 drivers/tty/serial/imx.c create mode 100644 drivers/tty/serial/ioc3_serial.c create mode 100644 drivers/tty/serial/ioc4_serial.c create mode 100644 drivers/tty/serial/ip22zilog.c create mode 100644 drivers/tty/serial/ip22zilog.h create mode 100644 drivers/tty/serial/jsm/Makefile create mode 100644 drivers/tty/serial/jsm/jsm.h create mode 100644 drivers/tty/serial/jsm/jsm_driver.c create mode 100644 drivers/tty/serial/jsm/jsm_neo.c create mode 100644 drivers/tty/serial/jsm/jsm_tty.c create mode 100644 drivers/tty/serial/kgdboc.c create mode 100644 drivers/tty/serial/m32r_sio.c create mode 100644 drivers/tty/serial/m32r_sio.h create mode 100644 drivers/tty/serial/m32r_sio_reg.h create mode 100644 drivers/tty/serial/max3100.c create mode 100644 drivers/tty/serial/max3107-aava.c create mode 100644 drivers/tty/serial/max3107.c create mode 100644 drivers/tty/serial/max3107.h create mode 100644 drivers/tty/serial/mcf.c create mode 100644 drivers/tty/serial/mfd.c create mode 100644 drivers/tty/serial/mpc52xx_uart.c create mode 100644 drivers/tty/serial/mpsc.c create mode 100644 drivers/tty/serial/mrst_max3110.c create mode 100644 drivers/tty/serial/mrst_max3110.h create mode 100644 drivers/tty/serial/msm_serial.c create mode 100644 drivers/tty/serial/msm_serial.h create mode 100644 drivers/tty/serial/mux.c create mode 100644 drivers/tty/serial/netx-serial.c create mode 100644 drivers/tty/serial/nwpserial.c create mode 100644 drivers/tty/serial/of_serial.c create mode 100644 drivers/tty/serial/omap-serial.c create mode 100644 drivers/tty/serial/pch_uart.c create mode 100644 drivers/tty/serial/pmac_zilog.c create mode 100644 drivers/tty/serial/pmac_zilog.h create mode 100644 drivers/tty/serial/pnx8xxx_uart.c create mode 100644 drivers/tty/serial/pxa.c create mode 100644 drivers/tty/serial/s3c2400.c create mode 100644 drivers/tty/serial/s3c2410.c create mode 100644 drivers/tty/serial/s3c2412.c create mode 100644 drivers/tty/serial/s3c2440.c create mode 100644 drivers/tty/serial/s3c24a0.c create mode 100644 drivers/tty/serial/s3c6400.c create mode 100644 drivers/tty/serial/s5pv210.c create mode 100644 drivers/tty/serial/sa1100.c create mode 100644 drivers/tty/serial/samsung.c create mode 100644 drivers/tty/serial/samsung.h create mode 100644 drivers/tty/serial/sb1250-duart.c create mode 100644 drivers/tty/serial/sc26xx.c create mode 100644 drivers/tty/serial/serial_core.c create mode 100644 drivers/tty/serial/serial_cs.c create mode 100644 drivers/tty/serial/serial_ks8695.c create mode 100644 drivers/tty/serial/serial_lh7a40x.c create mode 100644 drivers/tty/serial/serial_txx9.c create mode 100644 drivers/tty/serial/sh-sci.c create mode 100644 drivers/tty/serial/sh-sci.h create mode 100644 drivers/tty/serial/sn_console.c create mode 100644 drivers/tty/serial/suncore.c create mode 100644 drivers/tty/serial/suncore.h create mode 100644 drivers/tty/serial/sunhv.c create mode 100644 drivers/tty/serial/sunsab.c create mode 100644 drivers/tty/serial/sunsab.h create mode 100644 drivers/tty/serial/sunsu.c create mode 100644 drivers/tty/serial/sunzilog.c create mode 100644 drivers/tty/serial/sunzilog.h create mode 100644 drivers/tty/serial/timbuart.c create mode 100644 drivers/tty/serial/timbuart.h create mode 100644 drivers/tty/serial/uartlite.c create mode 100644 drivers/tty/serial/ucc_uart.c create mode 100644 drivers/tty/serial/vr41xx_siu.c create mode 100644 drivers/tty/serial/vt8500_serial.c create mode 100644 drivers/tty/serial/zs.c create mode 100644 drivers/tty/serial/zs.h diff --git a/drivers/Makefile b/drivers/Makefile index ef5132469f58..1e2cda18c718 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_XEN) += xen/ # regulators early, since some subsystems rely on them to initialize obj-$(CONFIG_REGULATOR) += regulator/ -# char/ comes before serial/ etc so that the VT console is the boot-time +# tty/ comes before char/ so that the VT console is the boot-time # default. obj-y += tty/ obj-y += char/ @@ -38,7 +38,6 @@ obj-$(CONFIG_CONNECTOR) += connector/ obj-$(CONFIG_FB_I810) += video/i810/ obj-$(CONFIG_FB_INTEL) += video/intelfb/ -obj-y += serial/ obj-$(CONFIG_PARPORT) += parport/ obj-y += base/ block/ misc/ mfd/ nfc/ obj-$(CONFIG_NUBUS) += nubus/ diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 0f175a866ef0..ccac7d090727 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -426,7 +426,7 @@ config SGI_MBCS If you have an SGI Altix with an attached SABrick say Y or M here, otherwise say N. -source "drivers/serial/Kconfig" +source "drivers/tty/serial/Kconfig" config UNIX98_PTYS bool "Unix98 PTY support" if EMBEDDED diff --git a/drivers/serial/21285.c b/drivers/serial/21285.c deleted file mode 100644 index d89aa38c5cf0..000000000000 --- a/drivers/serial/21285.c +++ /dev/null @@ -1,513 +0,0 @@ -/* - * linux/drivers/serial/21285.c - * - * Driver for the serial port on the 21285 StrongArm-110 core logic chip. - * - * Based on drivers/char/serial.c - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define BAUD_BASE (mem_fclk_21285/64) - -#define SERIAL_21285_NAME "ttyFB" -#define SERIAL_21285_MAJOR 204 -#define SERIAL_21285_MINOR 4 - -#define RXSTAT_DUMMY_READ 0x80000000 -#define RXSTAT_FRAME (1 << 0) -#define RXSTAT_PARITY (1 << 1) -#define RXSTAT_OVERRUN (1 << 2) -#define RXSTAT_ANYERR (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN) - -#define H_UBRLCR_BREAK (1 << 0) -#define H_UBRLCR_PARENB (1 << 1) -#define H_UBRLCR_PAREVN (1 << 2) -#define H_UBRLCR_STOPB (1 << 3) -#define H_UBRLCR_FIFO (1 << 4) - -static const char serial21285_name[] = "Footbridge UART"; - -#define tx_enabled(port) ((port)->unused[0]) -#define rx_enabled(port) ((port)->unused[1]) - -/* - * The documented expression for selecting the divisor is: - * BAUD_BASE / baud - 1 - * However, typically BAUD_BASE is not divisible by baud, so - * we want to select the divisor that gives us the minimum - * error. Therefore, we want: - * int(BAUD_BASE / baud - 0.5) -> - * int(BAUD_BASE / baud - (baud >> 1) / baud) -> - * int((BAUD_BASE - (baud >> 1)) / baud) - */ - -static void serial21285_stop_tx(struct uart_port *port) -{ - if (tx_enabled(port)) { - disable_irq_nosync(IRQ_CONTX); - tx_enabled(port) = 0; - } -} - -static void serial21285_start_tx(struct uart_port *port) -{ - if (!tx_enabled(port)) { - enable_irq(IRQ_CONTX); - tx_enabled(port) = 1; - } -} - -static void serial21285_stop_rx(struct uart_port *port) -{ - if (rx_enabled(port)) { - disable_irq_nosync(IRQ_CONRX); - rx_enabled(port) = 0; - } -} - -static void serial21285_enable_ms(struct uart_port *port) -{ -} - -static irqreturn_t serial21285_rx_chars(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct tty_struct *tty = port->state->port.tty; - unsigned int status, ch, flag, rxs, max_count = 256; - - status = *CSR_UARTFLG; - while (!(status & 0x10) && max_count--) { - ch = *CSR_UARTDR; - flag = TTY_NORMAL; - port->icount.rx++; - - rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ; - if (unlikely(rxs & RXSTAT_ANYERR)) { - if (rxs & RXSTAT_PARITY) - port->icount.parity++; - else if (rxs & RXSTAT_FRAME) - port->icount.frame++; - if (rxs & RXSTAT_OVERRUN) - port->icount.overrun++; - - rxs &= port->read_status_mask; - - if (rxs & RXSTAT_PARITY) - flag = TTY_PARITY; - else if (rxs & RXSTAT_FRAME) - flag = TTY_FRAME; - } - - uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag); - - status = *CSR_UARTFLG; - } - tty_flip_buffer_push(tty); - - return IRQ_HANDLED; -} - -static irqreturn_t serial21285_tx_chars(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct circ_buf *xmit = &port->state->xmit; - int count = 256; - - if (port->x_char) { - *CSR_UARTDR = port->x_char; - port->icount.tx++; - port->x_char = 0; - goto out; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - serial21285_stop_tx(port); - goto out; - } - - do { - *CSR_UARTDR = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0 && !(*CSR_UARTFLG & 0x20)); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - serial21285_stop_tx(port); - - out: - return IRQ_HANDLED; -} - -static unsigned int serial21285_tx_empty(struct uart_port *port) -{ - return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT; -} - -/* no modem control lines */ -static unsigned int serial21285_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; -} - -static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -static void serial21285_break_ctl(struct uart_port *port, int break_state) -{ - unsigned long flags; - unsigned int h_lcr; - - spin_lock_irqsave(&port->lock, flags); - h_lcr = *CSR_H_UBRLCR; - if (break_state) - h_lcr |= H_UBRLCR_BREAK; - else - h_lcr &= ~H_UBRLCR_BREAK; - *CSR_H_UBRLCR = h_lcr; - spin_unlock_irqrestore(&port->lock, flags); -} - -static int serial21285_startup(struct uart_port *port) -{ - int ret; - - tx_enabled(port) = 1; - rx_enabled(port) = 1; - - ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0, - serial21285_name, port); - if (ret == 0) { - ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0, - serial21285_name, port); - if (ret) - free_irq(IRQ_CONRX, port); - } - - return ret; -} - -static void serial21285_shutdown(struct uart_port *port) -{ - free_irq(IRQ_CONTX, port); - free_irq(IRQ_CONRX, port); -} - -static void -serial21285_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - unsigned int baud, quot, h_lcr, b; - - /* - * We don't support modem control lines. - */ - termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); - termios->c_cflag |= CLOCAL; - - /* - * We don't support BREAK character recognition. - */ - termios->c_iflag &= ~(IGNBRK | BRKINT); - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - b = port->uartclk / (16 * quot); - tty_termios_encode_baud_rate(termios, b, b); - - switch (termios->c_cflag & CSIZE) { - case CS5: - h_lcr = 0x00; - break; - case CS6: - h_lcr = 0x20; - break; - case CS7: - h_lcr = 0x40; - break; - default: /* CS8 */ - h_lcr = 0x60; - break; - } - - if (termios->c_cflag & CSTOPB) - h_lcr |= H_UBRLCR_STOPB; - if (termios->c_cflag & PARENB) { - h_lcr |= H_UBRLCR_PARENB; - if (!(termios->c_cflag & PARODD)) - h_lcr |= H_UBRLCR_PAREVN; - } - - if (port->fifosize) - h_lcr |= H_UBRLCR_FIFO; - - spin_lock_irqsave(&port->lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * Which character status flags are we interested in? - */ - port->read_status_mask = RXSTAT_OVERRUN; - if (termios->c_iflag & INPCK) - port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; - - /* - * Which character status flags should we ignore? - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; - if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) - port->ignore_status_mask |= RXSTAT_OVERRUN; - - /* - * Ignore all characters if CREAD is not set. - */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= RXSTAT_DUMMY_READ; - - quot -= 1; - - *CSR_UARTCON = 0; - *CSR_L_UBRLCR = quot & 0xff; - *CSR_M_UBRLCR = (quot >> 8) & 0x0f; - *CSR_H_UBRLCR = h_lcr; - *CSR_UARTCON = 1; - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *serial21285_type(struct uart_port *port) -{ - return port->type == PORT_21285 ? "DC21285" : NULL; -} - -static void serial21285_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, 32); -} - -static int serial21285_request_port(struct uart_port *port) -{ - return request_mem_region(port->mapbase, 32, serial21285_name) - != NULL ? 0 : -EBUSY; -} - -static void serial21285_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0) - port->type = PORT_21285; -} - -/* - * verify the new serial_struct (for TIOCSSERIAL). - */ -static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - int ret = 0; - if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285) - ret = -EINVAL; - if (ser->irq != NO_IRQ) - ret = -EINVAL; - if (ser->baud_base != port->uartclk / 16) - ret = -EINVAL; - return ret; -} - -static struct uart_ops serial21285_ops = { - .tx_empty = serial21285_tx_empty, - .get_mctrl = serial21285_get_mctrl, - .set_mctrl = serial21285_set_mctrl, - .stop_tx = serial21285_stop_tx, - .start_tx = serial21285_start_tx, - .stop_rx = serial21285_stop_rx, - .enable_ms = serial21285_enable_ms, - .break_ctl = serial21285_break_ctl, - .startup = serial21285_startup, - .shutdown = serial21285_shutdown, - .set_termios = serial21285_set_termios, - .type = serial21285_type, - .release_port = serial21285_release_port, - .request_port = serial21285_request_port, - .config_port = serial21285_config_port, - .verify_port = serial21285_verify_port, -}; - -static struct uart_port serial21285_port = { - .mapbase = 0x42000160, - .iotype = UPIO_MEM, - .irq = NO_IRQ, - .fifosize = 16, - .ops = &serial21285_ops, - .flags = UPF_BOOT_AUTOCONF, -}; - -static void serial21285_setup_ports(void) -{ - serial21285_port.uartclk = mem_fclk_21285 / 4; -} - -#ifdef CONFIG_SERIAL_21285_CONSOLE -static void serial21285_console_putchar(struct uart_port *port, int ch) -{ - while (*CSR_UARTFLG & 0x20) - barrier(); - *CSR_UARTDR = ch; -} - -static void -serial21285_console_write(struct console *co, const char *s, - unsigned int count) -{ - uart_console_write(&serial21285_port, s, count, serial21285_console_putchar); -} - -static void __init -serial21285_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) -{ - if (*CSR_UARTCON == 1) { - unsigned int tmp; - - tmp = *CSR_H_UBRLCR; - switch (tmp & 0x60) { - case 0x00: - *bits = 5; - break; - case 0x20: - *bits = 6; - break; - case 0x40: - *bits = 7; - break; - default: - case 0x60: - *bits = 8; - break; - } - - if (tmp & H_UBRLCR_PARENB) { - *parity = 'o'; - if (tmp & H_UBRLCR_PAREVN) - *parity = 'e'; - } - - tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8); - - *baud = port->uartclk / (16 * (tmp + 1)); - } -} - -static int __init serial21285_console_setup(struct console *co, char *options) -{ - struct uart_port *port = &serial21285_port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (machine_is_personal_server()) - baud = 57600; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - serial21285_get_options(port, &baud, &parity, &bits); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver serial21285_reg; - -static struct console serial21285_console = -{ - .name = SERIAL_21285_NAME, - .write = serial21285_console_write, - .device = uart_console_device, - .setup = serial21285_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial21285_reg, -}; - -static int __init rs285_console_init(void) -{ - serial21285_setup_ports(); - register_console(&serial21285_console); - return 0; -} -console_initcall(rs285_console_init); - -#define SERIAL_21285_CONSOLE &serial21285_console -#else -#define SERIAL_21285_CONSOLE NULL -#endif - -static struct uart_driver serial21285_reg = { - .owner = THIS_MODULE, - .driver_name = "ttyFB", - .dev_name = "ttyFB", - .major = SERIAL_21285_MAJOR, - .minor = SERIAL_21285_MINOR, - .nr = 1, - .cons = SERIAL_21285_CONSOLE, -}; - -static int __init serial21285_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: 21285 driver\n"); - - serial21285_setup_ports(); - - ret = uart_register_driver(&serial21285_reg); - if (ret == 0) - uart_add_one_port(&serial21285_reg, &serial21285_port); - - return ret; -} - -static void __exit serial21285_exit(void) -{ - uart_remove_one_port(&serial21285_reg, &serial21285_port); - uart_unregister_driver(&serial21285_reg); -} - -module_init(serial21285_init); -module_exit(serial21285_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver"); -MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c deleted file mode 100644 index be0ebce36e54..000000000000 --- a/drivers/serial/68328serial.c +++ /dev/null @@ -1,1472 +0,0 @@ -/* 68328serial.c: Serial port driver for 68328 microcontroller - * - * Copyright (C) 1995 David S. Miller - * Copyright (C) 1998 Kenneth Albanowski - * Copyright (C) 1998, 1999 D. Jeff Dionne - * Copyright (C) 1999 Vladimir Gurevich - * Copyright (C) 2002-2003 David McCullough - * Copyright (C) 2002 Greg Ungerer - * - * VZ Support/Fixes Evan Stawnyczy - * Multiple UART support Daniel Potts - * Power management support Daniel Potts - * VZ Second Serial Port enable Phil Wilshire - * 2.4/2.5 port David McCullough - */ - -#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 -#include -#include - -/* (es) */ -/* note: perhaps we can murge these files, so that you can just - * define 1 of them, and they can sort that out for themselves - */ -#if defined(CONFIG_M68EZ328) -#include -#else -#if defined(CONFIG_M68VZ328) -#include -#else -#include -#endif /* CONFIG_M68VZ328 */ -#endif /* CONFIG_M68EZ328 */ - -#include "68328serial.h" - -/* Turn off usage of real serial interrupt code, to "support" Copilot */ -#ifdef CONFIG_XCOPILOT_BUGS -#undef USE_INTS -#else -#define USE_INTS -#endif - -static struct m68k_serial m68k_soft[NR_PORTS]; - -static unsigned int uart_irqs[NR_PORTS] = UART_IRQ_DEFNS; - -/* multiple ports are contiguous in memory */ -m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR; - -struct tty_struct m68k_ttys; -struct m68k_serial *m68k_consinfo = 0; - -#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */ - -struct tty_driver *serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* Debugging... DEBUG_INTR is bad to use when one of the zs - * lines is your console ;( - */ -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW - -#define RS_ISR_PASS_LIMIT 256 - -static void change_speed(struct m68k_serial *info); - -/* - * Setup for console. Argument comes from the boot command line. - */ - -/* note: this is messy, but it works, again, perhaps defined somewhere else?*/ -#ifdef CONFIG_M68VZ328 -#define CONSOLE_BAUD_RATE 19200 -#define DEFAULT_CBAUD B19200 -#endif - - -#ifndef CONSOLE_BAUD_RATE -#define CONSOLE_BAUD_RATE 9600 -#define DEFAULT_CBAUD B9600 -#endif - - -static int m68328_console_initted = 0; -static int m68328_console_baud = CONSOLE_BAUD_RATE; -static int m68328_console_cbaud = DEFAULT_CBAUD; - - -static inline int serial_paranoia_check(struct m68k_serial *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for serial struct %s in %s\n"; - static const char *badinfo = - "Warning: null m68k_serial for %s in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * This is used to figure out the divisor speeds and the timeouts - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 0 }; - -/* Sets or clears DTR/RTS on the requested line */ -static inline void m68k_rtsdtr(struct m68k_serial *ss, int set) -{ - if (set) { - /* set the RTS/CTS line */ - } else { - /* clear it */ - } - return; -} - -/* Utility routines */ -static inline int get_baud(struct m68k_serial *ss) -{ - unsigned long result = 115200; - unsigned short int baud = uart_addr[ss->line].ubaud; - if (GET_FIELD(baud, UBAUD_PRESCALER) == 0x38) result = 38400; - result >>= GET_FIELD(baud, UBAUD_DIVIDE); - - return result; -} - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rs_stop(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - uart->ustcnt &= ~USTCNT_TXEN; - local_irq_restore(flags); -} - -static int rs_put_char(char ch) -{ - int flags, loops = 0; - - local_irq_save(flags); - - while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) { - loops++; - udelay(5); - } - - UTX_TXDATA = ch; - udelay(5); - local_irq_restore(flags); - return 1; -} - -static void rs_start(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_start")) - return; - - local_irq_save(flags); - if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) { -#ifdef USE_INTS - uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK; -#else - uart->ustcnt |= USTCNT_TXEN; -#endif - } - local_irq_restore(flags); -} - -/* Drop into either the boot monitor or kadb upon receiving a break - * from keyboard/console input. - */ -static void batten_down_hatches(void) -{ - /* Drop into the debugger */ -} - -static void status_handle(struct m68k_serial *info, unsigned short status) -{ -#if 0 - if(status & DCD) { - if((info->port.tty->termios->c_cflag & CRTSCTS) && - ((info->curregs[3] & AUTO_ENAB)==0)) { - info->curregs[3] |= AUTO_ENAB; - info->pendregs[3] |= AUTO_ENAB; - write_zsreg(info->m68k_channel, 3, info->curregs[3]); - } - } else { - if((info->curregs[3] & AUTO_ENAB)) { - info->curregs[3] &= ~AUTO_ENAB; - info->pendregs[3] &= ~AUTO_ENAB; - write_zsreg(info->m68k_channel, 3, info->curregs[3]); - } - } -#endif - /* If this is console input and this is a - * 'break asserted' status change interrupt - * see if we can drop into the debugger - */ - if((status & URX_BREAK) && info->break_abort) - batten_down_hatches(); - return; -} - -static void receive_chars(struct m68k_serial *info, unsigned short rx) -{ - struct tty_struct *tty = info->port.tty; - m68328_uart *uart = &uart_addr[info->line]; - unsigned char ch, flag; - - /* - * This do { } while() loop will get ALL chars out of Rx FIFO - */ -#ifndef CONFIG_XCOPILOT_BUGS - do { -#endif - ch = GET_FIELD(rx, URX_RXDATA); - - if(info->is_cons) { - if(URX_BREAK & rx) { /* whee, break received */ - status_handle(info, rx); - return; -#ifdef CONFIG_MAGIC_SYSRQ - } else if (ch == 0x10) { /* ^P */ - show_state(); - show_free_areas(); - show_buffers(); -/* show_net_buffers(); */ - return; - } else if (ch == 0x12) { /* ^R */ - emergency_restart(); - return; -#endif /* CONFIG_MAGIC_SYSRQ */ - } - } - - if(!tty) - goto clear_and_exit; - - flag = TTY_NORMAL; - - if(rx & URX_PARITY_ERROR) { - flag = TTY_PARITY; - status_handle(info, rx); - } else if(rx & URX_OVRUN) { - flag = TTY_OVERRUN; - status_handle(info, rx); - } else if(rx & URX_FRAME_ERROR) { - flag = TTY_FRAME; - status_handle(info, rx); - } - tty_insert_flip_char(tty, ch, flag); -#ifndef CONFIG_XCOPILOT_BUGS - } while((rx = uart->urx.w) & URX_DATA_READY); -#endif - - tty_schedule_flip(tty); - -clear_and_exit: - return; -} - -static void transmit_chars(struct m68k_serial *info) -{ - m68328_uart *uart = &uart_addr[info->line]; - - if (info->x_char) { - /* Send next char */ - uart->utx.b.txdata = info->x_char; - info->x_char = 0; - goto clear_and_return; - } - - if((info->xmit_cnt <= 0) || info->port.tty->stopped) { - /* That's peculiar... TX ints off */ - uart->ustcnt &= ~USTCNT_TX_INTR_MASK; - goto clear_and_return; - } - - /* Send char */ - uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - - if (info->xmit_cnt < WAKEUP_CHARS) - schedule_work(&info->tqueue); - - if(info->xmit_cnt <= 0) { - /* All done for now... TX ints off */ - uart->ustcnt &= ~USTCNT_TX_INTR_MASK; - goto clear_and_return; - } - -clear_and_return: - /* Clear interrupt (should be auto)*/ - return; -} - -/* - * This is the serial driver's generic interrupt routine - */ -irqreturn_t rs_interrupt(int irq, void *dev_id) -{ - struct m68k_serial *info = dev_id; - m68328_uart *uart; - unsigned short rx; - unsigned short tx; - - uart = &uart_addr[info->line]; - rx = uart->urx.w; - -#ifdef USE_INTS - tx = uart->utx.w; - - if (rx & URX_DATA_READY) receive_chars(info, rx); - if (tx & UTX_TX_AVAIL) transmit_chars(info); -#else - receive_chars(info, rx); -#endif - return IRQ_HANDLED; -} - -static void do_softint(struct work_struct *work) -{ - struct m68k_serial *info = container_of(work, struct m68k_serial, tqueue); - struct tty_struct *tty; - - tty = info->port.tty; - if (!tty) - return; -#if 0 - if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { - tty_wakeup(tty); - } -#endif -} - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * - */ -static void do_serial_hangup(struct work_struct *work) -{ - struct m68k_serial *info = container_of(work, struct m68k_serial, tqueue_hangup); - struct tty_struct *tty; - - tty = info->port.tty; - if (!tty) - return; - - tty_hangup(tty); -} - - -static int startup(struct m68k_serial * info) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (info->flags & S_INITIALIZED) - return 0; - - if (!info->xmit_buf) { - info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL); - if (!info->xmit_buf) - return -ENOMEM; - } - - local_irq_save(flags); - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in change_speed()) - */ - - uart->ustcnt = USTCNT_UEN; - info->xmit_fifo_size = 1; - uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN; - (void)uart->urx.w; - - /* - * Finally, enable sequencing and interrupts - */ -#ifdef USE_INTS - uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | - USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK; -#else - uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK; -#endif - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - - change_speed(info); - - info->flags |= S_INITIALIZED; - local_irq_restore(flags); - return 0; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(struct m68k_serial * info) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - uart->ustcnt = 0; /* All off! */ - if (!(info->flags & S_INITIALIZED)) - return; - - local_irq_save(flags); - - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; - } - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->flags &= ~S_INITIALIZED; - local_irq_restore(flags); -} - -struct { - int divisor, prescale; -} -#ifndef CONFIG_M68VZ328 - hw_baud_table[18] = { - {0,0}, /* 0 */ - {0,0}, /* 50 */ - {0,0}, /* 75 */ - {0,0}, /* 110 */ - {0,0}, /* 134 */ - {0,0}, /* 150 */ - {0,0}, /* 200 */ - {7,0x26}, /* 300 */ - {6,0x26}, /* 600 */ - {5,0x26}, /* 1200 */ - {0,0}, /* 1800 */ - {4,0x26}, /* 2400 */ - {3,0x26}, /* 4800 */ - {2,0x26}, /* 9600 */ - {1,0x26}, /* 19200 */ - {0,0x26}, /* 38400 */ - {1,0x38}, /* 57600 */ - {0,0x38}, /* 115200 */ -}; -#else - hw_baud_table[18] = { - {0,0}, /* 0 */ - {0,0}, /* 50 */ - {0,0}, /* 75 */ - {0,0}, /* 110 */ - {0,0}, /* 134 */ - {0,0}, /* 150 */ - {0,0}, /* 200 */ - {0,0}, /* 300 */ - {7,0x26}, /* 600 */ - {6,0x26}, /* 1200 */ - {0,0}, /* 1800 */ - {5,0x26}, /* 2400 */ - {4,0x26}, /* 4800 */ - {3,0x26}, /* 9600 */ - {2,0x26}, /* 19200 */ - {1,0x26}, /* 38400 */ - {0,0x26}, /* 57600 */ - {1,0x38}, /* 115200 */ -}; -#endif -/* rate = 1036800 / ((65 - prescale) * (1<line]; - unsigned short port; - unsigned short ustcnt; - unsigned cflag; - int i; - - if (!info->port.tty || !info->port.tty->termios) - return; - cflag = info->port.tty->termios->c_cflag; - if (!(port = info->port)) - return; - - ustcnt = uart->ustcnt; - uart->ustcnt = ustcnt & ~USTCNT_TXEN; - - i = cflag & CBAUD; - if (i & CBAUDEX) { - i = (i & ~CBAUDEX) + B38400; - } - - info->baud = baud_table[i]; - uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | - PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); - - ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); - - if ((cflag & CSIZE) == CS8) - ustcnt |= USTCNT_8_7; - - if (cflag & CSTOPB) - ustcnt |= USTCNT_STOP; - - if (cflag & PARENB) - ustcnt |= USTCNT_PARITYEN; - if (cflag & PARODD) - ustcnt |= USTCNT_ODD_EVEN; - -#ifdef CONFIG_SERIAL_68328_RTS_CTS - if (cflag & CRTSCTS) { - uart->utx.w &= ~ UTX_NOCTS; - } else { - uart->utx.w |= UTX_NOCTS; - } -#endif - - ustcnt |= USTCNT_TXEN; - - uart->ustcnt = ustcnt; - return; -} - -/* - * Fair output driver allows a process to speak. - */ -static void rs_fair_output(void) -{ - int left; /* Output no more than that */ - unsigned long flags; - struct m68k_serial *info = &m68k_soft[0]; - char c; - - if (info == 0) return; - if (info->xmit_buf == 0) return; - - local_irq_save(flags); - left = info->xmit_cnt; - while (left != 0) { - c = info->xmit_buf[info->xmit_tail]; - info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - local_irq_restore(flags); - - rs_put_char(c); - - local_irq_save(flags); - left = min(info->xmit_cnt, left-1); - } - - /* Last character is being transmitted now (hopefully). */ - udelay(5); - - local_irq_restore(flags); - return; -} - -/* - * m68k_console_print is registered for printk. - */ -void console_print_68328(const char *p) -{ - char c; - - while((c=*(p++)) != 0) { - if(c == '\n') - rs_put_char('\r'); - rs_put_char(c); - } - - /* Comment this if you want to have a strict interrupt-driven output */ - rs_fair_output(); - - return; -} - -static void rs_set_ldisc(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_set_ldisc")) - return; - - info->is_cons = (tty->termios->c_line == N_TTY); - - printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off"); -} - -static void rs_flush_chars(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) - return; -#ifndef USE_INTS - for(;;) { -#endif - - /* Enable transmitter */ - local_irq_save(flags); - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) { - local_irq_restore(flags); - return; - } - -#ifdef USE_INTS - uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK; -#else - uart->ustcnt |= USTCNT_TXEN; -#endif - -#ifdef USE_INTS - if (uart->utx.w & UTX_TX_AVAIL) { -#else - if (1) { -#endif - /* Send char */ - uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - } - -#ifndef USE_INTS - while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); - } -#endif - local_irq_restore(flags); -} - -extern void console_printn(const char * b, int count); - -static int rs_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, total = 0; - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_write")) - return 0; - - if (!tty || !info->xmit_buf) - return 0; - - local_save_flags(flags); - while (1) { - local_irq_disable(); - c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - local_irq_restore(flags); - - if (c <= 0) - break; - - memcpy(info->xmit_buf + info->xmit_head, buf, c); - - local_irq_disable(); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - local_irq_restore(flags); - buf += c; - count -= c; - total += c; - } - - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { - /* Enable transmitter */ - local_irq_disable(); -#ifndef USE_INTS - while(info->xmit_cnt) { -#endif - - uart->ustcnt |= USTCNT_TXEN; -#ifdef USE_INTS - uart->ustcnt |= USTCNT_TX_INTR_MASK; -#else - while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); -#endif - if (uart->utx.w & UTX_TX_AVAIL) { - uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt--; - } - -#ifndef USE_INTS - } -#endif - local_irq_restore(flags); - } - - return total; -} - -static int rs_write_room(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "rs_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int rs_chars_in_buffer(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) - return 0; - return info->xmit_cnt; -} - -static void rs_flush_buffer(struct tty_struct *tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) - return; - local_irq_save(flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - local_irq_restore(flags); - tty_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void rs_throttle(struct tty_struct * tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_throttle")) - return; - - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); - - /* Turn off RTS line (do this atomic) */ -} - -static void rs_unthrottle(struct tty_struct * tty) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } - - /* Assert RTS line (do this atomic) */ -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct m68k_serial * info, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->port; - tmp.irq = info->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -static int set_serial_info(struct m68k_serial * info, - struct serial_struct * new_info) -{ - struct serial_struct new_serial; - struct m68k_serial old_info; - int retval = 0; - - if (!new_info) - return -EFAULT; - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~S_USR_MASK) != - (info->flags & ~S_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~S_USR_MASK) | - (new_serial.flags & S_USR_MASK)); - info->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - if (info->count > 1) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~S_FLAGS) | - (new_serial.flags & S_FLAGS)); - info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - -check_and_exit: - retval = startup(info); - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct m68k_serial * info, unsigned int *value) -{ -#ifdef CONFIG_SERIAL_68328_RTS_CTS - m68328_uart *uart = &uart_addr[info->line]; -#endif - unsigned char status; - unsigned long flags; - - local_irq_save(flags); -#ifdef CONFIG_SERIAL_68328_RTS_CTS - status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0; -#else - status = 0; -#endif - local_irq_restore(flags); - return put_user(status, value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break(struct m68k_serial * info, unsigned int duration) -{ - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - if (!info->port) - return; - local_irq_save(flags); -#ifdef USE_INTS - uart->utx.w |= UTX_SEND_BREAK; - msleep_interruptible(duration); - uart->utx.w &= ~UTX_SEND_BREAK; -#endif - local_irq_restore(flags); -} - -static int rs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int error; - struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; - int retval; - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, 250); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(100) : 250); - return 0; - case TIOCGSERIAL: - return get_serial_info(info, - (struct serial_struct *) arg); - case TIOCSSERIAL: - return set_serial_info(info, - (struct serial_struct *) arg); - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); - case TIOCSERGSTRUCT: - if (copy_to_user((struct m68k_serial *) arg, - info, sizeof(struct m68k_serial))) - return -EFAULT; - return 0; - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; - - change_speed(info); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rs_start(tty); - } - -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * S structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void rs_close(struct tty_struct *tty, struct file * filp) -{ - struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; - m68328_uart *uart = &uart_addr[info->line]; - unsigned long flags; - - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) - return; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - local_irq_restore(flags); - return; - } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - if (--info->count < 0) { - printk("rs_close: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; - } - if (info->count) { - local_irq_restore(flags); - return; - } - info->flags |= S_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->closing_wait != S_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - - uart->ustcnt &= ~USTCNT_RXEN; - uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK); - - shutdown(info); - rs_flush_buffer(tty); - - tty_ldisc_flush(tty); - tty->closing = 0; - info->event = 0; - info->port.tty = NULL; -#warning "This is not and has never been valid so fix it" -#if 0 - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } -#endif - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(S_NORMAL_ACTIVE|S_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void rs_hangup(struct tty_struct *tty) -{ - struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_hangup")) - return; - - rs_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~S_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct m68k_serial *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & S_CLOSING) { - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - if (info->flags & S_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= S_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); - - info->count--; - info->blocked_open++; - while (1) { - local_irq_disable(); - m68k_rtsdtr(info, 1); - local_irq_enable(); - current->state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) || - !(info->flags & S_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & S_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & S_CLOSING) && do_clocal) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - tty_unlock(); - schedule(); - tty_lock(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - - if (retval) - return retval; - info->flags |= S_NORMAL_ACTIVE; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its S structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -int rs_open(struct tty_struct *tty, struct file * filp) -{ - struct m68k_serial *info; - int retval, line; - - line = tty->index; - - if (line >= NR_PORTS || line < 0) /* we have exactly one */ - return -ENODEV; - - info = &m68k_soft[line]; - - if (serial_paranoia_check(info, tty->name, "rs_open")) - return -ENODEV; - - info->count++; - tty->driver_data = info; - info->port.tty = tty; - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) - return retval; - - return block_til_ready(tty, filp, info); -} - -/* Finally, routines used to initialize the serial driver. */ - -static void show_serial_version(void) -{ - printk("MC68328 serial driver version 1.00\n"); -} - -static const struct tty_operations rs_ops = { - .open = rs_open, - .close = rs_close, - .write = rs_write, - .flush_chars = rs_flush_chars, - .write_room = rs_write_room, - .chars_in_buffer = rs_chars_in_buffer, - .flush_buffer = rs_flush_buffer, - .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, - .set_termios = rs_set_termios, - .stop = rs_stop, - .start = rs_start, - .hangup = rs_hangup, - .set_ldisc = rs_set_ldisc, -}; - -/* rs_init inits the driver */ -static int __init -rs68328_init(void) -{ - int flags, i; - struct m68k_serial *info; - - serial_driver = alloc_tty_driver(NR_PORTS); - if (!serial_driver) - return -ENOMEM; - - show_serial_version(); - - /* Initialize the tty_driver structure */ - /* SPARC: Not all of this is exactly right for us. */ - - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - m68328_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &rs_ops); - - if (tty_register_driver(serial_driver)) { - put_tty_driver(serial_driver); - printk(KERN_ERR "Couldn't register serial driver\n"); - return -ENOMEM; - } - - local_irq_save(flags); - - for(i=0;imagic = SERIAL_MAGIC; - info->port = (int) &uart_addr[i]; - info->port.tty = NULL; - info->irq = uart_irqs[i]; - info->custom_divisor = 16; - info->close_delay = 50; - info->closing_wait = 3000; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - INIT_WORK(&info->tqueue, do_softint); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - info->line = i; - info->is_cons = 1; /* Means shortcuts work */ - - printk("%s%d at 0x%08x (irq = %d)", serial_driver->name, info->line, - info->port, info->irq); - printk(" is a builtin MC68328 UART\n"); - -#ifdef CONFIG_M68VZ328 - if (i > 0 ) - PJSEL &= 0xCF; /* PSW enable second port output */ -#endif - - if (request_irq(uart_irqs[i], - rs_interrupt, - IRQF_DISABLED, - "M68328_UART", info)) - panic("Unable to attach 68328 serial interrupt\n"); - } - local_irq_restore(flags); - return 0; -} - -module_init(rs68328_init); - - - -static void m68328_set_baud(void) -{ - unsigned short ustcnt; - int i; - - ustcnt = USTCNT; - USTCNT = ustcnt & ~USTCNT_TXEN; - -again: - for (i = 0; i < ARRAY_SIZE(baud_table); i++) - if (baud_table[i] == m68328_console_baud) - break; - if (i >= ARRAY_SIZE(baud_table)) { - m68328_console_baud = 9600; - goto again; - } - - UBAUD = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | - PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); - ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); - ustcnt |= USTCNT_8_7; - ustcnt |= USTCNT_TXEN; - USTCNT = ustcnt; - m68328_console_initted = 1; - return; -} - - -int m68328_console_setup(struct console *cp, char *arg) -{ - int i, n = CONSOLE_BAUD_RATE; - - if (!cp) - return(-1); - - if (arg) - n = simple_strtoul(arg,NULL,0); - - for (i = 0; i < ARRAY_SIZE(baud_table); i++) - if (baud_table[i] == n) - break; - if (i < ARRAY_SIZE(baud_table)) { - m68328_console_baud = n; - m68328_console_cbaud = 0; - if (i > 15) { - m68328_console_cbaud |= CBAUDEX; - i -= 15; - } - m68328_console_cbaud |= i; - } - - m68328_set_baud(); /* make sure baud rate changes */ - return(0); -} - - -static struct tty_driver *m68328_console_device(struct console *c, int *index) -{ - *index = c->index; - return serial_driver; -} - - -void m68328_console_write (struct console *co, const char *str, - unsigned int count) -{ - if (!m68328_console_initted) - m68328_set_baud(); - while (count--) { - if (*str == '\n') - rs_put_char('\r'); - rs_put_char( *str++ ); - } -} - - -static struct console m68328_driver = { - .name = "ttyS", - .write = m68328_console_write, - .device = m68328_console_device, - .setup = m68328_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - - -static int __init m68328_console_init(void) -{ - register_console(&m68328_driver); - return 0; -} - -console_initcall(m68328_console_init); diff --git a/drivers/serial/68328serial.h b/drivers/serial/68328serial.h deleted file mode 100644 index 664ceb0a158c..000000000000 --- a/drivers/serial/68328serial.h +++ /dev/null @@ -1,188 +0,0 @@ -/* 68328serial.h: Definitions for the mc68328 serial driver. - * - * Copyright (C) 1995 David S. Miller - * Copyright (C) 1998 Kenneth Albanowski - * Copyright (C) 1998, 1999 D. Jeff Dionne - * Copyright (C) 1999 Vladimir Gurevich - * - * VZ Support/Fixes Evan Stawnyczy - */ - -#ifndef _MC683XX_SERIAL_H -#define _MC683XX_SERIAL_H - - -struct serial_struct { - int type; - int line; - int port; - int irq; - int flags; - int xmit_fifo_size; - int custom_divisor; - int baud_base; - unsigned short close_delay; - char reserved_char[2]; - int hub6; /* FIXME: We don't have AT&T Hub6 boards! */ - unsigned short closing_wait; /* time to wait before closing */ - unsigned short closing_wait2; /* no longer used... */ - int reserved[4]; -}; - -/* - * For the close wait times, 0 means wait forever for serial port to - * flush its output. 65535 means don't wait at all. - */ -#define S_CLOSING_WAIT_INF 0 -#define S_CLOSING_WAIT_NONE 65535 - -/* - * Definitions for S_struct (and serial_struct) flags field - */ -#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes - on the callout port */ -#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ -#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */ -#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ - -#define S_SPD_MASK 0x0030 -#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ - -#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ -#define S_SPD_CUST 0x0030 /* Use user-specified divisor */ - -#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ -#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ -#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ -#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ -#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ - -#define S_FLAGS 0x0FFF /* Possible legal S flags */ -#define S_USR_MASK 0x0430 /* Legal flags that non-privileged - * users can set or reset */ - -/* Internal flags used only by kernel/chr_drv/serial.c */ -#define S_INITIALIZED 0x80000000 /* Serial port was initialized */ -#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ -#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ -#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ -#define S_CLOSING 0x08000000 /* Serial port is closing */ -#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */ - -/* Software state per channel */ - -#ifdef __KERNEL__ - -/* - * I believe this is the optimal setting that reduces the number of interrupts. - * At high speeds the output might become a little "bursted" (use USTCNT_TXHE - * if that bothers you), but in most cases it will not, since we try to - * transmit characters every time rs_interrupt is called. Thus, quite often - * you'll see that a receive interrupt occures before the transmit one. - * -- Vladimir Gurevich - */ -#define USTCNT_TX_INTR_MASK (USTCNT_TXEE) - -/* - * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special - * "Old data interrupt" which occures whenever the data stay in the FIFO - * longer than 30 bits time. This allows us to use FIFO without compromising - * latency. '328 does not have this feature and without the real 328-based - * board I would assume that RXRE is the safest setting. - * - * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of - * interrupts. RXFE (receive queue full) causes the system to lose data - * at least at 115200 baud - * - * If your board is busy doing other stuff, you might consider to use - * RXRE (data ready intrrupt) instead. - * - * The other option is to make these INTR masks run-time configurable, so - * that people can dynamically adapt them according to the current usage. - * -- Vladimir Gurevich - */ - -/* (es) */ -#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328) -#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN) -#elif defined(CONFIG_M68328) -#define USTCNT_RX_INTR_MASK (USTCNT_RXRE) -#else -#error Please, define the Rx interrupt events for your CPU -#endif -/* (/es) */ - -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -struct m68k_serial { - char soft_carrier; /* Use soft carrier on this channel */ - char break_abort; /* Is serial console in, so process brk/abrt */ - char is_cons; /* Is this our console. */ - - /* We need to know the current clock divisor - * to read the bps rate the chip has currently - * loaded. - */ - unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */ - int baud; - int magic; - int baud_base; - int port; - int irq; - int flags; /* defined in tty.h */ - int type; /* UART type */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int timeout; - int xmit_fifo_size; - int custom_divisor; - int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - unsigned long last_active; - int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - struct work_struct tqueue; - struct work_struct tqueue_hangup; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; -}; - - -#define SERIAL_MAGIC 0x5301 - -/* - * The size of the serial xmit buffer is 1 page, or 4096 bytes - */ -#define SERIAL_XMIT_SIZE 4096 - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define RS_EVENT_WRITE_WAKEUP 0 - -/* - * Define the number of ports supported and their irqs. - */ -#define NR_PORTS 1 -#define UART_IRQ_DEFNS {UART_IRQ_NUM} - -#endif /* __KERNEL__ */ -#endif /* !(_MC683XX_SERIAL_H) */ diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c deleted file mode 100644 index 88b13356ec10..000000000000 --- a/drivers/serial/68360serial.c +++ /dev/null @@ -1,2978 +0,0 @@ -/* - * UART driver for 68360 CPM SCC or SMC - * Copyright (c) 2000 D. Jeff Dionne , - * Copyright (c) 2000 Michael Leslie - * Copyright (c) 1997 Dan Malek - * - * I used the serial.c driver as the framework for this driver. - * Give credit to those guys. - * The original code was written for the MBX860 board. I tried to make - * it generic, but there may be some assumptions in the structures that - * have to be fixed later. - * To save porting time, I did not bother to change any object names - * that are not accessed outside of this file. - * It still needs lots of work........When it was easy, I included code - * to support the SCCs, but this has never been tested, nor is it complete. - * Only the SCCs support modem control, so that is not complete either. - * - * This module exports the following rs232 io functions: - * - * int rs_360_init(void); - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifdef CONFIG_KGDB -extern void breakpoint(void); -extern void set_debug_traps(void); -extern int kgdb_output_string (const char* s, unsigned int count); -#endif - - -/* #ifdef CONFIG_SERIAL_CONSOLE */ /* This seems to be a post 2.0 thing - mles */ -#include -#include - -/* this defines the index into rs_table for the port to use - */ -#ifndef CONFIG_SERIAL_CONSOLE_PORT -#define CONFIG_SERIAL_CONSOLE_PORT 1 /* ie SMC2 - note USE_SMC2 must be defined */ -#endif -/* #endif */ - -#if 0 -/* SCC2 for console - */ -#undef CONFIG_SERIAL_CONSOLE_PORT -#define CONFIG_SERIAL_CONSOLE_PORT 2 -#endif - - -#define TX_WAKEUP ASYNC_SHARE_IRQ - -static char *serial_name = "CPM UART driver"; -static char *serial_version = "0.03"; - -static struct tty_driver *serial_driver; -int serial_console_setup(struct console *co, char *options); - -/* - * Serial driver configuration section. Here are the various options: - */ -#define SERIAL_PARANOIA_CHECK -#define CONFIG_SERIAL_NOPAUSE_IO -#define SERIAL_DO_RESTART - -/* Set of debugging defines */ - -#undef SERIAL_DEBUG_INTR -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW -#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - -#define _INLINE_ inline - -#define DBG_CNT(s) - -/* We overload some of the items in the data structure to meet our - * needs. For example, the port address is the CPM parameter ram - * offset for the SCC or SMC. The maximum number of ports is 4 SCCs and - * 2 SMCs. The "hub6" field is used to indicate the channel number, with - * a flag indicating SCC or SMC, and the number is used as an index into - * the CPM parameter area for this device. - * The "type" field is currently set to 0, for PORT_UNKNOWN. It is - * not currently used. I should probably use it to indicate the port - * type of SMC or SCC. - * The SMCs do not support any modem control signals. - */ -#define smc_scc_num hub6 -#define NUM_IS_SCC ((int)0x00010000) -#define PORT_NUM(P) ((P) & 0x0000ffff) - - -#if defined (CONFIG_UCQUICC) - -volatile extern void *_periph_base; -/* sipex transceiver - * mode bits for are on pins - * - * SCC2 d16..19 - * SCC3 d20..23 - * SCC4 d24..27 - */ -#define SIPEX_MODE(n,m) ((m & 0x0f)<<(16+4*(n-1))) - -static uint sipex_mode_bits = 0x00000000; - -#endif - -/* There is no `serial_state' defined back here in 2.0. - * Try to get by with serial_struct - */ -/* #define serial_state serial_struct */ - -/* 2.4 -> 2.0 portability problem: async_icount in 2.4 has a few - * extras: */ - -#if 0 -struct async_icount_24 { - __u32 cts, dsr, rng, dcd, tx, rx; - __u32 frame, parity, overrun, brk; - __u32 buf_overrun; -} icount; -#endif - -#if 0 - -struct serial_state { - int magic; - int baud_base; - unsigned long port; - int irq; - int flags; - int hub6; - int type; - int line; - int revision; /* Chip revision (950) */ - int xmit_fifo_size; - int custom_divisor; - int count; - u8 *iomem_base; - u16 iomem_reg_shift; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ - struct async_icount_24 icount; - int io_type; - struct async_struct *info; -}; -#endif - -#define SSTATE_MAGIC 0x5302 - - - -/* SMC2 is sometimes used for low performance TDM interfaces. Define - * this as 1 if you want SMC2 as a serial port UART managed by this driver. - * Define this as 0 if you wish to use SMC2 for something else. - */ -#define USE_SMC2 1 - -#if 0 -/* Define SCC to ttySx mapping. */ -#define SCC_NUM_BASE (USE_SMC2 + 1) /* SCC base tty "number" */ - -/* Define which SCC is the first one to use for a serial port. These - * are 0-based numbers, i.e. this assumes the first SCC (SCC1) is used - * for Ethernet, and the first available SCC for serial UART is SCC2. - * NOTE: IF YOU CHANGE THIS, you have to change the PROFF_xxx and - * interrupt vectors in the table below to match. - */ -#define SCC_IDX_BASE 1 /* table index */ -#endif - - -/* Processors other than the 860 only get SMCs configured by default. - * Either they don't have SCCs or they are allocated somewhere else. - * Of course, there are now 860s without some SCCs, so we will need to - * address that someday. - * The Embedded Planet Multimedia I/O cards use TDM interfaces to the - * stereo codec parts, and we use SMC2 to help support that. - */ -static struct serial_state rs_table[] = { -/* type line PORT IRQ FLAGS smc_scc_num (F.K.A. hub6) */ - { 0, 0, PRSLOT_SMC1, CPMVEC_SMC1, 0, 0 } /* SMC1 ttyS0 */ -#if USE_SMC2 - ,{ 0, 0, PRSLOT_SMC2, CPMVEC_SMC2, 0, 1 } /* SMC2 ttyS1 */ -#endif - -#if defined(CONFIG_SERIAL_68360_SCC) - ,{ 0, 0, PRSLOT_SCC2, CPMVEC_SCC2, 0, (NUM_IS_SCC | 1) } /* SCC2 ttyS2 */ - ,{ 0, 0, PRSLOT_SCC3, CPMVEC_SCC3, 0, (NUM_IS_SCC | 2) } /* SCC3 ttyS3 */ - ,{ 0, 0, PRSLOT_SCC4, CPMVEC_SCC4, 0, (NUM_IS_SCC | 3) } /* SCC4 ttyS4 */ -#endif -}; - -#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) - -/* The number of buffer descriptors and their sizes. - */ -#define RX_NUM_FIFO 4 -#define RX_BUF_SIZE 32 -#define TX_NUM_FIFO 4 -#define TX_BUF_SIZE 32 - -#define CONSOLE_NUM_FIFO 2 -#define CONSOLE_BUF_SIZE 4 - -char *console_fifos[CONSOLE_NUM_FIFO * CONSOLE_BUF_SIZE]; - -/* The async_struct in serial.h does not really give us what we - * need, so define our own here. - */ -typedef struct serial_info { - int magic; - int flags; - - struct serial_state *state; - /* struct serial_struct *state; */ - /* struct async_struct *state; */ - - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int timeout; - int line; - int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - unsigned long last_active; - int blocked_open; /* # of blocked opens */ - struct work_struct tqueue; - struct work_struct tqueue_hangup; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - - -/* CPM Buffer Descriptor pointers. - */ - QUICC_BD *rx_bd_base; - QUICC_BD *rx_cur; - QUICC_BD *tx_bd_base; - QUICC_BD *tx_cur; -} ser_info_t; - - -/* since kmalloc_init() does not get called until much after this initialization: */ -static ser_info_t quicc_ser_info[NR_PORTS]; -static char rx_buf_pool[NR_PORTS * RX_NUM_FIFO * RX_BUF_SIZE]; -static char tx_buf_pool[NR_PORTS * TX_NUM_FIFO * TX_BUF_SIZE]; - -static void change_speed(ser_info_t *info); -static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout); - -static inline int serial_paranoia_check(ser_info_t *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for serial struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null async_struct for (%s) in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * This is used to figure out the divisor speeds and the timeouts, - * indexed by the termio value. The generic CPM functions are responsible - * for setting and assigning baud rate generators for us. - */ -static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; - -/* This sucks. There is a better way: */ -#if defined(CONFIG_CONSOLE_9600) - #define CONSOLE_BAUDRATE 9600 -#elif defined(CONFIG_CONSOLE_19200) - #define CONSOLE_BAUDRATE 19200 -#elif defined(CONFIG_CONSOLE_115200) - #define CONSOLE_BAUDRATE 115200 -#else - #warning "console baud rate undefined" - #define CONSOLE_BAUDRATE 9600 -#endif - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void rs_360_stop(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - int idx; - unsigned long flags; - volatile struct scc_regs *sccp; - volatile struct smc_regs *smcp; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_sccm &= ~UART_SCCM_TX; - } else { - /* smcp = &cpmp->cp_smc[idx]; */ - smcp = &pquicc->smc_regs[idx]; - smcp->smc_smcm &= ~SMCM_TX; - } - local_irq_restore(flags); -} - - -static void rs_360_start(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - int idx; - unsigned long flags; - volatile struct scc_regs *sccp; - volatile struct smc_regs *smcp; - - if (serial_paranoia_check(info, tty->name, "rs_stop")) - return; - - local_irq_save(flags); - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_sccm |= UART_SCCM_TX; - } else { - smcp = &pquicc->smc_regs[idx]; - smcp->smc_smcm |= SMCM_TX; - } - local_irq_restore(flags); -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * rs_interrupt(). They were separated out for readability's sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -static _INLINE_ void receive_chars(ser_info_t *info) -{ - struct tty_struct *tty = info->port.tty; - unsigned char ch, flag, *cp; - /*int ignored = 0;*/ - int i; - ushort status; - struct async_icount *icount; - /* struct async_icount_24 *icount; */ - volatile QUICC_BD *bdp; - - icount = &info->state->icount; - - /* Just loop through the closed BDs and copy the characters into - * the buffer. - */ - bdp = info->rx_cur; - for (;;) { - if (bdp->status & BD_SC_EMPTY) /* If this one is empty */ - break; /* we are all done */ - - /* The read status mask tell us what we should do with - * incoming characters, especially if errors occur. - * One special case is the use of BD_SC_EMPTY. If - * this is not set, we are supposed to be ignoring - * inputs. In this case, just mark the buffer empty and - * continue. - */ - if (!(info->read_status_mask & BD_SC_EMPTY)) { - bdp->status |= BD_SC_EMPTY; - bdp->status &= - ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); - - if (bdp->status & BD_SC_WRAP) - bdp = info->rx_bd_base; - else - bdp++; - continue; - } - - /* Get the number of characters and the buffer pointer. - */ - i = bdp->length; - /* cp = (unsigned char *)__va(bdp->buf); */ - cp = (char *)bdp->buf; - status = bdp->status; - - while (i-- > 0) { - ch = *cp++; - icount->rx++; - -#ifdef SERIAL_DEBUG_INTR - printk("DR%02x:%02x...", ch, status); -#endif - flag = TTY_NORMAL; - - if (status & (BD_SC_BR | BD_SC_FR | - BD_SC_PR | BD_SC_OV)) { - /* - * For statistics only - */ - if (status & BD_SC_BR) - icount->brk++; - else if (status & BD_SC_PR) - icount->parity++; - else if (status & BD_SC_FR) - icount->frame++; - if (status & BD_SC_OV) - icount->overrun++; - - /* - * Now check to see if character should be - * ignored, and mask off conditions which - * should be ignored. - if (status & info->ignore_status_mask) { - if (++ignored > 100) - break; - continue; - } - */ - status &= info->read_status_mask; - - if (status & (BD_SC_BR)) { -#ifdef SERIAL_DEBUG_INTR - printk("handling break...."); -#endif - *tty->flip.flag_buf_ptr = TTY_BREAK; - if (info->flags & ASYNC_SAK) - do_SAK(tty); - } else if (status & BD_SC_PR) - flag = TTY_PARITY; - else if (status & BD_SC_FR) - flag = TTY_FRAME; - } - tty_insert_flip_char(tty, ch, flag); - if (status & BD_SC_OV) - /* - * Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - - /* This BD is ready to be used again. Clear status. - * Get next BD. - */ - bdp->status |= BD_SC_EMPTY; - bdp->status &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); - - if (bdp->status & BD_SC_WRAP) - bdp = info->rx_bd_base; - else - bdp++; - } - - info->rx_cur = (QUICC_BD *)bdp; - - tty_schedule_flip(tty); -} - -static _INLINE_ void receive_break(ser_info_t *info) -{ - struct tty_struct *tty = info->port.tty; - - info->state->icount.brk++; - /* Check to see if there is room in the tty buffer for - * the break. If not, we exit now, losing the break. FIXME - */ - tty_insert_flip_char(tty, 0, TTY_BREAK); - tty_schedule_flip(tty); -} - -static _INLINE_ void transmit_chars(ser_info_t *info) -{ - - if ((info->flags & TX_WAKEUP) || - (info->port.tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) { - schedule_work(&info->tqueue); - } - -#ifdef SERIAL_DEBUG_INTR - printk("THRE..."); -#endif -} - -#ifdef notdef - /* I need to do this for the SCCs, so it is left as a reminder. - */ -static _INLINE_ void check_modem_status(struct async_struct *info) -{ - int status; - /* struct async_icount *icount; */ - struct async_icount_24 *icount; - - status = serial_in(info, UART_MSR); - - if (status & UART_MSR_ANY_DELTA) { - icount = &info->state->icount; - /* update input line counters */ - if (status & UART_MSR_TERI) - icount->rng++; - if (status & UART_MSR_DDSR) - icount->dsr++; - if (status & UART_MSR_DDCD) { - icount->dcd++; -#ifdef CONFIG_HARD_PPS - if ((info->flags & ASYNC_HARDPPS_CD) && - (status & UART_MSR_DCD)) - hardpps(); -#endif - } - if (status & UART_MSR_DCTS) - icount->cts++; - wake_up_interruptible(&info->delta_msr_wait); - } - - if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { -#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) - printk("ttys%d CD now %s...", info->line, - (status & UART_MSR_DCD) ? "on" : "off"); -#endif - if (status & UART_MSR_DCD) - wake_up_interruptible(&info->open_wait); - else { -#ifdef SERIAL_DEBUG_OPEN - printk("scheduling hangup..."); -#endif - queue_task(&info->tqueue_hangup, - &tq_scheduler); - } - } - if (info->flags & ASYNC_CTS_FLOW) { - if (info->port.tty->hw_stopped) { - if (status & UART_MSR_CTS) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx start..."); -#endif - info->port.tty->hw_stopped = 0; - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - return; - } - } else { - if (!(status & UART_MSR_CTS)) { -#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) - printk("CTS tx stop..."); -#endif - info->port.tty->hw_stopped = 1; - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_IER, info->IER); - } - } - } -} -#endif - -/* - * This is the serial driver's interrupt routine for a single port - */ -/* static void rs_360_interrupt(void *dev_id) */ /* until and if we start servicing irqs here */ -static void rs_360_interrupt(int vec, void *dev_id) -{ - u_char events; - int idx; - ser_info_t *info; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - info = dev_id; - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - events = sccp->scc_scce; - if (events & SCCM_RX) - receive_chars(info); - if (events & SCCM_TX) - transmit_chars(info); - sccp->scc_scce = events; - } else { - smcp = &pquicc->smc_regs[idx]; - events = smcp->smc_smce; - if (events & SMCM_BRKE) - receive_break(info); - if (events & SMCM_RX) - receive_chars(info); - if (events & SMCM_TX) - transmit_chars(info); - smcp->smc_smce = events; - } - -#ifdef SERIAL_DEBUG_INTR - printk("rs_interrupt_single(%d, %x)...", - info->state->smc_scc_num, events); -#endif -#ifdef modem_control - check_modem_status(info); -#endif - info->last_active = jiffies; -#ifdef SERIAL_DEBUG_INTR - printk("end.\n"); -#endif -} - - -/* - * ------------------------------------------------------------------- - * Here ends the serial interrupt routines. - * ------------------------------------------------------------------- - */ - - -static void do_softint(void *private_) -{ - ser_info_t *info = (ser_info_t *) private_; - struct tty_struct *tty; - - tty = info->port.tty; - if (!tty) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) - tty_wakeup(tty); -} - - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * - */ -static void do_serial_hangup(void *private_) -{ - struct async_struct *info = (struct async_struct *) private_; - struct tty_struct *tty; - - tty = info->port.tty; - if (!tty) - return; - - tty_hangup(tty); -} - - -static int startup(ser_info_t *info) -{ - unsigned long flags; - int retval=0; - int idx; - /*struct serial_state *state = info->state;*/ - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - volatile struct smc_uart_pram *up; - volatile struct uart_pram *scup; - - - local_irq_save(flags); - - if (info->flags & ASYNC_INITIALIZED) { - goto errout; - } - -#ifdef maybe - if (!state->port || !state->type) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - goto errout; - } -#endif - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d (irq %d)...", info->line, state->irq); -#endif - - -#ifdef modem_control - info->MCR = 0; - if (info->port.tty->termios->c_cflag & CBAUD) - info->MCR = UART_MCR_DTR | UART_MCR_RTS; -#endif - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - /* - * and set the speed of the serial port - */ - change_speed(info); - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - scup = &pquicc->pram[info->state->port].scc.pscc.u; - - scup->mrblr = RX_BUF_SIZE; - scup->max_idl = RX_BUF_SIZE; - - sccp->scc_sccm |= (UART_SCCM_TX | UART_SCCM_RX); - sccp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); - - } else { - smcp = &pquicc->smc_regs[idx]; - - /* Enable interrupts and I/O. - */ - smcp->smc_smcm |= (SMCM_RX | SMCM_TX); - smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN); - - /* We can tune the buffer length and idle characters - * to take advantage of the entire incoming buffer size. - * If mrblr is something other than 1, maxidl has to be - * non-zero or we never get an interrupt. The maxidl - * is the number of character times we wait after reception - * of the last character before we decide no more characters - * are coming. - */ - /* up = (smc_uart_t *)&pquicc->cp_dparam[state->port]; */ - /* holy unionized structures, Batman: */ - up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; - - up->mrblr = RX_BUF_SIZE; - up->max_idl = RX_BUF_SIZE; - - up->brkcr = 1; /* number of break chars */ - } - - info->flags |= ASYNC_INITIALIZED; - local_irq_restore(flags); - return 0; - -errout: - local_irq_restore(flags); - return retval; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(ser_info_t *info) -{ - unsigned long flags; - struct serial_state *state; - int idx; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - state = info->state; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....", info->line, - state->irq); -#endif - - local_irq_save(flags); - - idx = PORT_NUM(state->smc_scc_num); - if (state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_gsmr.w.low &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#ifdef CONFIG_SERIAL_CONSOLE - /* We can't disable the transmitter if this is the - * system console. - */ - if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT) -#endif - sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); - } else { - smcp = &pquicc->smc_regs[idx]; - - /* Disable interrupts and I/O. - */ - smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); -#ifdef CONFIG_SERIAL_CONSOLE - /* We can't disable the transmitter if this is the - * system console. - */ - if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT) -#endif - smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); - } - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); -} - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void change_speed(ser_info_t *info) -{ - int baud_rate; - unsigned cflag, cval, scval, prev_mode; - int i, bits, sbits, idx; - unsigned long flags; - struct serial_state *state; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - if (!info->port.tty || !info->port.tty->termios) - return; - cflag = info->port.tty->termios->c_cflag; - - state = info->state; - - /* Character length programmed into the mode register is the - * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, - * 1 or 2 stop bits, minus 1. - * The value 'bits' counts this for us. - */ - cval = 0; - scval = 0; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: bits = 5; break; - case CS6: bits = 6; break; - case CS7: bits = 7; break; - case CS8: bits = 8; break; - /* Never happens, but GCC is too dumb to figure it out */ - default: bits = 8; break; - } - sbits = bits - 5; - - if (cflag & CSTOPB) { - cval |= SMCMR_SL; /* Two stops */ - scval |= SCU_PMSR_SL; - bits++; - } - if (cflag & PARENB) { - cval |= SMCMR_PEN; - scval |= SCU_PMSR_PEN; - bits++; - } - if (!(cflag & PARODD)) { - cval |= SMCMR_PM_EVEN; - scval |= (SCU_PMSR_REVP | SCU_PMSR_TEVP); - } - - /* Determine divisor based on baud rate */ - i = cflag & CBAUD; - if (i >= (sizeof(baud_table)/sizeof(int))) - baud_rate = 9600; - else - baud_rate = baud_table[i]; - - info->timeout = (TX_BUF_SIZE*HZ*bits); - info->timeout += HZ/50; /* Add .02 seconds of slop */ - -#ifdef modem_control - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - if (info->flags & ASYNC_HARDPPS_CD) - info->IER |= UART_IER_MSI; - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - } else - info->flags &= ~ASYNC_CTS_FLOW; - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else { - info->flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; - } - serial_out(info, UART_IER, info->IER); -#endif - - /* - * Set up parity check flag - */ - info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); - if (I_INPCK(info->port.tty)) - info->read_status_mask |= BD_SC_FR | BD_SC_PR; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) - info->read_status_mask |= BD_SC_BR; - - /* - * Characters to ignore - */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= BD_SC_PR | BD_SC_FR; - if (I_IGNBRK(info->port.tty)) { - info->ignore_status_mask |= BD_SC_BR; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->port.tty)) - info->ignore_status_mask |= BD_SC_OV; - } - /* - * !!! ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - info->read_status_mask &= ~BD_SC_EMPTY; - local_irq_save(flags); - - /* Start bit has not been added (so don't, because we would just - * subtract it later), and we need to add one for the number of - * stops bits (there is always at least one). - */ - bits++; - idx = PORT_NUM(state->smc_scc_num); - if (state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_psmr = (sbits << 12) | scval; - } else { - smcp = &pquicc->smc_regs[idx]; - - /* Set the mode register. We want to keep a copy of the - * enables, because we want to put them back if they were - * present. - */ - prev_mode = smcp->smc_smcmr; - smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART; - smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); - } - - m360_cpm_setbrg((state - rs_table), baud_rate); - - local_irq_restore(flags); -} - -static void rs_360_put_char(struct tty_struct *tty, unsigned char ch) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - volatile QUICC_BD *bdp; - - if (serial_paranoia_check(info, tty->name, "rs_put_char")) - return 0; - - if (!tty) - return 0; - - bdp = info->tx_cur; - while (bdp->status & BD_SC_READY); - - /* *((char *)__va(bdp->buf)) = ch; */ - *((char *)bdp->buf) = ch; - bdp->length = 1; - bdp->status |= BD_SC_READY; - - /* Get next BD. - */ - if (bdp->status & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - - info->tx_cur = (QUICC_BD *)bdp; - return 1; - -} - -static int rs_360_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - ser_info_t *info = (ser_info_t *)tty->driver_data; - volatile QUICC_BD *bdp; - -#ifdef CONFIG_KGDB - /* Try to let stub handle output. Returns true if it did. */ - if (kgdb_output_string(buf, count)) - return ret; -#endif - - if (serial_paranoia_check(info, tty->name, "rs_write")) - return 0; - - if (!tty) - return 0; - - bdp = info->tx_cur; - - while (1) { - c = min(count, TX_BUF_SIZE); - - if (c <= 0) - break; - - if (bdp->status & BD_SC_READY) { - info->flags |= TX_WAKEUP; - break; - } - - /* memcpy(__va(bdp->buf), buf, c); */ - memcpy((void *)bdp->buf, buf, c); - - bdp->length = c; - bdp->status |= BD_SC_READY; - - buf += c; - count -= c; - ret += c; - - /* Get next BD. - */ - if (bdp->status & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - info->tx_cur = (QUICC_BD *)bdp; - } - return ret; -} - -static int rs_360_write_room(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "rs_write_room")) - return 0; - - if ((info->tx_cur->status & BD_SC_READY) == 0) { - info->flags &= ~TX_WAKEUP; - ret = TX_BUF_SIZE; - } - else { - info->flags |= TX_WAKEUP; - ret = 0; - } - return ret; -} - -/* I could track this with transmit counters....maybe later. -*/ -static int rs_360_chars_in_buffer(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) - return 0; - return 0; -} - -static void rs_360_flush_buffer(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) - return; - - /* There is nothing to "flush", whatever we gave the CPM - * is on its way out. - */ - tty_wakeup(tty); - info->flags &= ~TX_WAKEUP; -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void rs_360_send_xchar(struct tty_struct *tty, char ch) -{ - volatile QUICC_BD *bdp; - - ser_info_t *info = (ser_info_t *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "rs_send_char")) - return; - - bdp = info->tx_cur; - while (bdp->status & BD_SC_READY); - - /* *((char *)__va(bdp->buf)) = ch; */ - *((char *)bdp->buf) = ch; - bdp->length = 1; - bdp->status |= BD_SC_READY; - - /* Get next BD. - */ - if (bdp->status & BD_SC_WRAP) - bdp = info->tx_bd_base; - else - bdp++; - - info->tx_cur = (QUICC_BD *)bdp; -} - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void rs_360_throttle(struct tty_struct * tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_throttle")) - return; - - if (I_IXOFF(tty)) - rs_360_send_xchar(tty, STOP_CHAR(tty)); - -#ifdef modem_control - if (tty->termios->c_cflag & CRTSCTS) - info->MCR &= ~UART_MCR_RTS; - - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); -#endif -} - -static void rs_360_unthrottle(struct tty_struct * tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - rs_360_send_xchar(tty, START_CHAR(tty)); - } -#ifdef modem_control - if (tty->termios->c_cflag & CRTSCTS) - info->MCR |= UART_MCR_RTS; - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); -#endif -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -#ifdef maybe -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct async_struct * info, unsigned int *value) -{ - unsigned char status; - unsigned int result; - - local_irq_disable(); - status = serial_in(info, UART_LSR); - local_irq_enable(); - result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result,value); -} -#endif - -static int rs_360_tiocmget(struct tty_struct *tty, struct file *file) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned int result = 0; -#ifdef modem_control - unsigned char control, status; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - control = info->MCR; - local_irq_disable(); - status = serial_in(info, UART_MSR); - local_irq_enable(); - result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) - | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) -#ifdef TIOCM_OUT1 - | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) - | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) -#endif - | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) - | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) - | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) - | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); -#endif - return result; -} - -static int rs_360_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ -#ifdef modem_control - ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned int arg; - - if (serial_paranoia_check(info, tty->name, __func__)) - return -ENODEV; - - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - /* FIXME: locking on info->mcr */ - if (set & TIOCM_RTS) - info->mcr |= UART_MCR_RTS; - if (set & TIOCM_DTR) - info->mcr |= UART_MCR_DTR; - if (clear & TIOCM_RTS) - info->MCR &= ~UART_MCR_RTS; - if (clear & TIOCM_DTR) - info->MCR &= ~UART_MCR_DTR; - -#ifdef TIOCM_OUT1 - if (set & TIOCM_OUT1) - info->MCR |= UART_MCR_OUT1; - if (set & TIOCM_OUT2) - info->MCR |= UART_MCR_OUT2; - if (clear & TIOCM_OUT1) - info->MCR &= ~UART_MCR_OUT1; - if (clear & TIOCM_OUT2) - info->MCR &= ~UART_MCR_OUT2; -#endif - - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); -#endif - return 0; -} - -/* Sending a break is a two step process on the SMC/SCC. It is accomplished - * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT - * command. We take advantage of the begin/end functions to make this - * happen. - */ -static ushort smc_chan_map[] = { - CPM_CR_CH_SMC1, - CPM_CR_CH_SMC2 -}; - -static ushort scc_chan_map[] = { - CPM_CR_CH_SCC1, - CPM_CR_CH_SCC2, - CPM_CR_CH_SCC3, - CPM_CR_CH_SCC4 -}; - -static void begin_break(ser_info_t *info) -{ - volatile QUICC *cp; - ushort chan; - int idx; - - cp = pquicc; - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) - chan = scc_chan_map[idx]; - else - chan = smc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); -} - -static void end_break(ser_info_t *info) -{ - volatile QUICC *cp; - ushort chan; - int idx; - - cp = pquicc; - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) - chan = scc_chan_map[idx]; - else - chan = smc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break(ser_info_t *info, unsigned int duration) -{ -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("rs_send_break(%d) jiff=%lu...", duration, jiffies); -#endif - begin_break(info); - msleep_interruptible(duration); - end_break(info); -#ifdef SERIAL_DEBUG_SEND_BREAK - printk("done jiffies=%lu\n", jiffies); -#endif -} - - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int rs_360_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - struct async_icount cnow; - - local_irq_disable(); - cnow = info->state->icount; - local_irq_enable(); - - icount->cts = cnow.cts; - icount->dsr = cnow.dsr; - icount->rng = cnow.rng; - icount->dcd = cnow.dcd; - - return 0; -} - -static int rs_360_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - int error; - ser_info_t *info = (ser_info_t *)tty->driver_data; - int retval; - struct async_icount cnow; - /* struct async_icount_24 cnow;*/ /* kernel counter temps */ - struct serial_icounter_struct *p_cuser; /* user space */ - - if (serial_paranoia_check(info, tty->name, "rs_ioctl")) - return -ENODEV; - - if (cmd != TIOCMIWAIT) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - if (!arg) { - send_break(info, 250); /* 1/4 second */ - if (signal_pending(current)) - return -EINTR; - } - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (signal_pending(current)) - return -EINTR; - send_break(info, arg ? arg*100 : 250); - if (signal_pending(current)) - return -EINTR; - return 0; - case TIOCSBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - begin_break(info); - return 0; - case TIOCCBRK: - retval = tty_check_change(tty); - if (retval) - return retval; - end_break(info); - return 0; -#ifdef maybe - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); -#endif - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: -#ifdef modem_control - local_irq_disable(); - /* note the counters on entry */ - cprev = info->state->icount; - local_irq_enable(); - while (1) { - interruptible_sleep_on(&info->delta_msr_wait); - /* see if a signal did it */ - if (signal_pending(current)) - return -ERESTARTSYS; - local_irq_disable(); - cnow = info->state->icount; /* atomic copy */ - local_irq_enable(); - if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && - cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) - return -EIO; /* no change => error */ - 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)) ) { - return 0; - } - cprev = cnow; - } - /* NOTREACHED */ -#else - return 0; -#endif - - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -/* FIX UP modem control here someday...... -*/ -static void rs_360_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - - change_speed(info); - -#ifdef modem_control - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && - !(tty->termios->c_cflag & CBAUD)) { - info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && - (tty->termios->c_cflag & CBAUD)) { - info->MCR |= UART_MCR_DTR; - if (!tty->hw_stopped || - !(tty->termios->c_cflag & CRTSCTS)) { - info->MCR |= UART_MCR_RTS; - } - local_irq_disable(); - serial_out(info, UART_MCR, info->MCR); - local_irq_enable(); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rs_360_start(tty); - } -#endif - -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); -#endif -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void rs_360_close(struct tty_struct *tty, struct file * filp) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - /* struct async_state *state; */ - struct serial_state *state; - unsigned long flags; - int idx; - volatile struct smc_regs *smcp; - volatile struct scc_regs *sccp; - - if (!info || serial_paranoia_check(info, tty->name, "rs_close")) - return; - - state = info->state; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - DBG_CNT("before DEC-hung"); - local_irq_restore(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, state->count); -#endif - if ((tty->count == 1) && (state->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("rs_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; - } - if (--state->count < 0) { - printk("rs_close: bad serial port count for ttys%d: %d\n", - info->line, state->count); - state->count = 0; - } - if (state->count) { - DBG_CNT("before DEC-2"); - local_irq_restore(flags); - return; - } - info->flags |= ASYNC_CLOSING; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->read_status_mask &= ~BD_SC_EMPTY; - if (info->flags & ASYNC_INITIALIZED) { - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - sccp = &pquicc->scc_regs[idx]; - sccp->scc_sccm &= ~UART_SCCM_RX; - sccp->scc_gsmr.w.low &= ~SCC_GSMRL_ENR; - } else { - smcp = &pquicc->smc_regs[idx]; - smcp->smc_smcm &= ~SMCM_RX; - smcp->smc_smcmr &= ~SMCMR_REN; - } - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - rs_360_wait_until_sent(tty, info->timeout); - } - shutdown(info); - rs_360_flush_buffer(tty); - tty_ldisc_flush(tty); - tty->closing = 0; - info->event = 0; - info->port.tty = NULL; - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); -} - -/* - * rs_wait_until_sent() --- wait until the transmitter is empty - */ -static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - unsigned long orig_jiffies, char_time; - /*int lsr;*/ - volatile QUICC_BD *bdp; - - if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) - return; - -#ifdef maybe - if (info->state->type == PORT_UNKNOWN) - return; -#endif - - orig_jiffies = jiffies; - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = 1; - if (timeout) - char_time = min(char_time, (unsigned long)timeout); -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif - - /* We go through the loop at least once because we can't tell - * exactly when the last character exits the shifter. There can - * be at least two characters waiting to be sent after the buffers - * are empty. - */ - do { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); -#endif -/* current->counter = 0; make us low-priority */ - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && (time_after(jiffies, orig_jiffies + timeout))) - break; - /* The 'tx_cur' is really the next buffer to send. We - * have to back up to the previous BD and wait for it - * to go. This isn't perfect, because all this indicates - * is the buffer is available. There are still characters - * in the CPM FIFO. - */ - bdp = info->tx_cur; - if (bdp == info->tx_bd_base) - bdp += (TX_NUM_FIFO-1); - else - bdp--; - } while (bdp->status & BD_SC_READY); - current->state = TASK_RUNNING; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -static void rs_360_hangup(struct tty_struct *tty) -{ - ser_info_t *info = (ser_info_t *)tty->driver_data; - struct serial_state *state = info->state; - - if (serial_paranoia_check(info, tty->name, "rs_hangup")) - return; - - state = info->state; - - rs_360_flush_buffer(tty); - shutdown(info); - info->event = 0; - state->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - ser_info_t *info) -{ -#ifdef DO_THIS_LATER - DECLARE_WAITQUEUE(wait, current); -#endif - struct serial_state *state = info->state; - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - * If this is an SMC port, we don't have modem control to wait - * for, so just get out here. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR)) || - !(info->state->smc_scc_num & NUM_IS_SCC)) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, state->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; -#ifdef DO_THIS_LATER - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttys%d, count = %d\n", - state->line, state->count); -#endif - local_irq_disable(); - if (!tty_hung_up_p(filp)) - state->count--; - local_irq_enable(); - info->blocked_open++; - while (1) { - local_irq_disable(); - if (tty->termios->c_cflag & CBAUD) - serial_out(info, UART_MCR, - serial_inp(info, UART_MCR) | - (UART_MCR_DTR | UART_MCR_RTS)); - local_irq_enable(); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ASYNC_CLOSING) && - (do_clocal || (serial_in(info, UART_MSR) & - UART_MSR_DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif - tty_unlock(); - schedule(); - tty_lock(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - state->count++; - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttys%d, count = %d\n", - info->line, state->count); -#endif -#endif /* DO_THIS_LATER */ - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static int get_async_struct(int line, ser_info_t **ret_info) -{ - struct serial_state *sstate; - - sstate = rs_table + line; - if (sstate->info) { - sstate->count++; - *ret_info = (ser_info_t *)sstate->info; - return 0; - } - else { - return -ENOMEM; - } -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its async structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int rs_360_open(struct tty_struct *tty, struct file * filp) -{ - ser_info_t *info; - int retval, line; - - line = tty->index; - if ((line < 0) || (line >= NR_PORTS)) - return -ENODEV; - retval = get_async_struct(line, &info); - if (retval) - return retval; - if (serial_paranoia_check(info, tty->name, "rs_open")) - return -ENODEV; - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s, count = %d\n", tty->name, info->state->count); -#endif - tty->driver_data = info; - info->port.tty = tty; - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) - return retval; - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open returning after block_til_ready with %d\n", - retval); -#endif - return retval; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open %s successful...", tty->name); -#endif - return 0; -} - -/* - * /proc fs routines.... - */ - -static inline int line_info(char *buf, struct serial_state *state) -{ -#ifdef notdef - struct async_struct *info = state->info, scr_info; - char stat_buf[30], control, status; -#endif - int ret; - - ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", - state->line, - (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC", - (unsigned int)(state->port), state->irq); - - if (!state->port || (state->type == PORT_UNKNOWN)) { - ret += sprintf(buf+ret, "\n"); - return ret; - } - -#ifdef notdef - /* - * Figure out the current RS-232 lines - */ - if (!info) { - info = &scr_info; /* This is just for serial_{in,out} */ - - info->magic = SERIAL_MAGIC; - info->port = state->port; - info->flags = state->flags; - info->quot = 0; - info->port.tty = NULL; - } - local_irq_disable(); - status = serial_in(info, UART_MSR); - control = info ? info->MCR : serial_in(info, UART_MCR); - local_irq_enable(); - - stat_buf[0] = 0; - stat_buf[1] = 0; - if (control & UART_MCR_RTS) - strcat(stat_buf, "|RTS"); - if (status & UART_MSR_CTS) - strcat(stat_buf, "|CTS"); - if (control & UART_MCR_DTR) - strcat(stat_buf, "|DTR"); - if (status & UART_MSR_DSR) - strcat(stat_buf, "|DSR"); - if (status & UART_MSR_DCD) - strcat(stat_buf, "|CD"); - if (status & UART_MSR_RI) - strcat(stat_buf, "|RI"); - - if (info->quot) { - ret += sprintf(buf+ret, " baud:%d", - state->baud_base / info->quot); - } - - ret += sprintf(buf+ret, " tx:%d rx:%d", - state->icount.tx, state->icount.rx); - - if (state->icount.frame) - ret += sprintf(buf+ret, " fe:%d", state->icount.frame); - - if (state->icount.parity) - ret += sprintf(buf+ret, " pe:%d", state->icount.parity); - - if (state->icount.brk) - ret += sprintf(buf+ret, " brk:%d", state->icount.brk); - - if (state->icount.overrun) - ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); - - /* - * Last thing is the RS-232 status lines - */ - ret += sprintf(buf+ret, " %s\n", stat_buf+1); -#endif - return ret; -} - -int rs_360_read_proc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int i, len = 0; - off_t begin = 0; - - len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); - for (i = 0; i < NR_PORTS && len < 4000; i++) { - len += line_info(page + len, &rs_table[i]); - if (len+begin > off+count) - goto done; - if (len+begin < off) { - begin += len; - len = 0; - } - } - *eof = 1; -done: - if (off >= len+begin) - return 0; - *start = page + (begin-off); - return ((count < begin+len-off) ? count : begin+len-off); -} - -/* - * --------------------------------------------------------------------- - * rs_init() and friends - * - * rs_init() is called at boot-time to initialize the serial driver. - * --------------------------------------------------------------------- - */ - -/* - * This routine prints out the appropriate serial driver version - * number, and identifies which options were configured into this - * driver. - */ -static _INLINE_ void show_serial_version(void) -{ - printk(KERN_INFO "%s version %s\n", serial_name, serial_version); -} - - -/* - * The serial console driver used during boot. Note that these names - * clash with those found in "serial.c", so we currently can't support - * the 16xxx uarts and these at the same time. I will fix this to become - * an indirect function call from tty_io.c (or something). - */ - -#ifdef CONFIG_SERIAL_CONSOLE - -/* - * Print a string to the serial port trying not to disturb any possible - * real use of the port... - */ -static void my_console_write(int idx, const char *s, - unsigned count) -{ - struct serial_state *ser; - ser_info_t *info; - unsigned i; - QUICC_BD *bdp, *bdbase; - volatile struct smc_uart_pram *up; - volatile u_char *cp; - - ser = rs_table + idx; - - - /* If the port has been initialized for general use, we have - * to use the buffer descriptors allocated there. Otherwise, - * we simply use the single buffer allocated. - */ - if ((info = (ser_info_t *)ser->info) != NULL) { - bdp = info->tx_cur; - bdbase = info->tx_bd_base; - } - else { - /* Pointer to UART in parameter ram. - */ - /* up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; */ - up = &pquicc->pram[ser->port].scc.pothers.idma_smc.psmc.u; - - /* Get the address of the host memory buffer. - */ - bdp = bdbase = (QUICC_BD *)((uint)pquicc + (uint)up->tbase); - } - - /* - * We need to gracefully shut down the transmitter, disable - * interrupts, then send our bytes out. - */ - - /* - * Now, do each character. This is not as bad as it looks - * since this is a holding FIFO and not a transmitting FIFO. - * We could add the complexity of filling the entire transmit - * buffer, but we would just wait longer between accesses...... - */ - for (i = 0; i < count; i++, s++) { - /* Wait for transmitter fifo to empty. - * Ready indicates output is ready, and xmt is doing - * that, not that it is ready for us to send. - */ - while (bdp->status & BD_SC_READY); - - /* Send the character out. - */ - cp = bdp->buf; - *cp = *s; - - bdp->length = 1; - bdp->status |= BD_SC_READY; - - if (bdp->status & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - - /* if a LF, also do CR... */ - if (*s == 10) { - while (bdp->status & BD_SC_READY); - /* cp = __va(bdp->buf); */ - cp = bdp->buf; - *cp = 13; - bdp->length = 1; - bdp->status |= BD_SC_READY; - - if (bdp->status & BD_SC_WRAP) { - bdp = bdbase; - } - else { - bdp++; - } - } - } - - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - while (bdp->status & BD_SC_READY); - - if (info) - info->tx_cur = (QUICC_BD *)bdp; -} - -static void serial_console_write(struct console *c, const char *s, - unsigned count) -{ -#ifdef CONFIG_KGDB - /* Try to let stub handle output. Returns true if it did. */ - if (kgdb_output_string(s, count)) - return; -#endif - my_console_write(c->index, s, count); -} - - - -/*void console_print_68360(const char *p) -{ - const char *cp = p; - int i; - - for (i=0;cp[i]!=0;i++); - - serial_console_write (p, i); - - //Comment this if you want to have a strict interrupt-driven output - //rs_fair_output(); - - return; -}*/ - - - - - - -#ifdef CONFIG_XMON -int -xmon_360_write(const char *s, unsigned count) -{ - my_console_write(0, s, count); - return(count); -} -#endif - -#ifdef CONFIG_KGDB -void -putDebugChar(char ch) -{ - my_console_write(0, &ch, 1); -} -#endif - -/* - * Receive character from the serial port. This only works well - * before the port is initialized for real use. - */ -static int my_console_wait_key(int idx, int xmon, char *obuf) -{ - struct serial_state *ser; - u_char c, *cp; - ser_info_t *info; - QUICC_BD *bdp; - volatile struct smc_uart_pram *up; - int i; - - ser = rs_table + idx; - - /* Get the address of the host memory buffer. - * If the port has been initialized for general use, we must - * use information from the port structure. - */ - if ((info = (ser_info_t *)ser->info)) - bdp = info->rx_cur; - else - /* bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_rbase]; */ - bdp = (QUICC_BD *)((uint)pquicc + (uint)up->tbase); - - /* Pointer to UART in parameter ram. - */ - /* up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; */ - up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; - - /* - * We need to gracefully shut down the receiver, disable - * interrupts, then read the input. - * XMON just wants a poll. If no character, return -1, else - * return the character. - */ - if (!xmon) { - while (bdp->status & BD_SC_EMPTY); - } - else { - if (bdp->status & BD_SC_EMPTY) - return -1; - } - - cp = (char *)bdp->buf; - - if (obuf) { - i = c = bdp->length; - while (i-- > 0) - *obuf++ = *cp++; - } - else { - c = *cp; - } - bdp->status |= BD_SC_EMPTY; - - if (info) { - if (bdp->status & BD_SC_WRAP) { - bdp = info->rx_bd_base; - } - else { - bdp++; - } - info->rx_cur = (QUICC_BD *)bdp; - } - - return((int)c); -} - -static int serial_console_wait_key(struct console *co) -{ - return(my_console_wait_key(co->index, 0, NULL)); -} - -#ifdef CONFIG_XMON -int -xmon_360_read_poll(void) -{ - return(my_console_wait_key(0, 1, NULL)); -} - -int -xmon_360_read_char(void) -{ - return(my_console_wait_key(0, 0, NULL)); -} -#endif - -#ifdef CONFIG_KGDB -static char kgdb_buf[RX_BUF_SIZE], *kgdp; -static int kgdb_chars; - -unsigned char -getDebugChar(void) -{ - if (kgdb_chars <= 0) { - kgdb_chars = my_console_wait_key(0, 0, kgdb_buf); - kgdp = kgdb_buf; - } - kgdb_chars--; - - return(*kgdp++); -} - -void kgdb_interruptible(int state) -{ -} -void kgdb_map_scc(void) -{ - struct serial_state *ser; - uint mem_addr; - volatile QUICC_BD *bdp; - volatile smc_uart_t *up; - - cpmp = (cpm360_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); - - /* To avoid data cache CPM DMA coherency problems, allocate a - * buffer in the CPM DPRAM. This will work until the CPM and - * serial ports are initialized. At that time a memory buffer - * will be allocated. - * The port is already initialized from the boot procedure, all - * we do here is give it a different buffer and make it a FIFO. - */ - - ser = rs_table; - - /* Right now, assume we are using SMCs. - */ - up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; - - /* Allocate space for an input FIFO, plus a few bytes for output. - * Allocate bytes to maintain word alignment. - */ - mem_addr = (uint)(&cpmp->cp_dpmem[0x1000]); - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_rbase]; - bdp->buf = mem_addr; - - bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_tbase]; - bdp->buf = mem_addr+RX_BUF_SIZE; - - up->smc_mrblr = RX_BUF_SIZE; /* receive buffer length */ - up->smc_maxidl = RX_BUF_SIZE; -} -#endif - -static struct tty_struct *serial_console_device(struct console *c, int *index) -{ - *index = c->index; - return serial_driver; -} - - -struct console sercons = { - .name = "ttyS", - .write = serial_console_write, - .device = serial_console_device, - .wait_key = serial_console_wait_key, - .setup = serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = CONFIG_SERIAL_CONSOLE_PORT, -}; - - - -/* - * Register console. - */ -long console_360_init(long kmem_start, long kmem_end) -{ - register_console(&sercons); - /*register_console (console_print_68360); - 2.0.38 only required a write - function pointer. */ - return kmem_start; -} - -#endif - -/* Index in baud rate table of the default console baud rate. -*/ -static int baud_idx; - -static const struct tty_operations rs_360_ops = { - .owner = THIS_MODULE, - .open = rs_360_open, - .close = rs_360_close, - .write = rs_360_write, - .put_char = rs_360_put_char, - .write_room = rs_360_write_room, - .chars_in_buffer = rs_360_chars_in_buffer, - .flush_buffer = rs_360_flush_buffer, - .ioctl = rs_360_ioctl, - .throttle = rs_360_throttle, - .unthrottle = rs_360_unthrottle, - /* .send_xchar = rs_360_send_xchar, */ - .set_termios = rs_360_set_termios, - .stop = rs_360_stop, - .start = rs_360_start, - .hangup = rs_360_hangup, - /* .wait_until_sent = rs_360_wait_until_sent, */ - /* .read_proc = rs_360_read_proc, */ - .tiocmget = rs_360_tiocmget, - .tiocmset = rs_360_tiocmset, -}; - -static int __init rs_360_init(void) -{ - struct serial_state * state; - ser_info_t *info; - void *mem_addr; - uint dp_addr, iobits; - int i, j, idx; - ushort chan; - QUICC_BD *bdp; - volatile QUICC *cp; - volatile struct smc_regs *sp; - volatile struct smc_uart_pram *up; - volatile struct scc_regs *scp; - volatile struct uart_pram *sup; - /* volatile immap_t *immap; */ - - serial_driver = alloc_tty_driver(NR_PORTS); - if (!serial_driver) - return -1; - - show_serial_version(); - - serial_driver->name = "ttyS"; - serial_driver->major = TTY_MAJOR; - serial_driver->minor_start = 64; - serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - serial_driver->subtype = SERIAL_TYPE_NORMAL; - serial_driver->init_termios = tty_std_termios; - serial_driver->init_termios.c_cflag = - baud_idx | CS8 | CREAD | HUPCL | CLOCAL; - serial_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(serial_driver, &rs_360_ops); - - if (tty_register_driver(serial_driver)) - panic("Couldn't register serial driver\n"); - - cp = pquicc; /* Get pointer to Communication Processor */ - /* immap = (immap_t *)IMAP_ADDR; */ /* and to internal registers */ - - - /* Configure SCC2, SCC3, and SCC4 instead of port A parallel I/O. - */ - /* The "standard" configuration through the 860. - */ -/* immap->im_ioport.iop_papar |= 0x00fc; */ -/* immap->im_ioport.iop_padir &= ~0x00fc; */ -/* immap->im_ioport.iop_paodr &= ~0x00fc; */ - cp->pio_papar |= 0x00fc; - cp->pio_padir &= ~0x00fc; - /* cp->pio_paodr &= ~0x00fc; */ - - - /* Since we don't yet do modem control, connect the port C pins - * as general purpose I/O. This will assert CTS and CD for the - * SCC ports. - */ - /* FIXME: see 360um p.7-365 and 860um p.34-12 - * I can't make sense of these bits - mleslie*/ -/* immap->im_ioport.iop_pcdir |= 0x03c6; */ -/* immap->im_ioport.iop_pcpar &= ~0x03c6; */ - -/* cp->pio_pcdir |= 0x03c6; */ -/* cp->pio_pcpar &= ~0x03c6; */ - - - - /* Connect SCC2 and SCC3 to NMSI. Connect BRG3 to SCC2 and - * BRG4 to SCC3. - */ - cp->si_sicr &= ~0x00ffff00; - cp->si_sicr |= 0x001b1200; - -#ifdef CONFIG_PP04 - /* Frequentis PP04 forced to RS-232 until we know better. - * Port C 12 and 13 low enables RS-232 on SCC3 and SCC4. - */ - immap->im_ioport.iop_pcdir |= 0x000c; - immap->im_ioport.iop_pcpar &= ~0x000c; - immap->im_ioport.iop_pcdat &= ~0x000c; - - /* This enables the TX driver. - */ - cp->cp_pbpar &= ~0x6000; - cp->cp_pbdat &= ~0x6000; -#endif - - for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { - state->magic = SSTATE_MAGIC; - state->line = i; - state->type = PORT_UNKNOWN; - state->custom_divisor = 0; - state->close_delay = 5*HZ/10; - state->closing_wait = 30*HZ; - state->icount.cts = state->icount.dsr = - state->icount.rng = state->icount.dcd = 0; - state->icount.rx = state->icount.tx = 0; - state->icount.frame = state->icount.parity = 0; - state->icount.overrun = state->icount.brk = 0; - printk(KERN_INFO "ttyS%d at irq 0x%02x is an %s\n", - i, (unsigned int)(state->irq), - (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC"); - -#ifdef CONFIG_SERIAL_CONSOLE - /* If we just printed the message on the console port, and - * we are about to initialize it for general use, we have - * to wait a couple of character times for the CR/NL to - * make it out of the transmit buffer. - */ - if (i == CONFIG_SERIAL_CONSOLE_PORT) - mdelay(8); - - -/* idx = PORT_NUM(info->state->smc_scc_num); */ -/* if (info->state->smc_scc_num & NUM_IS_SCC) */ -/* chan = scc_chan_map[idx]; */ -/* else */ -/* chan = smc_chan_map[idx]; */ - -/* cp->cp_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; */ -/* while (cp->cp_cr & CPM_CR_FLG); */ - -#endif - /* info = kmalloc(sizeof(ser_info_t), GFP_KERNEL); */ - info = &quicc_ser_info[i]; - if (info) { - memset (info, 0, sizeof(ser_info_t)); - info->magic = SERIAL_MAGIC; - info->line = i; - info->flags = state->flags; - INIT_WORK(&info->tqueue, do_softint, info); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup, info); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - info->state = state; - state->info = (struct async_struct *)info; - - /* We need to allocate a transmit and receive buffer - * descriptors from dual port ram, and a character - * buffer area from host mem. - */ - dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * RX_NUM_FIFO); - - /* Allocate space for FIFOs in the host memory. - * (for now this is from a static array of buffers :( - */ - /* mem_addr = m360_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); */ - /* mem_addr = kmalloc (RX_NUM_FIFO * RX_BUF_SIZE, GFP_BUFFER); */ - mem_addr = &rx_buf_pool[i * RX_NUM_FIFO * RX_BUF_SIZE]; - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - bdp = (QUICC_BD *)((uint)pquicc + dp_addr); - info->rx_cur = info->rx_bd_base = bdp; - - /* initialize rx buffer descriptors */ - for (j=0; j<(RX_NUM_FIFO-1); j++) { - bdp->buf = &rx_buf_pool[(i * RX_NUM_FIFO + j ) * RX_BUF_SIZE]; - bdp->status = BD_SC_EMPTY | BD_SC_INTRPT; - mem_addr += RX_BUF_SIZE; - bdp++; - } - bdp->buf = &rx_buf_pool[(i * RX_NUM_FIFO + j ) * RX_BUF_SIZE]; - bdp->status = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; - - - idx = PORT_NUM(info->state->smc_scc_num); - if (info->state->smc_scc_num & NUM_IS_SCC) { - -#if defined (CONFIG_UCQUICC) && 1 - /* set the transceiver mode to RS232 */ - sipex_mode_bits &= ~(uint)SIPEX_MODE(idx,0x0f); /* clear current mode */ - sipex_mode_bits |= (uint)SIPEX_MODE(idx,0x02); - *(uint *)_periph_base = sipex_mode_bits; - /* printk ("sipex bits = 0x%08x\n", sipex_mode_bits); */ -#endif - } - - dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * TX_NUM_FIFO); - - /* Allocate space for FIFOs in the host memory. - */ - /* mem_addr = m360_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); */ - /* mem_addr = kmalloc (TX_NUM_FIFO * TX_BUF_SIZE, GFP_BUFFER); */ - mem_addr = &tx_buf_pool[i * TX_NUM_FIFO * TX_BUF_SIZE]; - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - /* bdp = (QUICC_BD *)&cp->cp_dpmem[dp_addr]; */ - bdp = (QUICC_BD *)((uint)pquicc + dp_addr); - info->tx_cur = info->tx_bd_base = (QUICC_BD *)bdp; - - /* initialize tx buffer descriptors */ - for (j=0; j<(TX_NUM_FIFO-1); j++) { - bdp->buf = &tx_buf_pool[(i * TX_NUM_FIFO + j ) * TX_BUF_SIZE]; - bdp->status = BD_SC_INTRPT; - mem_addr += TX_BUF_SIZE; - bdp++; - } - bdp->buf = &tx_buf_pool[(i * TX_NUM_FIFO + j ) * TX_BUF_SIZE]; - bdp->status = (BD_SC_WRAP | BD_SC_INTRPT); - - if (info->state->smc_scc_num & NUM_IS_SCC) { - scp = &pquicc->scc_regs[idx]; - sup = &pquicc->pram[info->state->port].scc.pscc.u; - sup->rbase = dp_addr; - sup->tbase = dp_addr; - - /* Set up the uart parameters in the - * parameter ram. - */ - sup->rfcr = SMC_EB; - sup->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single - * character interrupts. Using idle character - * time requires some additional tuning. - */ - sup->mrblr = 1; - sup->max_idl = 0; - sup->brkcr = 1; - sup->parec = 0; - sup->frmer = 0; - sup->nosec = 0; - sup->brkec = 0; - sup->uaddr1 = 0; - sup->uaddr2 = 0; - sup->toseq = 0; - { - int i; - for (i=0;i<8;i++) - sup->cc[i] = 0x8000; - } - sup->rccm = 0xc0ff; - - /* Send the CPM an initialize command. - */ - chan = scc_chan_map[idx]; - - /* execute the INIT RX & TX PARAMS command for this channel. */ - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - scp->scc_gsmr.w.high = 0; - scp->scc_gsmr.w.low = - (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); - - /* Disable all interrupts and clear all pending - * events. - */ - scp->scc_sccm = 0; - scp->scc_scce = 0xffff; - scp->scc_dsr = 0x7e7e; - scp->scc_psmr = 0x3000; - - /* If the port is the console, enable Rx and Tx. - */ -#ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - scp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); -#endif - } - else { - /* Configure SMCs Tx/Rx instead of port B - * parallel I/O. - */ - up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; - up->rbase = dp_addr; - - iobits = 0xc0 << (idx * 4); - cp->pip_pbpar |= iobits; - cp->pip_pbdir &= ~iobits; - cp->pip_pbodr &= ~iobits; - - - /* Connect the baud rate generator to the - * SMC based upon index in rs_table. Also - * make sure it is connected to NMSI. - */ - cp->si_simode &= ~(0xffff << (idx * 16)); - cp->si_simode |= (i << ((idx * 16) + 12)); - - up->tbase = dp_addr; - - /* Set up the uart parameters in the - * parameter ram. - */ - up->rfcr = SMC_EB; - up->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single - * character interrupts. Using idle character - * time requires some additional tuning. - */ - up->mrblr = 1; - up->max_idl = 0; - up->brkcr = 1; - - /* Send the CPM an initialize command. - */ - chan = smc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, - CPM_CR_INIT_TRX) | CPM_CR_FLG; -#ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - printk(""); -#endif - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp = &cp->smc_regs[idx]; - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* Disable all interrupts and clear all pending - * events. - */ - sp->smc_smcm = 0; - sp->smc_smce = 0xff; - - /* If the port is the console, enable Rx and Tx. - */ -#ifdef CONFIG_SERIAL_CONSOLE - if (i == CONFIG_SERIAL_CONSOLE_PORT) - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; -#endif - } - - /* Install interrupt handler. - */ - /* cpm_install_handler(IRQ_MACHSPEC | state->irq, rs_360_interrupt, info); */ - /*request_irq(IRQ_MACHSPEC | state->irq, rs_360_interrupt, */ - request_irq(state->irq, rs_360_interrupt, - IRQ_FLG_LOCK, "ttyS", (void *)info); - - /* Set up the baud rate generator. - */ - m360_cpm_setbrg(i, baud_table[baud_idx]); - - } - } - - return 0; -} -module_init(rs_360_init); - -/* This must always be called before the rs_360_init() function, otherwise - * it blows away the port control information. - */ -//static int __init serial_console_setup( struct console *co, char *options) -int serial_console_setup( struct console *co, char *options) -{ - struct serial_state *ser; - uint mem_addr, dp_addr, bidx, idx, iobits; - ushort chan; - QUICC_BD *bdp; - volatile QUICC *cp; - volatile struct smc_regs *sp; - volatile struct scc_regs *scp; - volatile struct smc_uart_pram *up; - volatile struct uart_pram *sup; - -/* mleslie TODO: - * add something to the 68k bootloader to store a desired initial console baud rate */ - -/* bd_t *bd; */ /* a board info struct used by EPPC-bug */ -/* bd = (bd_t *)__res; */ - - for (bidx = 0; bidx < (sizeof(baud_table) / sizeof(int)); bidx++) - /* if (bd->bi_baudrate == baud_table[bidx]) */ - if (CONSOLE_BAUDRATE == baud_table[bidx]) - break; - - /* co->cflag = CREAD|CLOCAL|bidx|CS8; */ - baud_idx = bidx; - - ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT; - - cp = pquicc; /* Get pointer to Communication Processor */ - - idx = PORT_NUM(ser->smc_scc_num); - if (ser->smc_scc_num & NUM_IS_SCC) { - - /* TODO: need to set up SCC pin assignment etc. here */ - - } - else { - iobits = 0xc0 << (idx * 4); - cp->pip_pbpar |= iobits; - cp->pip_pbdir &= ~iobits; - cp->pip_pbodr &= ~iobits; - - /* Connect the baud rate generator to the - * SMC based upon index in rs_table. Also - * make sure it is connected to NMSI. - */ - cp->si_simode &= ~(0xffff << (idx * 16)); - cp->si_simode |= (idx << ((idx * 16) + 12)); - } - - /* When we get here, the CPM has been reset, so we need - * to configure the port. - * We need to allocate a transmit and receive buffer descriptor - * from dual port ram, and a character buffer area from host mem. - */ - - /* Allocate space for two buffer descriptors in the DP ram. - */ - dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * CONSOLE_NUM_FIFO); - - /* Allocate space for two 2 byte FIFOs in the host memory. - */ - /* mem_addr = m360_cpm_hostalloc(8); */ - mem_addr = (uint)console_fifos; - - - /* Set the physical address of the host memory buffers in - * the buffer descriptors. - */ - /* bdp = (QUICC_BD *)&cp->cp_dpmem[dp_addr]; */ - bdp = (QUICC_BD *)((uint)pquicc + dp_addr); - bdp->buf = (char *)mem_addr; - (bdp+1)->buf = (char *)(mem_addr+4); - - /* For the receive, set empty and wrap. - * For transmit, set wrap. - */ - bdp->status = BD_SC_EMPTY | BD_SC_WRAP; - (bdp+1)->status = BD_SC_WRAP; - - /* Set up the uart parameters in the parameter ram. - */ - if (ser->smc_scc_num & NUM_IS_SCC) { - scp = &cp->scc_regs[idx]; - /* sup = (scc_uart_t *)&cp->cp_dparam[ser->port]; */ - sup = &pquicc->pram[ser->port].scc.pscc.u; - - sup->rbase = dp_addr; - sup->tbase = dp_addr + sizeof(QUICC_BD); - - /* Set up the uart parameters in the - * parameter ram. - */ - sup->rfcr = SMC_EB; - sup->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single - * character interrupts. Using idle character - * time requires some additional tuning. - */ - sup->mrblr = 1; - sup->max_idl = 0; - sup->brkcr = 1; - sup->parec = 0; - sup->frmer = 0; - sup->nosec = 0; - sup->brkec = 0; - sup->uaddr1 = 0; - sup->uaddr2 = 0; - sup->toseq = 0; - { - int i; - for (i=0;i<8;i++) - sup->cc[i] = 0x8000; - } - sup->rccm = 0xc0ff; - - /* Send the CPM an initialize command. - */ - chan = scc_chan_map[idx]; - - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - scp->scc_gsmr.w.high = 0; - scp->scc_gsmr.w.low = - (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); - - /* Disable all interrupts and clear all pending - * events. - */ - scp->scc_sccm = 0; - scp->scc_scce = 0xffff; - scp->scc_dsr = 0x7e7e; - scp->scc_psmr = 0x3000; - - scp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); - - } - else { - /* up = (smc_uart_t *)&cp->cp_dparam[ser->port]; */ - up = &pquicc->pram[ser->port].scc.pothers.idma_smc.psmc.u; - - up->rbase = dp_addr; /* Base of receive buffer desc. */ - up->tbase = dp_addr+sizeof(QUICC_BD); /* Base of xmt buffer desc. */ - up->rfcr = SMC_EB; - up->tfcr = SMC_EB; - - /* Set this to 1 for now, so we get single character interrupts. - */ - up->mrblr = 1; /* receive buffer length */ - up->max_idl = 0; /* wait forever for next char */ - - /* Send the CPM an initialize command. - */ - chan = smc_chan_map[idx]; - cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; - while (cp->cp_cr & CPM_CR_FLG); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - sp = &cp->smc_regs[idx]; - sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; - - /* And finally, enable Rx and Tx. - */ - sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; - } - - /* Set up the baud rate generator. - */ - /* m360_cpm_setbrg((ser - rs_table), bd->bi_baudrate); */ - m360_cpm_setbrg((ser - rs_table), CONSOLE_BAUDRATE); - - return 0; -} - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c deleted file mode 100644 index b25e6e490530..000000000000 --- a/drivers/serial/8250.c +++ /dev/null @@ -1,3377 +0,0 @@ -/* - * linux/drivers/char/8250.c - * - * Driver for 8250/16550-type serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King. - * - * 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. - * - * A note about mapbase / membase - * - * mapbase is the physical address of the IO port. - * membase is an 'ioremapped' cookie. - */ - -#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "8250.h" - -#ifdef CONFIG_SPARC -#include "suncore.h" -#endif - -/* - * Configuration: - * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option - * is unsafe when used on edge-triggered interrupts. - */ -static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; - -static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; - -static struct uart_driver serial8250_reg; - -static int serial_index(struct uart_port *port) -{ - return (serial8250_reg.minor - 64) + port->line; -} - -static unsigned int skip_txen_test; /* force skip of txen test at init time */ - -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - -#define PASS_LIMIT 256 - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - - -/* - * We default to IRQ0 for the "no irq" hack. Some - * machine types want others as well - they're free - * to redefine this in their header file. - */ -#define is_real_interrupt(irq) ((irq) != 0) - -#ifdef CONFIG_SERIAL_8250_DETECT_IRQ -#define CONFIG_SERIAL_DETECT_IRQ 1 -#endif -#ifdef CONFIG_SERIAL_8250_MANY_PORTS -#define CONFIG_SERIAL_MANY_PORTS 1 -#endif - -/* - * HUB6 is always on. This will be removed once the header - * files have been cleaned. - */ -#define CONFIG_HUB6 1 - -#include -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ -#ifndef SERIAL_PORT_DFNS -#define SERIAL_PORT_DFNS -#endif - -static const struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS /* defined in asm/serial.h */ -}; - -#define UART_NR CONFIG_SERIAL_8250_NR_UARTS - -#ifdef CONFIG_SERIAL_8250_RSA - -#define PORT_RSA_MAX 4 -static unsigned long probe_rsa[PORT_RSA_MAX]; -static unsigned int probe_rsa_count; -#endif /* CONFIG_SERIAL_8250_RSA */ - -struct uart_8250_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short capabilities; /* port capabilities */ - unsigned short bugs; /* port bugs */ - unsigned int tx_loadsz; /* transmit fifo load size */ - 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 cur_iotype; /* Running I/O type */ - - /* - * Some bits in registers are cleared on a read, so they must - * be saved whenever the register is read but the bits will not - * be immediately processed. - */ -#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS - unsigned char lsr_saved_flags; -#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA - unsigned char msr_saved_flags; -}; - -struct irq_info { - struct hlist_node node; - int irq; - spinlock_t lock; /* Protects list not the hash */ - struct list_head *head; -}; - -#define NR_IRQ_HASH 32 /* Can be adjusted later */ -static struct hlist_head irq_lists[NR_IRQ_HASH]; -static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */ - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial8250_config uart_config[] = { - [PORT_UNKNOWN] = { - .name = "unknown", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_8250] = { - .name = "8250", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16450] = { - .name = "16450", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16550] = { - .name = "16550", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16550A] = { - .name = "16550A", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, - [PORT_CIRRUS] = { - .name = "Cirrus", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16650] = { - .name = "ST16650", - .fifo_size = 1, - .tx_loadsz = 1, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16650V2] = { - .name = "ST16650V2", - .fifo_size = 32, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | - UART_FCR_T_TRIG_00, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16750] = { - .name = "TI16750", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | - UART_FCR7_64BYTE, - .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, - }, - [PORT_STARTECH] = { - .name = "Startech", - .fifo_size = 1, - .tx_loadsz = 1, - }, - [PORT_16C950] = { - .name = "16C950/954", - .fifo_size = 128, - .tx_loadsz = 128, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16654] = { - .name = "ST16654", - .fifo_size = 64, - .tx_loadsz = 32, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | - UART_FCR_T_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_16850] = { - .name = "XR16850", - .fifo_size = 128, - .tx_loadsz = 128, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, - }, - [PORT_RSA] = { - .name = "RSA", - .fifo_size = 2048, - .tx_loadsz = 2048, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11, - .flags = UART_CAP_FIFO, - }, - [PORT_NS16550A] = { - .name = "NS16550A", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_NATSEMI, - }, - [PORT_XSCALE] = { - .name = "XScale", - .fifo_size = 32, - .tx_loadsz = 32, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_UUE, - }, - [PORT_RM9000] = { - .name = "RM9000", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, - [PORT_OCTEON] = { - .name = "OCTEON", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, - }, - [PORT_AR7] = { - .name = "AR7", - .fifo_size = 16, - .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, - .flags = UART_CAP_FIFO | UART_CAP_AFE, - }, - [PORT_U6_16550A] = { - .name = "U6_16550A", - .fifo_size = 64, - .tx_loadsz = 64, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO | UART_CAP_AFE, - }, -}; - -#if defined(CONFIG_MIPS_ALCHEMY) - -/* Au1x00 UART hardware has a weird register layout */ -static const u8 au_io_in_map[] = { - [UART_RX] = 0, - [UART_IER] = 2, - [UART_IIR] = 3, - [UART_LCR] = 5, - [UART_MCR] = 6, - [UART_LSR] = 7, - [UART_MSR] = 8, -}; - -static const u8 au_io_out_map[] = { - [UART_TX] = 1, - [UART_IER] = 2, - [UART_FCR] = 4, - [UART_LCR] = 5, - [UART_MCR] = 6, -}; - -/* sane hardware needs no mapping */ -static inline int map_8250_in_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_AU) - return offset; - return au_io_in_map[offset]; -} - -static inline int map_8250_out_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_AU) - return offset; - return au_io_out_map[offset]; -} - -#elif defined(CONFIG_SERIAL_8250_RM9K) - -static const u8 - regmap_in[8] = { - [UART_RX] = 0x00, - [UART_IER] = 0x0c, - [UART_IIR] = 0x14, - [UART_LCR] = 0x1c, - [UART_MCR] = 0x20, - [UART_LSR] = 0x24, - [UART_MSR] = 0x28, - [UART_SCR] = 0x2c - }, - regmap_out[8] = { - [UART_TX] = 0x04, - [UART_IER] = 0x0c, - [UART_FCR] = 0x18, - [UART_LCR] = 0x1c, - [UART_MCR] = 0x20, - [UART_LSR] = 0x24, - [UART_MSR] = 0x28, - [UART_SCR] = 0x2c - }; - -static inline int map_8250_in_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_RM9000) - return offset; - return regmap_in[offset]; -} - -static inline int map_8250_out_reg(struct uart_port *p, int offset) -{ - if (p->iotype != UPIO_RM9000) - return offset; - return regmap_out[offset]; -} - -#else - -/* sane hardware needs no mapping */ -#define map_8250_in_reg(up, offset) (offset) -#define map_8250_out_reg(up, offset) (offset) - -#endif - -static unsigned int hub6_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - outb(p->hub6 - 1 + offset, p->iobase); - return inb(p->iobase + 1); -} - -static void hub6_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - outb(p->hub6 - 1 + offset, p->iobase); - outb(value, p->iobase + 1); -} - -static unsigned int mem_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return readb(p->membase + offset); -} - -static void mem_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - writeb(value, p->membase + offset); -} - -static void mem32_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - writel(value, p->membase + offset); -} - -static unsigned int mem32_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return readl(p->membase + offset); -} - -static unsigned int au_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return __raw_readl(p->membase + offset); -} - -static void au_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - __raw_writel(value, p->membase + offset); -} - -static unsigned int tsi_serial_in(struct uart_port *p, int offset) -{ - unsigned int tmp; - offset = map_8250_in_reg(p, offset) << p->regshift; - if (offset == UART_IIR) { - tmp = readl(p->membase + (UART_IIR & ~3)); - return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ - } else - return readb(p->membase + offset); -} - -static void tsi_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - if (!((offset == UART_IER) && (value & UART_IER_UUE))) - writeb(value, p->membase + offset); -} - -/* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ -static inline void dwapb_save_out_value(struct uart_port *p, int offset, - int value) -{ - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); - - if (offset == UART_LCR) - up->lcr = value; -} - -/* Read the IER to ensure any interrupt is cleared before returning from ISR. */ -static inline void dwapb_check_clear_ier(struct uart_port *p, int offset) -{ - if (offset == UART_TX || offset == UART_IER) - p->serial_in(p, UART_IER); -} - -static void dwapb_serial_out(struct uart_port *p, int offset, int value) -{ - int save_offset = offset; - offset = map_8250_out_reg(p, offset) << p->regshift; - dwapb_save_out_value(p, save_offset, value); - writeb(value, p->membase + offset); - dwapb_check_clear_ier(p, save_offset); -} - -static void dwapb32_serial_out(struct uart_port *p, int offset, int value) -{ - int save_offset = offset; - offset = map_8250_out_reg(p, offset) << p->regshift; - dwapb_save_out_value(p, save_offset, value); - writel(value, p->membase + offset); - dwapb_check_clear_ier(p, save_offset); -} - -static unsigned int io_serial_in(struct uart_port *p, int offset) -{ - offset = map_8250_in_reg(p, offset) << p->regshift; - return inb(p->iobase + offset); -} - -static void io_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - outb(value, p->iobase + offset); -} - -static void set_io_from_upio(struct uart_port *p) -{ - struct uart_8250_port *up = - container_of(p, struct uart_8250_port, port); - switch (p->iotype) { - case UPIO_HUB6: - p->serial_in = hub6_serial_in; - p->serial_out = hub6_serial_out; - break; - - case UPIO_MEM: - p->serial_in = mem_serial_in; - p->serial_out = mem_serial_out; - break; - - case UPIO_RM9000: - case UPIO_MEM32: - p->serial_in = mem32_serial_in; - p->serial_out = mem32_serial_out; - break; - - case UPIO_AU: - p->serial_in = au_serial_in; - p->serial_out = au_serial_out; - break; - - case UPIO_TSI: - p->serial_in = tsi_serial_in; - p->serial_out = tsi_serial_out; - break; - - case UPIO_DWAPB: - p->serial_in = mem_serial_in; - p->serial_out = dwapb_serial_out; - break; - - case UPIO_DWAPB32: - p->serial_in = mem32_serial_in; - p->serial_out = dwapb32_serial_out; - break; - - default: - p->serial_in = io_serial_in; - p->serial_out = io_serial_out; - break; - } - /* Remember loaded iotype */ - up->cur_iotype = p->iotype; -} - -static void -serial_out_sync(struct uart_8250_port *up, int offset, int value) -{ - struct uart_port *p = &up->port; - switch (p->iotype) { - case UPIO_MEM: - case UPIO_MEM32: - case UPIO_AU: - case UPIO_DWAPB: - case UPIO_DWAPB32: - p->serial_out(p, offset, value); - p->serial_in(p, UART_LCR); /* safe, no side-effects */ - break; - default: - p->serial_out(p, offset, value); - } -} - -#define serial_in(up, offset) \ - (up->port.serial_in(&(up)->port, (offset))) -#define serial_out(up, offset, value) \ - (up->port.serial_out(&(up)->port, (offset), (value))) -/* - * We used to support using pause I/O for certain machines. We - * haven't supported this for a while, but just in case it's badly - * needed for certain old 386 machines, I've left these #define's - * in.... - */ -#define serial_inp(up, offset) serial_in(up, offset) -#define serial_outp(up, offset, value) serial_out(up, offset, value) - -/* Uart divisor latch read */ -static inline int _serial_dl_read(struct uart_8250_port *up) -{ - return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8; -} - -/* Uart divisor latch write */ -static inline void _serial_dl_write(struct uart_8250_port *up, int value) -{ - serial_outp(up, UART_DLL, value & 0xff); - serial_outp(up, UART_DLM, value >> 8 & 0xff); -} - -#if defined(CONFIG_MIPS_ALCHEMY) -/* Au1x00 haven't got a standard divisor latch */ -static int serial_dl_read(struct uart_8250_port *up) -{ - if (up->port.iotype == UPIO_AU) - return __raw_readl(up->port.membase + 0x28); - else - return _serial_dl_read(up); -} - -static void serial_dl_write(struct uart_8250_port *up, int value) -{ - if (up->port.iotype == UPIO_AU) - __raw_writel(value, up->port.membase + 0x28); - else - _serial_dl_write(up, value); -} -#elif defined(CONFIG_SERIAL_8250_RM9K) -static int serial_dl_read(struct uart_8250_port *up) -{ - return (up->port.iotype == UPIO_RM9000) ? - (((__raw_readl(up->port.membase + 0x10) << 8) | - (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) : - _serial_dl_read(up); -} - -static void serial_dl_write(struct uart_8250_port *up, int value) -{ - if (up->port.iotype == UPIO_RM9000) { - __raw_writel(value, up->port.membase + 0x08); - __raw_writel(value >> 8, up->port.membase + 0x10); - } else { - _serial_dl_write(up, value); - } -} -#else -#define serial_dl_read(up) _serial_dl_read(up) -#define serial_dl_write(up, value) _serial_dl_write(up, value) -#endif - -/* - * For the 16C950 - */ -static void serial_icr_write(struct uart_8250_port *up, int offset, int value) -{ - serial_out(up, UART_SCR, offset); - serial_out(up, UART_ICR, value); -} - -static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) -{ - unsigned int value; - - serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); - serial_out(up, UART_SCR, offset); - value = serial_in(up, UART_ICR); - serial_icr_write(up, UART_ACR, up->acr); - - return value; -} - -/* - * FIFO support. - */ -static void serial8250_clear_fifos(struct uart_8250_port *p) -{ - if (p->capabilities & UART_CAP_FIFO) { - serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(p, UART_FCR, 0); - } -} - -/* - * IER sleep support. UARTs which have EFRs need the "extended - * capability" bit enabled. Note that on XR16C850s, we need to - * reset LCR to write to IER. - */ -static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) -{ - if (p->capabilities & UART_CAP_SLEEP) { - if (p->capabilities & UART_CAP_EFR) { - serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(p, UART_EFR, UART_EFR_ECB); - serial_outp(p, UART_LCR, 0); - } - serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); - if (p->capabilities & UART_CAP_EFR) { - serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(p, UART_EFR, 0); - serial_outp(p, UART_LCR, 0); - } - } -} - -#ifdef CONFIG_SERIAL_8250_RSA -/* - * Attempts to turn on the RSA FIFO. Returns zero on failure. - * We set the port uart clock rate if we succeed. - */ -static int __enable_rsa(struct uart_8250_port *up) -{ - unsigned char mode; - int result; - - mode = serial_inp(up, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - - if (!result) { - serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - } - - if (result) - up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; - - return result; -} - -static void enable_rsa(struct uart_8250_port *up) -{ - if (up->port.type == PORT_RSA) { - if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); - __enable_rsa(up); - spin_unlock_irq(&up->port.lock); - } - if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) - serial_outp(up, UART_RSA_FRR, 0); - } -} - -/* - * Attempts to turn off the RSA FIFO. Returns zero on failure. - * It is unknown why interrupts were disabled in here. However, - * the caller is expected to preserve this behaviour by grabbing - * the spinlock before calling this function. - */ -static void disable_rsa(struct uart_8250_port *up) -{ - unsigned char mode; - int result; - - if (up->port.type == PORT_RSA && - up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); - - mode = serial_inp(up, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - - if (!result) { - serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - } - - if (result) - up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; - spin_unlock_irq(&up->port.lock); - } -} -#endif /* CONFIG_SERIAL_8250_RSA */ - -/* - * This is a quickie test to see how big the FIFO is. - * It doesn't work at all the time, more's the pity. - */ -static int size_fifo(struct uart_8250_port *up) -{ - unsigned char old_fcr, old_mcr, old_lcr; - unsigned short old_dl; - int count; - - old_lcr = serial_inp(up, UART_LCR); - serial_outp(up, UART_LCR, 0); - old_fcr = serial_inp(up, UART_FCR); - old_mcr = serial_inp(up, UART_MCR); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_MCR, UART_MCR_LOOP); - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - old_dl = serial_dl_read(up); - serial_dl_write(up, 0x0001); - serial_outp(up, UART_LCR, 0x03); - for (count = 0; count < 256; count++) - serial_outp(up, UART_TX, count); - mdelay(20);/* FIXME - schedule_timeout */ - for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && - (count < 256); count++) - serial_inp(up, UART_RX); - serial_outp(up, UART_FCR, old_fcr); - serial_outp(up, UART_MCR, old_mcr); - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_dl_write(up, old_dl); - serial_outp(up, UART_LCR, old_lcr); - - return count; -} - -/* - * Read UART ID using the divisor method - set DLL and DLM to zero - * and the revision will be in DLL and device type in DLM. We - * preserve the device state across this. - */ -static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) -{ - unsigned char old_dll, old_dlm, old_lcr; - unsigned int id; - - old_lcr = serial_inp(p, UART_LCR); - serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_A); - - old_dll = serial_inp(p, UART_DLL); - old_dlm = serial_inp(p, UART_DLM); - - serial_outp(p, UART_DLL, 0); - serial_outp(p, UART_DLM, 0); - - id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8; - - serial_outp(p, UART_DLL, old_dll); - serial_outp(p, UART_DLM, old_dlm); - serial_outp(p, UART_LCR, old_lcr); - - return id; -} - -/* - * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. - * When this function is called we know it is at least a StarTech - * 16650 V2, but it might be one of several StarTech UARTs, or one of - * its clones. (We treat the broken original StarTech 16650 V1 as a - * 16550, and why not? Startech doesn't seem to even acknowledge its - * existence.) - * - * What evil have men's minds wrought... - */ -static void autoconfig_has_efr(struct uart_8250_port *up) -{ - unsigned int id1, id2, id3, rev; - - /* - * Everything with an EFR has SLEEP - */ - up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; - - /* - * First we check to see if it's an Oxford Semiconductor UART. - * - * If we have to do this here because some non-National - * Semiconductor clone chips lock up if you try writing to the - * LSR register (which serial_icr_read does) - */ - - /* - * Check for Oxford Semiconductor 16C950. - * - * EFR [4] must be set else this test fails. - * - * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) - * claims that it's needed for 952 dual UART's (which are not - * recommended for new designs). - */ - up->acr = 0; - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, UART_EFR_ECB); - serial_out(up, UART_LCR, 0x00); - id1 = serial_icr_read(up, UART_ID1); - id2 = serial_icr_read(up, UART_ID2); - id3 = serial_icr_read(up, UART_ID3); - rev = serial_icr_read(up, UART_REV); - - DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); - - if (id1 == 0x16 && id2 == 0xC9 && - (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { - up->port.type = PORT_16C950; - - /* - * Enable work around for the Oxford Semiconductor 952 rev B - * chip which causes it to seriously miscalculate baud rates - * when DLL is 0. - */ - if (id3 == 0x52 && rev == 0x01) - up->bugs |= UART_BUG_QUOT; - return; - } - - /* - * We check for a XR16C850 by setting DLL and DLM to 0, and then - * reading back DLL and DLM. The chip type depends on the DLM - * value read back: - * 0x10 - XR16C850 and the DLL contains the chip revision. - * 0x12 - XR16C2850. - * 0x14 - XR16C854. - */ - id1 = autoconfig_read_divisor_id(up); - DEBUG_AUTOCONF("850id=%04x ", id1); - - id2 = id1 >> 8; - if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) { - up->port.type = PORT_16850; - return; - } - - /* - * It wasn't an XR16C850. - * - * We distinguish between the '654 and the '650 by counting - * how many bytes are in the FIFO. I'm using this for now, - * since that's the technique that was sent to me in the - * serial driver update, but I'm not convinced this works. - * I've had problems doing this in the past. -TYT - */ - if (size_fifo(up) == 64) - up->port.type = PORT_16654; - else - up->port.type = PORT_16650V2; -} - -/* - * We detected a chip without a FIFO. Only two fall into - * this category - the original 8250 and the 16450. The - * 16450 has a scratch register (accessible with LCR=0) - */ -static void autoconfig_8250(struct uart_8250_port *up) -{ - unsigned char scratch, status1, status2; - - up->port.type = PORT_8250; - - scratch = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0xa5); - status1 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0x5a); - status2 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, scratch); - - if (status1 == 0xa5 && status2 == 0x5a) - up->port.type = PORT_16450; -} - -static int broken_efr(struct uart_8250_port *up) -{ - /* - * Exar ST16C2550 "A2" devices incorrectly detect as - * having an EFR, and report an ID of 0x0201. See - * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html - */ - if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16) - return 1; - - return 0; -} - -/* - * We know that the chip has FIFOs. Does it have an EFR? The - * EFR is located in the same register position as the IIR and - * we know the top two bits of the IIR are currently set. The - * EFR should contain zero. Try to read the EFR. - */ -static void autoconfig_16550a(struct uart_8250_port *up) -{ - unsigned char status1, status2; - unsigned int iersave; - - up->port.type = PORT_16550A; - up->capabilities |= UART_CAP_FIFO; - - /* - * Check for presence of the EFR when DLAB is set. - * Only ST16C650V1 UARTs pass this test. - */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - if (serial_in(up, UART_EFR) == 0) { - serial_outp(up, UART_EFR, 0xA8); - if (serial_in(up, UART_EFR) != 0) { - DEBUG_AUTOCONF("EFRv1 "); - up->port.type = PORT_16650; - up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; - } else { - DEBUG_AUTOCONF("Motorola 8xxx DUART "); - } - serial_outp(up, UART_EFR, 0); - return; - } - - /* - * Maybe it requires 0xbf to be written to the LCR. - * (other ST16C650V2 UARTs, TI16C752A, etc) - */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) { - DEBUG_AUTOCONF("EFRv2 "); - autoconfig_has_efr(up); - return; - } - - /* - * Check for a National Semiconductor SuperIO chip. - * Attempt to switch to bank 2, read the value of the LOOP bit - * from EXCR1. Switch back to bank 0, change it in MCR. Then - * switch back to bank 2, read it from EXCR1 again and check - * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2 - */ - serial_outp(up, UART_LCR, 0); - status1 = serial_in(up, UART_MCR); - serial_outp(up, UART_LCR, 0xE0); - status2 = serial_in(up, 0x02); /* EXCR1 */ - - if (!((status2 ^ status1) & UART_MCR_LOOP)) { - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP); - serial_outp(up, UART_LCR, 0xE0); - status2 = serial_in(up, 0x02); /* EXCR1 */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_MCR, status1); - - if ((status2 ^ status1) & UART_MCR_LOOP) { - unsigned short quot; - - serial_outp(up, UART_LCR, 0xE0); - - quot = serial_dl_read(up); - quot <<= 3; - - status1 = serial_in(up, 0x04); /* EXCR2 */ - status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, status1); - - serial_dl_write(up, quot); - - serial_outp(up, UART_LCR, 0); - - up->port.uartclk = 921600*16; - up->port.type = PORT_NS16550A; - up->capabilities |= UART_NATSEMI; - return; - } - } - - /* - * No EFR. Try to detect a TI16750, which only sets bit 5 of - * the IIR when 64 byte FIFO mode is enabled when DLAB is set. - * Try setting it with and without DLAB set. Cheap clones - * set bit 5 without DLAB set. - */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status1 = serial_in(up, UART_IIR) >> 5; - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - status2 = serial_in(up, UART_IIR) >> 5; - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, 0); - - DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); - - if (status1 == 6 && status2 == 7) { - up->port.type = PORT_16750; - up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; - return; - } - - /* - * Try writing and reading the UART_IER_UUE bit (b6). - * If it works, this is probably one of the Xscale platform's - * internal UARTs. - * We're going to explicitly set the UUE bit to 0 before - * trying to write and read a 1 just to make sure it's not - * already a 1 and maybe locked there before we even start start. - */ - iersave = serial_in(up, UART_IER); - serial_outp(up, UART_IER, iersave & ~UART_IER_UUE); - if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { - /* - * OK it's in a known zero state, try writing and reading - * without disturbing the current state of the other bits. - */ - serial_outp(up, UART_IER, iersave | UART_IER_UUE); - if (serial_in(up, UART_IER) & UART_IER_UUE) { - /* - * It's an Xscale. - * We'll leave the UART_IER_UUE bit set to 1 (enabled). - */ - DEBUG_AUTOCONF("Xscale "); - up->port.type = PORT_XSCALE; - up->capabilities |= UART_CAP_UUE; - return; - } - } else { - /* - * If we got here we couldn't force the IER_UUE bit to 0. - * Log it and continue. - */ - DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 "); - } - serial_outp(up, UART_IER, iersave); - - /* - * We distinguish between 16550A and U6 16550A by counting - * how many bytes are in the FIFO. - */ - if (up->port.type == PORT_16550A && size_fifo(up) == 64) { - up->port.type = PORT_U6_16550A; - up->capabilities |= UART_CAP_AFE; - } -} - -/* - * This routine is called by rs_init() to initialize a specific serial - * port. It determines what type of UART chip this serial port is - * using: 8250, 16450, 16550, 16550A. The important question is - * whether or not this UART is a 16550A or not, since this will - * determine whether or not we can use its FIFO features or not. - */ -static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) -{ - unsigned char status1, scratch, scratch2, scratch3; - unsigned char save_lcr, save_mcr; - unsigned long flags; - - if (!up->port.iobase && !up->port.mapbase && !up->port.membase) - return; - - DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", - serial_index(&up->port), up->port.iobase, up->port.membase); - - /* - * We really do need global IRQs disabled here - we're going to - * be frobbing the chips IRQ enable register to see if it exists. - */ - spin_lock_irqsave(&up->port.lock, flags); - - up->capabilities = 0; - up->bugs = 0; - - if (!(up->port.flags & UPF_BUGGY_UART)) { - /* - * Do a simple existence test first; if we fail this, - * there's no point trying anything else. - * - * 0x80 is used as a nonsense port to prevent against - * false positives due to ISA bus float. The - * assumption is that 0x80 is a non-existent port; - * which should be safe since include/asm/io.h also - * makes this assumption. - * - * Note: this is safe as long as MCR bit 4 is clear - * and the device is in "PC" mode. - */ - scratch = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, 0); -#ifdef __i386__ - outb(0xff, 0x080); -#endif - /* - * Mask out IER[7:4] bits for test as some UARTs (e.g. TL - * 16C754B) allow only to modify them if an EFR bit is set. - */ - scratch2 = serial_inp(up, UART_IER) & 0x0f; - serial_outp(up, UART_IER, 0x0F); -#ifdef __i386__ - outb(0, 0x080); -#endif - scratch3 = serial_inp(up, UART_IER) & 0x0f; - serial_outp(up, UART_IER, scratch); - if (scratch2 != 0 || scratch3 != 0x0F) { - /* - * We failed; there's nothing here - */ - DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", - scratch2, scratch3); - goto out; - } - } - - save_mcr = serial_in(up, UART_MCR); - save_lcr = serial_in(up, UART_LCR); - - /* - * Check to see if a UART is really there. Certain broken - * internal modems based on the Rockwell chipset fail this - * test, because they apparently don't implement the loopback - * test mode. So this test is skipped on the COM 1 through - * COM 4 ports. This *should* be safe, since no board - * manufacturer would be stupid enough to design a board - * that conflicts with COM 1-4 --- we hope! - */ - if (!(up->port.flags & UPF_SKIP_TEST)) { - serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = serial_inp(up, UART_MSR) & 0xF0; - serial_outp(up, UART_MCR, save_mcr); - if (status1 != 0x90) { - DEBUG_AUTOCONF("LOOP test failed (%02x) ", - status1); - goto out; - } - } - - /* - * We're pretty sure there's a port here. Lets find out what - * type of port it is. The IIR top two bits allows us to find - * out if it's 8250 or 16450, 16550, 16550A or later. This - * determines what we test for next. - * - * We also initialise the EFR (if any) to zero for later. The - * EFR occupies the same register location as the FCR and IIR. - */ - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, 0); - serial_outp(up, UART_LCR, 0); - - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - scratch = serial_in(up, UART_IIR) >> 6; - - DEBUG_AUTOCONF("iir=%d ", scratch); - - switch (scratch) { - case 0: - autoconfig_8250(up); - break; - case 1: - up->port.type = PORT_UNKNOWN; - break; - case 2: - up->port.type = PORT_16550; - break; - case 3: - autoconfig_16550a(up); - break; - } - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * Only probe for RSA ports if we got the region. - */ - if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { - int i; - - for (i = 0 ; i < probe_rsa_count; ++i) { - if (probe_rsa[i] == up->port.iobase && - __enable_rsa(up)) { - up->port.type = PORT_RSA; - break; - } - } - } -#endif - - serial_outp(up, UART_LCR, save_lcr); - - if (up->capabilities != uart_config[up->port.type].flags) { - printk(KERN_WARNING - "ttyS%d: detected caps %08x should be %08x\n", - serial_index(&up->port), up->capabilities, - uart_config[up->port.type].flags); - } - - up->port.fifosize = uart_config[up->port.type].fifo_size; - up->capabilities = uart_config[up->port.type].flags; - up->tx_loadsz = uart_config[up->port.type].tx_loadsz; - - if (up->port.type == PORT_UNKNOWN) - goto out; - - /* - * Reset the UART. - */ -#ifdef CONFIG_SERIAL_8250_RSA - if (up->port.type == PORT_RSA) - serial_outp(up, UART_RSA_FRR, 0); -#endif - serial_outp(up, UART_MCR, save_mcr); - serial8250_clear_fifos(up); - serial_in(up, UART_RX); - if (up->capabilities & UART_CAP_UUE) - serial_outp(up, UART_IER, UART_IER_UUE); - else - serial_outp(up, UART_IER, 0); - - out: - spin_unlock_irqrestore(&up->port.lock, flags); - DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); -} - -static void autoconfig_irq(struct uart_8250_port *up) -{ - unsigned char save_mcr, save_ier; - unsigned char save_ICP = 0; - unsigned int ICP = 0; - unsigned long irqs; - int irq; - - if (up->port.flags & UPF_FOURPORT) { - ICP = (up->port.iobase & 0xfe0) | 0x1f; - save_ICP = inb_p(ICP); - outb_p(0x80, ICP); - (void) inb_p(ICP); - } - - /* forget possible initially masked and pending IRQ */ - probe_irq_off(probe_irq_on()); - save_mcr = serial_inp(up, UART_MCR); - save_ier = serial_inp(up, UART_IER); - serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); - - irqs = probe_irq_on(); - serial_outp(up, UART_MCR, 0); - udelay(10); - if (up->port.flags & UPF_FOURPORT) { - serial_outp(up, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS); - } else { - serial_outp(up, UART_MCR, - UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); - } - serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ - (void)serial_inp(up, UART_LSR); - (void)serial_inp(up, UART_RX); - (void)serial_inp(up, UART_IIR); - (void)serial_inp(up, UART_MSR); - serial_outp(up, UART_TX, 0xFF); - udelay(20); - irq = probe_irq_off(irqs); - - serial_outp(up, UART_MCR, save_mcr); - serial_outp(up, UART_IER, save_ier); - - if (up->port.flags & UPF_FOURPORT) - outb_p(save_ICP, ICP); - - up->port.irq = (irq > 0) ? irq : 0; -} - -static inline void __stop_tx(struct uart_8250_port *p) -{ - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); - } -} - -static void serial8250_stop_tx(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - __stop_tx(up); - - /* - * We really want to stop the transmitter from sending. - */ - if (up->port.type == PORT_16C950) { - up->acr |= UART_ACR_TXDIS; - serial_icr_write(up, UART_ACR, up->acr); - } -} - -static void transmit_chars(struct uart_8250_port *up); - -static void serial8250_start_tx(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - - if (up->bugs & UART_BUG_TXEN) { - unsigned char lsr; - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - if ((up->port.type == PORT_RM9000) ? - (lsr & UART_LSR_THRE) : - (lsr & UART_LSR_TEMT)) - transmit_chars(up); - } - } - - /* - * Re-enable the transmitter if we disabled it. - */ - if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { - up->acr &= ~UART_ACR_TXDIS; - serial_icr_write(up, UART_ACR, up->acr); - } -} - -static void serial8250_stop_rx(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void serial8250_enable_ms(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - /* no MSR capabilities */ - if (up->bugs & UART_BUG_NOMSR) - return; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void -receive_chars(struct uart_8250_port *up, unsigned int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch, lsr = *status; - int max_count = 256; - char flag; - - do { - if (likely(lsr & UART_LSR_DR)) - ch = serial_inp(up, UART_RX); - else - /* - * Intel 82571 has a Serial Over Lan device that will - * set UART_LSR_BI without setting UART_LSR_DR when - * it receives a break. To avoid reading from the - * receive buffer without UART_LSR_DR bit set, we - * just force the read character to be 0 - */ - ch = 0; - - flag = TTY_NORMAL; - up->port.icount.rx++; - - lsr |= up->lsr_saved_flags; - up->lsr_saved_flags = 0; - - if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ - if (lsr & UART_LSR_BI) { - lsr &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (lsr & UART_LSR_PE) - up->port.icount.parity++; - else if (lsr & UART_LSR_FE) - up->port.icount.frame++; - if (lsr & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ignored. - */ - lsr &= up->port.read_status_mask; - - if (lsr & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (lsr & UART_LSR_PE) - flag = TTY_PARITY; - else if (lsr & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - - uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); - -ignore_char: - lsr = serial_inp(up, UART_LSR); - } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); - spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&up->port.lock); - *status = lsr; -} - -static void transmit_chars(struct uart_8250_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_outp(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_tx_stopped(&up->port)) { - serial8250_stop_tx(&up->port); - return; - } - if (uart_circ_empty(xmit)) { - __stop_tx(up); - return; - } - - count = up->tx_loadsz; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - DEBUG_INTR("THRE..."); - - if (uart_circ_empty(xmit)) - __stop_tx(up); -} - -static unsigned int check_modem_status(struct uart_8250_port *up) -{ - unsigned int status = serial_in(up, UART_MSR); - - status |= up->msr_saved_flags; - up->msr_saved_flags = 0; - if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && - up->port.state != NULL) { - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); - } - - return status; -} - -/* - * This handles the interrupt from one port. - */ -static void serial8250_handle_port(struct uart_8250_port *up) -{ - unsigned int status; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - status = serial_inp(up, UART_LSR); - - DEBUG_INTR("status = %x...", status); - - if (status & (UART_LSR_DR | UART_LSR_BI)) - receive_chars(up, &status); - check_modem_status(up); - if (status & UART_LSR_THRE) - transmit_chars(up); - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* - * This is the serial driver's interrupt routine. - * - * Arjan thinks the old way was overly complex, so it got simplified. - * Alan disagrees, saying that need the complexity to handle the weird - * nature of ISA shared interrupts. (This is a special exception.) - * - * In order to handle ISA shared interrupts properly, we need to check - * that all ports have been serviced, and therefore the ISA interrupt - * line has been de-asserted. - * - * This means we need to loop through all ports. checking that they - * don't have an interrupt pending. - */ -static irqreturn_t serial8250_interrupt(int irq, void *dev_id) -{ - struct irq_info *i = dev_id; - struct list_head *l, *end = NULL; - int pass_counter = 0, handled = 0; - - DEBUG_INTR("serial8250_interrupt(%d)...", irq); - - spin_lock(&i->lock); - - l = i->head; - do { - struct uart_8250_port *up; - unsigned int iir; - - up = list_entry(l, struct uart_8250_port, list); - - iir = serial_in(up, UART_IIR); - if (!(iir & UART_IIR_NO_INT)) { - serial8250_handle_port(up); - - handled = 1; - - end = NULL; - } else if ((up->port.iotype == UPIO_DWAPB || - up->port.iotype == UPIO_DWAPB32) && - (iir & UART_IIR_BUSY) == UART_IIR_BUSY) { - /* The DesignWare APB UART has an Busy Detect (0x07) - * interrupt meaning an LCR write attempt occured while the - * UART was busy. The interrupt must be cleared by reading - * the UART status register (USR) and the LCR re-written. */ - unsigned int status; - status = *(volatile u32 *)up->port.private_data; - serial_out(up, UART_LCR, up->lcr); - - handled = 1; - - end = NULL; - } else if (end == NULL) - end = l; - - l = l->next; - - if (l == i->head && pass_counter++ > PASS_LIMIT) { - /* If we hit this, we're dead. */ - printk_ratelimited(KERN_ERR - "serial8250: too much work for irq%d\n", irq); - break; - } - } while (l != end); - - spin_unlock(&i->lock); - - DEBUG_INTR("end.\n"); - - return IRQ_RETVAL(handled); -} - -/* - * To support ISA shared interrupts, we need to have one interrupt - * handler that ensures that the IRQ line has been deasserted - * before returning. Failing to do this will result in the IRQ - * line being stuck active, and, since ISA irqs are edge triggered, - * no more IRQs will be seen. - */ -static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) -{ - spin_lock_irq(&i->lock); - - if (!list_empty(i->head)) { - if (i->head == &up->list) - i->head = i->head->next; - list_del(&up->list); - } else { - BUG_ON(i->head != &up->list); - i->head = NULL; - } - spin_unlock_irq(&i->lock); - /* List empty so throw away the hash node */ - if (i->head == NULL) { - hlist_del(&i->node); - kfree(i); - } -} - -static int serial_link_irq_chain(struct uart_8250_port *up) -{ - struct hlist_head *h; - struct hlist_node *n; - struct irq_info *i; - int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; - - mutex_lock(&hash_mutex); - - h = &irq_lists[up->port.irq % NR_IRQ_HASH]; - - hlist_for_each(n, h) { - i = hlist_entry(n, struct irq_info, node); - if (i->irq == up->port.irq) - break; - } - - if (n == NULL) { - i = kzalloc(sizeof(struct irq_info), GFP_KERNEL); - if (i == NULL) { - mutex_unlock(&hash_mutex); - return -ENOMEM; - } - spin_lock_init(&i->lock); - i->irq = up->port.irq; - hlist_add_head(&i->node, h); - } - mutex_unlock(&hash_mutex); - - spin_lock_irq(&i->lock); - - if (i->head) { - list_add(&up->list, i->head); - spin_unlock_irq(&i->lock); - - ret = 0; - } else { - INIT_LIST_HEAD(&up->list); - i->head = &up->list; - spin_unlock_irq(&i->lock); - irq_flags |= up->port.irqflags; - ret = request_irq(up->port.irq, serial8250_interrupt, - irq_flags, "serial", i); - if (ret < 0) - serial_do_unlink(i, up); - } - - return ret; -} - -static void serial_unlink_irq_chain(struct uart_8250_port *up) -{ - struct irq_info *i; - struct hlist_node *n; - struct hlist_head *h; - - mutex_lock(&hash_mutex); - - h = &irq_lists[up->port.irq % NR_IRQ_HASH]; - - hlist_for_each(n, h) { - i = hlist_entry(n, struct irq_info, node); - if (i->irq == up->port.irq) - break; - } - - BUG_ON(n == NULL); - BUG_ON(i->head == NULL); - - if (list_empty(i->head)) - free_irq(up->port.irq, i); - - serial_do_unlink(i, up); - mutex_unlock(&hash_mutex); -} - -/* - * This function is used to handle ports that do not have an - * interrupt. This doesn't work very well for 16450's, but gives - * barely passable results for a 16550A. (Although at the expense - * of much CPU overhead). - */ -static void serial8250_timeout(unsigned long data) -{ - struct uart_8250_port *up = (struct uart_8250_port *)data; - unsigned int iir; - - iir = serial_in(up, UART_IIR); - if (!(iir & UART_IIR_NO_INT)) - serial8250_handle_port(up); - mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); -} - -static void serial8250_backup_timeout(unsigned long data) -{ - struct uart_8250_port *up = (struct uart_8250_port *)data; - unsigned int iir, ier = 0, lsr; - unsigned long flags; - - /* - * Must disable interrupts or else we risk racing with the interrupt - * based handler. - */ - if (is_real_interrupt(up->port.irq)) { - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - } - - iir = serial_in(up, UART_IIR); - - /* - * This should be a safe test for anyone who doesn't trust the - * IIR bits on their UART, but it's specifically designed for - * the "Diva" UART used on the management processor on many HP - * ia64 and parisc boxes. - */ - spin_lock_irqsave(&up->port.lock, flags); - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - spin_unlock_irqrestore(&up->port.lock, flags); - if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && - (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && - (lsr & UART_LSR_THRE)) { - iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); - iir |= UART_IIR_THRI; - } - - if (!(iir & UART_IIR_NO_INT)) - serial8250_handle_port(up); - - if (is_real_interrupt(up->port.irq)) - serial_out(up, UART_IER, ier); - - /* Standard timer interval plus 0.2s to keep the port running */ - mod_timer(&up->timer, - jiffies + uart_poll_timeout(&up->port) + HZ / 5); -} - -static unsigned int serial8250_tx_empty(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - unsigned int lsr; - - spin_lock_irqsave(&up->port.lock, flags); - lsr = serial_in(up, UART_LSR); - up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - spin_unlock_irqrestore(&up->port.lock, flags); - - return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; -} - -static unsigned int serial8250_get_mctrl(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned int status; - unsigned int ret; - - status = check_modem_status(up); - - 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 serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned char mcr = 0; - - 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; - - mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; - - serial_out(up, UART_MCR, mcr); -} - -static void serial8250_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* - * Wait for transmitter & holding register to empty - */ -static void wait_for_xmitr(struct uart_8250_port *up, int bits) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - for (;;) { - status = serial_in(up, UART_LSR); - - up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; - - if ((status & bits) == bits) - break; - if (--tmout == 0) - break; - udelay(1); - } - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - unsigned int tmout; - for (tmout = 1000000; tmout; tmout--) { - unsigned int msr = serial_in(up, UART_MSR); - up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; - if (msr & UART_MSR_CTS) - break; - udelay(1); - touch_nmi_watchdog(); - } - } -} - -#ifdef CONFIG_CONSOLE_POLL -/* - * Console polling routines for writing and reading from the uart while - * in an interrupt or debug context. - */ - -static int serial8250_get_poll_char(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned char lsr = serial_inp(up, UART_LSR); - - if (!(lsr & UART_LSR_DR)) - return NO_POLL_CHAR; - - return serial_inp(up, UART_RX); -} - - -static void serial8250_put_poll_char(struct uart_port *port, - unsigned char c) -{ - unsigned int ier; - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - if (up->capabilities & UART_CAP_UUE) - serial_out(up, UART_IER, UART_IER_UUE); - else - serial_out(up, UART_IER, 0); - - wait_for_xmitr(up, BOTH_EMPTY); - /* - * Send the character out. - * If a LF, also do CR... - */ - serial_out(up, UART_TX, c); - if (c == 10) { - wait_for_xmitr(up, BOTH_EMPTY); - serial_out(up, UART_TX, 13); - } - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up, BOTH_EMPTY); - serial_out(up, UART_IER, ier); -} - -#endif /* CONFIG_CONSOLE_POLL */ - -static int serial8250_startup(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - unsigned char lsr, iir; - int retval; - - up->port.fifosize = uart_config[up->port.type].fifo_size; - up->tx_loadsz = uart_config[up->port.type].tx_loadsz; - up->capabilities = uart_config[up->port.type].flags; - up->mcr = 0; - - if (up->port.iotype != up->cur_iotype) - set_io_from_upio(port); - - if (up->port.type == PORT_16C950) { - /* Wake up and initialize UART */ - up->acr = 0; - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_IER, 0); - serial_outp(up, UART_LCR, 0); - serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_LCR, 0); - } - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * If this is an RSA port, see if we can kick it up to the - * higher speed clock. - */ - enable_rsa(up); -#endif - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - serial8250_clear_fifos(up); - - /* - * Clear the interrupt registers. - */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); - - /* - * At this point, there's no way the LSR could still be 0xff; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (!(up->port.flags & UPF_BUGGY_UART) && - (serial_inp(up, UART_LSR) == 0xff)) { - printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n", - serial_index(&up->port)); - return -ENODEV; - } - - /* - * For a XR16C850, we need to set the trigger levels - */ - if (up->port.type == PORT_16850) { - unsigned char fctr; - - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - - fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); - serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); - serial_outp(up, UART_TRG, UART_TRG_96); - serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); - serial_outp(up, UART_TRG, UART_TRG_96); - - serial_outp(up, UART_LCR, 0); - } - - if (is_real_interrupt(up->port.irq)) { - unsigned char iir1; - /* - * Test for UARTs that do not reassert THRE when the - * transmitter is idle and the interrupt has already - * been cleared. Real 16550s should always reassert - * this interrupt whenever the transmitter is idle and - * the interrupt is enabled. Delays are necessary to - * allow register changes to become visible. - */ - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.irqflags & IRQF_SHARED) - disable_irq_nosync(up->port.irq); - - wait_for_xmitr(up, UART_LSR_THRE); - serial_out_sync(up, UART_IER, UART_IER_THRI); - udelay(1); /* allow THRE to set */ - iir1 = serial_in(up, UART_IIR); - serial_out(up, UART_IER, 0); - serial_out_sync(up, UART_IER, UART_IER_THRI); - udelay(1); /* allow a working UART time to re-assert THRE */ - iir = serial_in(up, UART_IIR); - serial_out(up, UART_IER, 0); - - if (up->port.irqflags & IRQF_SHARED) - enable_irq(up->port.irq); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * If the interrupt is not reasserted, setup a timer to - * kick the UART on a regular basis. - */ - if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { - up->bugs |= UART_BUG_THRE; - pr_debug("ttyS%d - using backup timer\n", - serial_index(port)); - } - } - - /* - * The above check will only give an accurate result the first time - * the port is opened so this value needs to be preserved. - */ - if (up->bugs & UART_BUG_THRE) { - up->timer.function = serial8250_backup_timeout; - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + - uart_poll_timeout(port) + HZ / 5); - } - - /* - * If the "interrupt" for this port doesn't correspond with any - * hardware interrupt, we use a timer-based system. The original - * driver used to do this with IRQ0. - */ - if (!is_real_interrupt(up->port.irq)) { - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); - } else { - retval = serial_link_irq_chain(up); - if (retval) - return retval; - } - - /* - * Now, initialize the UART - */ - serial_outp(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - if (!is_real_interrupt(up->port.irq)) - up->port.mctrl |= TIOCM_OUT1; - } else - /* - * Most PC uarts need OUT2 raised to enable interrupts. - */ - if (is_real_interrupt(up->port.irq)) - up->port.mctrl |= TIOCM_OUT2; - - serial8250_set_mctrl(&up->port, up->port.mctrl); - - /* Serial over Lan (SoL) hack: - Intel 8257x Gigabit ethernet chips have a - 16550 emulation, to be used for Serial Over Lan. - Those chips take a longer time than a normal - serial device to signalize that a transmission - data was queued. Due to that, the above test generally - fails. One solution would be to delay the reading of - iir. However, this is not reliable, since the timeout - is variable. So, let's just don't test if we receive - TX irq. This way, we'll never enable UART_BUG_TXEN. - */ - if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST) - goto dont_test_tx_en; - - /* - * Do a quick test to see if we receive an - * interrupt when we enable the TX irq. - */ - serial_outp(up, UART_IER, UART_IER_THRI); - lsr = serial_in(up, UART_LSR); - iir = serial_in(up, UART_IIR); - serial_outp(up, UART_IER, 0); - - if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { - if (!(up->bugs & UART_BUG_TXEN)) { - up->bugs |= UART_BUG_TXEN; - pr_debug("ttyS%d - enabling bad tx status workarounds\n", - serial_index(port)); - } - } else { - up->bugs &= ~UART_BUG_TXEN; - } - -dont_test_tx_en: - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Clear the interrupt registers again for luck, and clear the - * saved flags to avoid getting false values from polling - * routines or the previous session. - */ - serial_inp(up, UART_LSR); - serial_inp(up, UART_RX); - serial_inp(up, UART_IIR); - serial_inp(up, UART_MSR); - up->lsr_saved_flags = 0; - up->msr_saved_flags = 0; - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_outp(up, UART_IER, up->ier); - - if (up->port.flags & UPF_FOURPORT) { - unsigned int icp; - /* - * Enable interrupts on the AST Fourport board - */ - icp = (up->port.iobase & 0xfe0) | 0x01f; - outb_p(0x80, icp); - (void) inb_p(icp); - } - - return 0; -} - -static void serial8250_shutdown(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned long flags; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - serial_outp(up, UART_IER, 0); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - /* reset interrupts on the AST Fourport board */ - inb((up->port.iobase & 0xfe0) | 0x1f); - up->port.mctrl |= TIOCM_OUT1; - } else - up->port.mctrl &= ~TIOCM_OUT2; - - serial8250_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition and FIFOs - */ - serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); - serial8250_clear_fifos(up); - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * Reset the RSA board back to 115kbps compat mode. - */ - disable_rsa(up); -#endif - - /* - * Read data port to reset things, and then unlink from - * the IRQ chain. - */ - (void) serial_in(up, UART_RX); - - del_timer_sync(&up->timer); - up->timer.function = serial8250_timeout; - if (is_real_interrupt(up->port.irq)) - serial_unlink_irq_chain(up); -} - -static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) -{ - unsigned int quot; - - /* - * Handle magic divisors for baud rates above baud_base on - * SMSC SuperIO chips. - */ - 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 = uart_get_divisor(port, baud); - - return quot; -} - -void -serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - unsigned char cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - 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 - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, - port->uartclk / 16 / 0xffff, - port->uartclk / 16); - quot = serial8250_get_divisor(port, baud); - - /* - * Oxford Semi 952 rev B workaround - */ - if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) - quot++; - - if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { - if (baud < 2400) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; - else - fcr = uart_config[up->port.type].fcr; - } - - /* - * MCR-based auto flow control. When AFE is enabled, RTS will be - * deasserted when the receive FIFO contains more characters than - * the trigger, or the MCR RTS bit is cleared. In the case where - * the remote UART is not using CTS auto flow control, we must - * have sufficient FIFO entries for the latency of the remote - * UART to respond. IOW, at least 32 bytes of FIFO. - */ - if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) { - up->mcr &= ~UART_MCR_AFE; - if (termios->c_cflag & CRTSCTS) - up->mcr |= UART_MCR_AFE; - } - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (!(up->bugs & UART_BUG_NOMSR) && - UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - if (up->capabilities & UART_CAP_UUE) - up->ier |= UART_IER_UUE | UART_IER_RTOIE; - - serial_out(up, UART_IER, up->ier); - - if (up->capabilities & UART_CAP_EFR) { - unsigned char efr = 0; - /* - * TI16C752/Startech hardware flow control. FIXME: - * - TI16C752 requires control thresholds to be set. - * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled. - */ - if (termios->c_cflag & CRTSCTS) - efr |= UART_EFR_CTS; - - serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_outp(up, UART_EFR, efr); - } - -#ifdef CONFIG_ARCH_OMAP - /* Workaround to enable 115200 baud on OMAP1510 internal ports */ - if (cpu_is_omap1510() && is_omap_port(up)) { - if (baud == 115200) { - quot = 1; - serial_out(up, UART_OMAP_OSC_12M_SEL, 1); - } else - serial_out(up, UART_OMAP_OSC_12M_SEL, 0); - } -#endif - - if (up->capabilities & UART_NATSEMI) { - /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ - serial_outp(up, UART_LCR, 0xe0); - } else { - serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ - } - - serial_dl_write(up, quot); - - /* - * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR - * is written without DLAB set, this mode will be disabled. - */ - if (up->port.type == PORT_16750) - serial_outp(up, UART_FCR, fcr); - - serial_outp(up, UART_LCR, cval); /* reset DLAB */ - up->lcr = cval; /* Save LCR */ - if (up->port.type != PORT_16750) { - if (fcr & UART_FCR_ENABLE_FIFO) { - /* emulated UARTs (Lucent Venus 167x) need two steps */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - } - serial_outp(up, UART_FCR, fcr); /* set fcr */ - } - serial8250_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - /* Don't rewrite B0 */ - if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, baud, baud); -} -EXPORT_SYMBOL(serial8250_do_set_termios); - -static void -serial8250_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - if (port->set_termios) - port->set_termios(port, termios, old); - else - serial8250_do_set_termios(port, termios, old); -} - -static void -serial8250_set_ldisc(struct uart_port *port, int new) -{ - if (new == N_PPS) { - port->flags |= UPF_HARDPPS_CD; - serial8250_enable_ms(port); - } else - port->flags &= ~UPF_HARDPPS_CD; -} - - -void serial8250_do_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_8250_port *p = - container_of(port, struct uart_8250_port, port); - - serial8250_set_sleep(p, state != 0); -} -EXPORT_SYMBOL(serial8250_do_pm); - -static void -serial8250_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - if (port->pm) - port->pm(port, state, oldstate); - else - serial8250_do_pm(port, state, oldstate); -} - -static unsigned int serial8250_port_size(struct uart_8250_port *pt) -{ - if (pt->port.iotype == UPIO_AU) - return 0x1000; -#ifdef CONFIG_ARCH_OMAP - if (is_omap_port(pt)) - return 0x16 << pt->port.regshift; -#endif - return 8 << pt->port.regshift; -} - -/* - * Resource handling. - */ -static int serial8250_request_std_resource(struct uart_8250_port *up) -{ - unsigned int size = serial8250_port_size(up); - int ret = 0; - - switch (up->port.iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM: - case UPIO_DWAPB: - case UPIO_DWAPB32: - if (!up->port.mapbase) - break; - - if (!request_mem_region(up->port.mapbase, size, "serial")) { - ret = -EBUSY; - break; - } - - if (up->port.flags & UPF_IOREMAP) { - up->port.membase = ioremap_nocache(up->port.mapbase, - size); - if (!up->port.membase) { - release_mem_region(up->port.mapbase, size); - ret = -ENOMEM; - } - } - break; - - case UPIO_HUB6: - case UPIO_PORT: - if (!request_region(up->port.iobase, size, "serial")) - ret = -EBUSY; - break; - } - return ret; -} - -static void serial8250_release_std_resource(struct uart_8250_port *up) -{ - unsigned int size = serial8250_port_size(up); - - switch (up->port.iotype) { - case UPIO_AU: - case UPIO_TSI: - case UPIO_MEM32: - case UPIO_MEM: - case UPIO_DWAPB: - case UPIO_DWAPB32: - if (!up->port.mapbase) - break; - - if (up->port.flags & UPF_IOREMAP) { - iounmap(up->port.membase); - up->port.membase = NULL; - } - - release_mem_region(up->port.mapbase, size); - break; - - case UPIO_HUB6: - case UPIO_PORT: - release_region(up->port.iobase, size); - break; - } -} - -static int serial8250_request_rsa_resource(struct uart_8250_port *up) -{ - unsigned long start = UART_RSA_BASE << up->port.regshift; - unsigned int size = 8 << up->port.regshift; - int ret = -EINVAL; - - switch (up->port.iotype) { - case UPIO_HUB6: - case UPIO_PORT: - start += up->port.iobase; - if (request_region(start, size, "serial-rsa")) - ret = 0; - else - ret = -EBUSY; - break; - } - - return ret; -} - -static void serial8250_release_rsa_resource(struct uart_8250_port *up) -{ - unsigned long offset = UART_RSA_BASE << up->port.regshift; - unsigned int size = 8 << up->port.regshift; - - switch (up->port.iotype) { - case UPIO_HUB6: - case UPIO_PORT: - release_region(up->port.iobase + offset, size); - break; - } -} - -static void serial8250_release_port(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - serial8250_release_std_resource(up); - if (up->port.type == PORT_RSA) - serial8250_release_rsa_resource(up); -} - -static int serial8250_request_port(struct uart_port *port) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - int ret = 0; - - ret = serial8250_request_std_resource(up); - if (ret == 0 && up->port.type == PORT_RSA) { - ret = serial8250_request_rsa_resource(up); - if (ret < 0) - serial8250_release_std_resource(up); - } - - return ret; -} - -static void serial8250_config_port(struct uart_port *port, int flags) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - int probeflags = PROBE_ANY; - int ret; - - /* - * Find the region that we can probe for. This in turn - * tells us whether we can probe for the type of port. - */ - ret = serial8250_request_std_resource(up); - if (ret < 0) - return; - - ret = serial8250_request_rsa_resource(up); - if (ret < 0) - probeflags &= ~PROBE_RSA; - - if (up->port.iotype != up->cur_iotype) - set_io_from_upio(port); - - if (flags & UART_CONFIG_TYPE) - autoconfig(up, probeflags); - - /* if access method is AU, it is a 16550 with a quirk */ - if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) - up->bugs |= UART_BUG_NOMSR; - - if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) - autoconfig_irq(up); - - if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) - serial8250_release_rsa_resource(up); - if (up->port.type == PORT_UNKNOWN) - serial8250_release_std_resource(up); -} - -static int -serial8250_verify_port(struct 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 >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS || - ser->type == PORT_STARTECH) - return -EINVAL; - return 0; -} - -static const char * -serial8250_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops serial8250_pops = { - .tx_empty = serial8250_tx_empty, - .set_mctrl = serial8250_set_mctrl, - .get_mctrl = serial8250_get_mctrl, - .stop_tx = serial8250_stop_tx, - .start_tx = serial8250_start_tx, - .stop_rx = serial8250_stop_rx, - .enable_ms = serial8250_enable_ms, - .break_ctl = serial8250_break_ctl, - .startup = serial8250_startup, - .shutdown = serial8250_shutdown, - .set_termios = serial8250_set_termios, - .set_ldisc = serial8250_set_ldisc, - .pm = serial8250_pm, - .type = serial8250_type, - .release_port = serial8250_release_port, - .request_port = serial8250_request_port, - .config_port = serial8250_config_port, - .verify_port = serial8250_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = serial8250_get_poll_char, - .poll_put_char = serial8250_put_poll_char, -#endif -}; - -static struct uart_8250_port serial8250_ports[UART_NR]; - -static void (*serial8250_isa_config)(int port, struct uart_port *up, - unsigned short *capabilities); - -void serial8250_set_isa_configurator( - void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) -{ - serial8250_isa_config = v; -} -EXPORT_SYMBOL(serial8250_set_isa_configurator); - -static void __init serial8250_isa_init_ports(void) -{ - struct uart_8250_port *up; - static int first = 1; - int i, irqflag = 0; - - if (!first) - return; - first = 0; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - up->port.line = i; - spin_lock_init(&up->port.lock); - - init_timer(&up->timer); - up->timer.function = serial8250_timeout; - - /* - * ALPHA_KLUDGE_MCR needs to be killed. - */ - up->mcr_mask = ~ALPHA_KLUDGE_MCR; - up->mcr_force = ALPHA_KLUDGE_MCR; - - up->port.ops = &serial8250_pops; - } - - if (share_irqs) - irqflag = IRQF_SHARED; - - for (i = 0, up = serial8250_ports; - i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; - i++, up++) { - up->port.iobase = old_serial_port[i].port; - up->port.irq = irq_canonicalize(old_serial_port[i].irq); - up->port.irqflags = old_serial_port[i].irqflags; - up->port.uartclk = old_serial_port[i].baud_base * 16; - up->port.flags = old_serial_port[i].flags; - up->port.hub6 = old_serial_port[i].hub6; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; - set_io_from_upio(&up->port); - up->port.irqflags |= irqflag; - if (serial8250_isa_config != NULL) - serial8250_isa_config(i, &up->port, &up->capabilities); - - } -} - -static void -serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) -{ - up->port.type = type; - up->port.fifosize = uart_config[type].fifo_size; - up->capabilities = uart_config[type].flags; - up->tx_loadsz = uart_config[type].tx_loadsz; -} - -static void __init -serial8250_register_ports(struct uart_driver *drv, struct device *dev) -{ - int i; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - up->cur_iotype = 0xFF; - } - - serial8250_isa_init_ports(); - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - up->port.dev = dev; - - if (up->port.flags & UPF_FIXED_TYPE) - serial8250_init_fixed_type_port(up, up->port.type); - - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_8250_CONSOLE - -static void serial8250_console_putchar(struct uart_port *port, int ch) -{ - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); - - wait_for_xmitr(up, UART_LSR_THRE); - serial_out(up, UART_TX, ch); -} - -/* - * 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. - */ -static void -serial8250_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_8250_port *up = &serial8250_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - touch_nmi_watchdog(); - - local_irq_save(flags); - if (up->port.sysrq) { - /* serial8250_handle_port() already took the lock */ - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - - if (up->capabilities & UART_CAP_UUE) - serial_out(up, UART_IER, UART_IER_UUE); - else - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, serial8250_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up, BOTH_EMPTY); - serial_out(up, UART_IER, ier); - - /* - * The receive handling will happen properly because the - * receive ready bit will still be set; it is not cleared - * on read. However, modem control will not, we must - * call it if we have saved something in the saved flags - * while processing with interrupts off. - */ - if (up->msr_saved_flags) - check_modem_status(up); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static int __init serial8250_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - 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. - */ - if (co->index >= nr_uarts) - co->index = 0; - port = &serial8250_ports[co->index].port; - if (!port->iobase && !port->membase) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static int serial8250_console_early_setup(void) -{ - return serial8250_find_port_for_earlycon(); -} - -static struct console serial8250_console = { - .name = "ttyS", - .write = serial8250_console_write, - .device = uart_console_device, - .setup = serial8250_console_setup, - .early_setup = serial8250_console_early_setup, - .flags = CON_PRINTBUFFER | CON_ANYTIME, - .index = -1, - .data = &serial8250_reg, -}; - -static int __init serial8250_console_init(void) -{ - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; - - serial8250_isa_init_ports(); - register_console(&serial8250_console); - return 0; -} -console_initcall(serial8250_console_init); - -int serial8250_find_port(struct uart_port *p) -{ - int line; - struct uart_port *port; - - for (line = 0; line < nr_uarts; line++) { - port = &serial8250_ports[line].port; - if (uart_match_port(p, port)) - return line; - } - return -ENODEV; -} - -#define SERIAL8250_CONSOLE &serial8250_console -#else -#define SERIAL8250_CONSOLE NULL -#endif - -static struct uart_driver serial8250_reg = { - .owner = THIS_MODULE, - .driver_name = "serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .cons = SERIAL8250_CONSOLE, -}; - -/* - * early_serial_setup - early registration for 8250 ports - * - * Setup an 8250 port structure prior to console initialisation. Use - * after console initialisation will cause undefined behaviour. - */ -int __init early_serial_setup(struct uart_port *port) -{ - struct uart_port *p; - - if (port->line >= ARRAY_SIZE(serial8250_ports)) - return -ENODEV; - - serial8250_isa_init_ports(); - p = &serial8250_ports[port->line].port; - p->iobase = port->iobase; - p->membase = port->membase; - p->irq = port->irq; - p->irqflags = port->irqflags; - p->uartclk = port->uartclk; - p->fifosize = port->fifosize; - p->regshift = port->regshift; - p->iotype = port->iotype; - p->flags = port->flags; - p->mapbase = port->mapbase; - p->private_data = port->private_data; - p->type = port->type; - p->line = port->line; - - set_io_from_upio(p); - if (port->serial_in) - p->serial_in = port->serial_in; - if (port->serial_out) - p->serial_out = port->serial_out; - - return 0; -} - -/** - * serial8250_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -void serial8250_suspend_port(int line) -{ - uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); -} - -/** - * serial8250_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -void serial8250_resume_port(int line) -{ - struct uart_8250_port *up = &serial8250_ports[line]; - - if (up->capabilities & UART_NATSEMI) { - unsigned char tmp; - - /* Ensure it's still in high speed mode */ - serial_outp(up, UART_LCR, 0xE0); - - tmp = serial_in(up, 0x04); /* EXCR2 */ - tmp &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ - tmp |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ - serial_outp(up, 0x04, tmp); - - serial_outp(up, UART_LCR, 0); - } - uart_resume_port(&serial8250_reg, &up->port); -} - -/* - * Register a set of serial devices attached to a platform device. The - * 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) -{ - struct plat_serial8250_port *p = dev->dev.platform_data; - struct uart_port port; - int ret, i, irqflag = 0; - - memset(&port, 0, sizeof(struct uart_port)); - - if (share_irqs) - irqflag = IRQF_SHARED; - - for (i = 0; p && p->flags != 0; p++, i++) { - port.iobase = p->iobase; - port.membase = p->membase; - port.irq = p->irq; - port.irqflags = p->irqflags; - port.uartclk = p->uartclk; - port.regshift = p->regshift; - port.iotype = p->iotype; - port.flags = p->flags; - port.mapbase = p->mapbase; - port.hub6 = p->hub6; - port.private_data = p->private_data; - port.type = p->type; - port.serial_in = p->serial_in; - port.serial_out = p->serial_out; - port.set_termios = p->set_termios; - port.pm = p->pm; - port.dev = &dev->dev; - port.irqflags |= irqflag; - ret = serial8250_register_port(&port); - if (ret < 0) { - dev_err(&dev->dev, "unable to register port at index %d " - "(IO%lx MEM%llx IRQ%d): %d\n", i, - p->iobase, (unsigned long long)p->mapbase, - p->irq, ret); - } - } - return 0; -} - -/* - * Remove serial ports registered against a platform device. - */ -static int __devexit serial8250_remove(struct platform_device *dev) -{ - int i; - - for (i = 0; i < nr_uarts; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.dev == &dev->dev) - serial8250_unregister_port(i); - } - return 0; -} - -static int serial8250_suspend(struct platform_device *dev, pm_message_t state) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - uart_suspend_port(&serial8250_reg, &up->port); - } - - return 0; -} - -static int serial8250_resume(struct platform_device *dev) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_8250_port *up = &serial8250_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - serial8250_resume_port(i); - } - - return 0; -} - -static struct platform_driver serial8250_isa_driver = { - .probe = serial8250_probe, - .remove = __devexit_p(serial8250_remove), - .suspend = serial8250_suspend, - .resume = serial8250_resume, - .driver = { - .name = "serial8250", - .owner = THIS_MODULE, - }, -}; - -/* - * This "device" covers _all_ ISA 8250-compatible serial devices listed - * in the table in include/asm/serial.h - */ -static struct platform_device *serial8250_isa_devs; - -/* - * serial8250_register_port and serial8250_unregister_port allows for - * 16x50 serial ports to be configured at run-time, to support PCMCIA - * modems and PCI multiport cards. - */ -static DEFINE_MUTEX(serial_mutex); - -static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *port) -{ - int i; - - /* - * First, find a port entry which matches. - */ - for (i = 0; i < nr_uarts; i++) - if (uart_match_port(&serial8250_ports[i].port, port)) - return &serial8250_ports[i]; - - /* - * We didn't find a matching entry, so look for the first - * free entry. We look for one which hasn't been previously - * used (indicated by zero iobase). - */ - for (i = 0; i < nr_uarts; i++) - if (serial8250_ports[i].port.type == PORT_UNKNOWN && - serial8250_ports[i].port.iobase == 0) - return &serial8250_ports[i]; - - /* - * That also failed. Last resort is to find any entry which - * doesn't have a real port associated with it. - */ - for (i = 0; i < nr_uarts; i++) - if (serial8250_ports[i].port.type == PORT_UNKNOWN) - return &serial8250_ports[i]; - - return NULL; -} - -/** - * serial8250_register_port - register a serial port - * @port: serial port template - * - * Configure the serial port specified by the request. If the - * port exists and is in use, it is hung up and unregistered - * first. - * - * The port is then probed and if necessary the IRQ is autodetected - * If this fails an error is returned. - * - * On success the port is ready to use and the line number is returned. - */ -int serial8250_register_port(struct uart_port *port) -{ - struct uart_8250_port *uart; - int ret = -ENOSPC; - - if (port->uartclk == 0) - return -EINVAL; - - mutex_lock(&serial_mutex); - - uart = serial8250_find_match_or_unused(port); - if (uart) { - uart_remove_one_port(&serial8250_reg, &uart->port); - - uart->port.iobase = port->iobase; - uart->port.membase = port->membase; - uart->port.irq = port->irq; - uart->port.irqflags = port->irqflags; - uart->port.uartclk = port->uartclk; - uart->port.fifosize = port->fifosize; - uart->port.regshift = port->regshift; - uart->port.iotype = port->iotype; - uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; - uart->port.mapbase = port->mapbase; - uart->port.private_data = port->private_data; - if (port->dev) - uart->port.dev = port->dev; - - if (port->flags & UPF_FIXED_TYPE) - serial8250_init_fixed_type_port(uart, port->type); - - set_io_from_upio(&uart->port); - /* Possibly override default I/O functions. */ - if (port->serial_in) - uart->port.serial_in = port->serial_in; - if (port->serial_out) - uart->port.serial_out = port->serial_out; - /* Possibly override set_termios call */ - if (port->set_termios) - uart->port.set_termios = port->set_termios; - if (port->pm) - uart->port.pm = port->pm; - - if (serial8250_isa_config != NULL) - serial8250_isa_config(0, &uart->port, - &uart->capabilities); - - ret = uart_add_one_port(&serial8250_reg, &uart->port); - if (ret == 0) - ret = uart->port.line; - } - mutex_unlock(&serial_mutex); - - return ret; -} -EXPORT_SYMBOL(serial8250_register_port); - -/** - * serial8250_unregister_port - remove a 16x50 serial port at runtime - * @line: serial line number - * - * Remove one serial port. This may not be called from interrupt - * context. We hand the port back to the our control. - */ -void serial8250_unregister_port(int line) -{ - struct uart_8250_port *uart = &serial8250_ports[line]; - - mutex_lock(&serial_mutex); - uart_remove_one_port(&serial8250_reg, &uart->port); - if (serial8250_isa_devs) { - uart->port.flags &= ~UPF_BOOT_AUTOCONF; - uart->port.type = PORT_UNKNOWN; - uart->port.dev = &serial8250_isa_devs->dev; - uart_add_one_port(&serial8250_reg, &uart->port); - } else { - uart->port.dev = NULL; - } - mutex_unlock(&serial_mutex); -} -EXPORT_SYMBOL(serial8250_unregister_port); - -static int __init serial8250_init(void) -{ - int ret; - - if (nr_uarts > UART_NR) - nr_uarts = UART_NR; - - printk(KERN_INFO "Serial: 8250/16550 driver, " - "%d ports, IRQ sharing %sabled\n", nr_uarts, - share_irqs ? "en" : "dis"); - -#ifdef CONFIG_SPARC - ret = sunserial_register_minors(&serial8250_reg, UART_NR); -#else - serial8250_reg.nr = UART_NR; - ret = uart_register_driver(&serial8250_reg); -#endif - if (ret) - goto out; - - serial8250_isa_devs = platform_device_alloc("serial8250", - PLAT8250_DEV_LEGACY); - if (!serial8250_isa_devs) { - ret = -ENOMEM; - goto unreg_uart_drv; - } - - ret = platform_device_add(serial8250_isa_devs); - if (ret) - goto put_dev; - - serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); - - ret = platform_driver_register(&serial8250_isa_driver); - if (ret == 0) - goto out; - - platform_device_del(serial8250_isa_devs); -put_dev: - platform_device_put(serial8250_isa_devs); -unreg_uart_drv: -#ifdef CONFIG_SPARC - sunserial_unregister_minors(&serial8250_reg, UART_NR); -#else - uart_unregister_driver(&serial8250_reg); -#endif -out: - return ret; -} - -static void __exit serial8250_exit(void) -{ - struct platform_device *isa_dev = serial8250_isa_devs; - - /* - * This tells serial8250_unregister_port() not to re-register - * the ports (thereby making serial8250_isa_driver permanently - * in use.) - */ - serial8250_isa_devs = NULL; - - platform_driver_unregister(&serial8250_isa_driver); - platform_device_unregister(isa_dev); - -#ifdef CONFIG_SPARC - sunserial_unregister_minors(&serial8250_reg, UART_NR); -#else - uart_unregister_driver(&serial8250_reg); -#endif -} - -module_init(serial8250_init); -module_exit(serial8250_exit); - -EXPORT_SYMBOL(serial8250_suspend_port); -EXPORT_SYMBOL(serial8250_resume_port); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); - -module_param(share_irqs, uint, 0644); -MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" - " (unsafe)"); - -module_param(nr_uarts, uint, 0644); -MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); - -module_param(skip_txen_test, uint, 0644); -MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); - -#ifdef CONFIG_SERIAL_8250_RSA -module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); -MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); -#endif -MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h deleted file mode 100644 index 6e19ea3e48d5..000000000000 --- a/drivers/serial/8250.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * linux/drivers/char/8250.h - * - * Driver for 8250/16550-type serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include - -struct old_serial_port { - unsigned int uart; - unsigned int baud_base; - unsigned int port; - unsigned int irq; - unsigned int flags; - unsigned char hub6; - unsigned char io_type; - unsigned char *iomem_base; - unsigned short iomem_reg_shift; - unsigned long irqflags; -}; - -/* - * This replaces serial_uart_config in include/linux/serial.h - */ -struct serial8250_config { - const char *name; - unsigned short fifo_size; - unsigned short tx_loadsz; - unsigned char fcr; - unsigned int flags; -}; - -#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ -#define UART_CAP_EFR (1 << 9) /* UART has EFR */ -#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ -#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ -#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ - -#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ -#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ -#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */ -#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ - -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) - -#ifdef CONFIG_SERIAL_8250_SHARE_IRQ -#define SERIAL8250_SHARE_IRQS 1 -#else -#define SERIAL8250_SHARE_IRQS 0 -#endif - -#if defined(__alpha__) && !defined(CONFIG_PCI) -/* - * Digital did something really horribly wrong with the OUT1 and OUT2 - * lines on at least some ALPHA's. The failure mode is that if either - * is cleared, the machine locks up with endless interrupts. - */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) -#elif defined(CONFIG_SBC8560) -/* - * WindRiver did something similarly broken on their SBC8560 board. The - * UART tristates its IRQ output while OUT2 is clear, but they pulled - * the interrupt line _up_ instead of down, so if we register the IRQ - * while the UART is in that state, we die in an IRQ storm. */ -#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) -#else -#define ALPHA_KLUDGE_MCR 0 -#endif diff --git a/drivers/serial/8250_accent.c b/drivers/serial/8250_accent.c deleted file mode 100644 index 9c10262f2469..000000000000 --- a/drivers/serial/8250_accent.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * linux/drivers/serial/8250_accent.c - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } - -static struct plat_serial8250_port accent_data[] = { - PORT(0x330, 4), - PORT(0x338, 4), - { }, -}; - -static struct platform_device accent_device = { - .name = "serial8250", - .id = PLAT8250_DEV_ACCENT, - .dev = { - .platform_data = accent_data, - }, -}; - -static int __init accent_init(void) -{ - return platform_device_register(&accent_device); -} - -module_init(accent_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_acorn.c b/drivers/serial/8250_acorn.c deleted file mode 100644 index b0ce8c56f1a4..000000000000 --- a/drivers/serial/8250_acorn.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * linux/drivers/serial/acorn.c - * - * Copyright (C) 1996-2003 Russell King. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "8250.h" - -#define MAX_PORTS 3 - -struct serial_card_type { - unsigned int num_ports; - unsigned int uartclk; - unsigned int type; - unsigned int offset[MAX_PORTS]; -}; - -struct serial_card_info { - unsigned int num_ports; - int ports[MAX_PORTS]; - void __iomem *vaddr; -}; - -static int __devinit -serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct serial_card_info *info; - struct serial_card_type *type = id->data; - struct uart_port port; - unsigned long bus_addr; - unsigned int i; - - info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->num_ports = type->num_ports; - - bus_addr = ecard_resource_start(ec, type->type); - info->vaddr = ecardm_iomap(ec, type->type, 0, 0); - if (!info->vaddr) { - kfree(info); - return -ENOMEM; - } - - ecard_set_drvdata(ec, info); - - memset(&port, 0, sizeof(struct uart_port)); - port.irq = ec->irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - port.uartclk = type->uartclk; - port.iotype = UPIO_MEM; - port.regshift = 2; - port.dev = &ec->dev; - - for (i = 0; i < info->num_ports; i ++) { - port.membase = info->vaddr + type->offset[i]; - port.mapbase = bus_addr + type->offset[i]; - - info->ports[i] = serial8250_register_port(&port); - } - - return 0; -} - -static void __devexit serial_card_remove(struct expansion_card *ec) -{ - struct serial_card_info *info = ecard_get_drvdata(ec); - int i; - - ecard_set_drvdata(ec, NULL); - - for (i = 0; i < info->num_ports; i++) - if (info->ports[i] > 0) - serial8250_unregister_port(info->ports[i]); - - kfree(info); -} - -static struct serial_card_type atomwide_type = { - .num_ports = 3, - .uartclk = 7372800, - .type = ECARD_RES_IOCSLOW, - .offset = { 0x2800, 0x2400, 0x2000 }, -}; - -static struct serial_card_type serport_type = { - .num_ports = 2, - .uartclk = 3686400, - .type = ECARD_RES_IOCSLOW, - .offset = { 0x2000, 0x2020 }, -}; - -static const struct ecard_id serial_cids[] = { - { MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, &atomwide_type }, - { MANU_SERPORT, PROD_SERPORT_DSPORT, &serport_type }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver serial_card_driver = { - .probe = serial_card_probe, - .remove = __devexit_p(serial_card_remove), - .id_table = serial_cids, - .drv = { - .name = "8250_acorn", - }, -}; - -static int __init serial_card_init(void) -{ - return ecard_register_driver(&serial_card_driver); -} - -static void __exit serial_card_exit(void) -{ - ecard_remove_driver(&serial_card_driver); -} - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); -MODULE_LICENSE("GPL"); - -module_init(serial_card_init); -module_exit(serial_card_exit); diff --git a/drivers/serial/8250_boca.c b/drivers/serial/8250_boca.c deleted file mode 100644 index 3bfe0f7b26fb..000000000000 --- a/drivers/serial/8250_boca.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * linux/drivers/serial/8250_boca.c - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } - -static struct plat_serial8250_port boca_data[] = { - PORT(0x100, 12), - PORT(0x108, 12), - PORT(0x110, 12), - PORT(0x118, 12), - PORT(0x120, 12), - PORT(0x128, 12), - PORT(0x130, 12), - PORT(0x138, 12), - PORT(0x140, 12), - PORT(0x148, 12), - PORT(0x150, 12), - PORT(0x158, 12), - PORT(0x160, 12), - PORT(0x168, 12), - PORT(0x170, 12), - PORT(0x178, 12), - { }, -}; - -static struct platform_device boca_device = { - .name = "serial8250", - .id = PLAT8250_DEV_BOCA, - .dev = { - .platform_data = boca_data, - }, -}; - -static int __init boca_init(void) -{ - return platform_device_register(&boca_device); -} - -module_init(boca_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for Boca cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_early.c b/drivers/serial/8250_early.c deleted file mode 100644 index eaafb98debed..000000000000 --- a/drivers/serial/8250_early.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Early serial console for 8250/16550 devices - * - * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. - * Bjorn Helgaas - * - * 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. - * - * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, - * and on early_printk.c by Andi Kleen. - * - * This is for use before the serial driver has initialized, in - * particular, before the UARTs have been discovered and named. - * Instead of specifying the console device as, e.g., "ttyS0", - * we locate the device directly by its MMIO or I/O port address. - * - * The user can specify the device directly, e.g., - * earlycon=uart8250,io,0x3f8,9600n8 - * earlycon=uart8250,mmio,0xff5e0000,115200n8 - * earlycon=uart8250,mmio32,0xff5e0000,115200n8 - * or - * console=uart8250,io,0x3f8,9600n8 - * console=uart8250,mmio,0xff5e0000,115200n8 - * console=uart8250,mmio32,0xff5e0000,115200n8 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_FIX_EARLYCON_MEM -#include -#include -#endif - -struct early_serial8250_device { - struct uart_port port; - char options[16]; /* e.g., 115200n8 */ - unsigned int baud; -}; - -static struct early_serial8250_device early_device; - -static unsigned int __init serial_in(struct uart_port *port, int offset) -{ - switch (port->iotype) { - case UPIO_MEM: - return readb(port->membase + offset); - case UPIO_MEM32: - return readl(port->membase + (offset << 2)); - case UPIO_PORT: - return inb(port->iobase + offset); - default: - return 0; - } -} - -static void __init serial_out(struct uart_port *port, int offset, int value) -{ - switch (port->iotype) { - case UPIO_MEM: - writeb(value, port->membase + offset); - break; - case UPIO_MEM32: - writel(value, port->membase + (offset << 2)); - break; - case UPIO_PORT: - outb(value, port->iobase + offset); - break; - } -} - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static void __init wait_for_xmitr(struct uart_port *port) -{ - unsigned int status; - - for (;;) { - status = serial_in(port, UART_LSR); - if ((status & BOTH_EMPTY) == BOTH_EMPTY) - return; - cpu_relax(); - } -} - -static void __init serial_putc(struct uart_port *port, int c) -{ - wait_for_xmitr(port); - serial_out(port, UART_TX, c); -} - -static void __init early_serial8250_write(struct console *console, - const char *s, unsigned int count) -{ - struct uart_port *port = &early_device.port; - unsigned int ier; - - /* Save the IER and disable interrupts */ - ier = serial_in(port, UART_IER); - serial_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); -} - -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); - - quot = (dlm << 8) | dll; - return (port->uartclk / 16) / quot; -} - -static void __init init_port(struct early_serial8250_device *device) -{ - struct uart_port *port = &device->port; - 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 */ - - divisor = 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); -} - -static int __init parse_options(struct early_serial8250_device *device, - char *options) -{ - struct uart_port *port = &device->port; - int mmio, mmio32, length; - - if (!options) - return -ENODEV; - - port->uartclk = BASE_BAUD * 16; - - mmio = !strncmp(options, "mmio,", 5); - mmio32 = !strncmp(options, "mmio32,", 7); - if (mmio || mmio32) { - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); - port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), - &options, 0); - if (mmio32) - port->regshift = 2; -#ifdef CONFIG_FIX_EARLYCON_MEM - set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, - port->mapbase & PAGE_MASK); - port->membase = - (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); - port->membase += port->mapbase & ~PAGE_MASK; -#else - port->membase = ioremap_nocache(port->mapbase, 64); - if (!port->membase) { - printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", - __func__, - (unsigned long long) port->mapbase); - return -ENOMEM; - } -#endif - } else if (!strncmp(options, "io,", 3)) { - port->iotype = UPIO_PORT; - port->iobase = simple_strtoul(options + 3, &options, 0); - mmio = 0; - } else - return -EINVAL; - - options = strchr(options, ','); - if (options) { - options++; - device->baud = simple_strtoul(options, NULL, 0); - length = min(strcspn(options, " "), sizeof(device->options)); - strncpy(device->options, options, length); - } else { - device->baud = probe_baud(port); - snprintf(device->options, sizeof(device->options), "%u", - device->baud); - } - - if (mmio || mmio32) - printk(KERN_INFO - "Early serial console at MMIO%s 0x%llx (options '%s')\n", - mmio32 ? "32" : "", - (unsigned long long)port->mapbase, - device->options); - else - printk(KERN_INFO - "Early serial console at I/O port 0x%lx (options '%s')\n", - port->iobase, - device->options); - - return 0; -} - -static struct console early_serial8250_console __initdata = { - .name = "uart", - .write = early_serial8250_write, - .flags = CON_PRINTBUFFER | CON_BOOT, - .index = -1, -}; - -static int __init early_serial8250_setup(char *options) -{ - struct early_serial8250_device *device = &early_device; - int err; - - if (device->port.membase || device->port.iobase) - return 0; - - err = parse_options(device, options); - if (err < 0) - return err; - - init_port(device); - return 0; -} - -int __init setup_early_serial8250_console(char *cmdline) -{ - char *options; - int err; - - options = strstr(cmdline, "uart8250,"); - if (!options) { - options = strstr(cmdline, "uart,"); - if (!options) - return 0; - } - - options = strchr(cmdline, ',') + 1; - err = early_serial8250_setup(options); - if (err < 0) - return err; - - register_console(&early_serial8250_console); - - return 0; -} - -int serial8250_find_port_for_earlycon(void) -{ - struct early_serial8250_device *device = &early_device; - struct uart_port *port = &device->port; - int line; - int ret; - - if (!device->port.membase && !device->port.iobase) - return -ENODEV; - - line = serial8250_find_port(port); - if (line < 0) - return -ENODEV; - - ret = update_console_cmdline("uart", 8250, - "ttyS", line, device->options); - if (ret < 0) - ret = update_console_cmdline("uart", 0, - "ttyS", line, device->options); - - return ret; -} - -early_param("earlycon", setup_early_serial8250_console); diff --git a/drivers/serial/8250_exar_st16c554.c b/drivers/serial/8250_exar_st16c554.c deleted file mode 100644 index 567143ace159..000000000000 --- a/drivers/serial/8250_exar_st16c554.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * linux/drivers/serial/8250_exar.c - * - * Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com > - * Based on 8250_boca. - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF, \ - } - -static struct plat_serial8250_port exar_data[] = { - PORT(0x100, 5), - PORT(0x108, 5), - PORT(0x110, 5), - PORT(0x118, 5), - { }, -}; - -static struct platform_device exar_device = { - .name = "serial8250", - .id = PLAT8250_DEV_EXAR_ST16C554, - .dev = { - .platform_data = exar_data, - }, -}; - -static int __init exar_init(void) -{ - return platform_device_register(&exar_device); -} - -module_init(exar_init); - -MODULE_AUTHOR("Paul B Schroeder"); -MODULE_DESCRIPTION("8250 serial probe module for Exar cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_fourport.c b/drivers/serial/8250_fourport.c deleted file mode 100644 index 6375d68b7913..000000000000 --- a/drivers/serial/8250_fourport.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * linux/drivers/serial/8250_fourport.c - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \ - } - -static struct plat_serial8250_port fourport_data[] = { - PORT(0x1a0, 9), - PORT(0x1a8, 9), - PORT(0x1b0, 9), - PORT(0x1b8, 9), - PORT(0x2a0, 5), - PORT(0x2a8, 5), - PORT(0x2b0, 5), - PORT(0x2b8, 5), - { }, -}; - -static struct platform_device fourport_device = { - .name = "serial8250", - .id = PLAT8250_DEV_FOURPORT, - .dev = { - .platform_data = fourport_data, - }, -}; - -static int __init fourport_init(void) -{ - return platform_device_register(&fourport_device); -} - -module_init(fourport_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c deleted file mode 100644 index d8c0ffbfa6e3..000000000000 --- a/drivers/serial/8250_gsc.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Serial Device Initialisation for Lasi/Asp/Wax/Dino - * - * (c) Copyright Matthew Wilcox 2001-2002 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "8250.h" - -static int __init serial_init_chip(struct parisc_device *dev) -{ - struct uart_port port; - unsigned long address; - int err; - - if (!dev->irq) { - /* We find some unattached serial ports by walking native - * busses. These should be silently ignored. Otherwise, - * what we have here is a missing parent device, so tell - * the user what they're missing. - */ - if (parisc_parent(dev)->id.hw_type != HPHW_IOA) - printk(KERN_INFO - "Serial: device 0x%llx not configured.\n" - "Enable support for Wax, Lasi, Asp or Dino.\n", - (unsigned long long)dev->hpa.start); - return -ENODEV; - } - - address = dev->hpa.start; - if (dev->id.sversion != 0x8d) - address += 0x800; - - memset(&port, 0, sizeof(port)); - port.iotype = UPIO_MEM; - /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ - port.uartclk = 7272727; - port.mapbase = address; - port.membase = ioremap_nocache(address, 16); - port.irq = dev->irq; - port.flags = UPF_BOOT_AUTOCONF; - port.dev = &dev->dev; - - err = serial8250_register_port(&port); - if (err < 0) { - printk(KERN_WARNING - "serial8250_register_port returned error %d\n", err); - iounmap(port.membase); - return err; - } - - return 0; -} - -static struct parisc_device_id serial_tbl[] = { - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, - { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, - { 0 } -}; - -/* Hack. Some machines have SERIAL_0 attached to Lasi and SERIAL_1 - * attached to Dino. Unfortunately, Dino appears before Lasi in the device - * tree. To ensure that ttyS0 == SERIAL_0, we register two drivers; one - * which only knows about Lasi and then a second which will find all the - * other serial ports. HPUX ignores this problem. - */ -static struct parisc_device_id lasi_tbl[] = { - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */ - { HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */ - { 0 } -}; - - -MODULE_DEVICE_TABLE(parisc, serial_tbl); - -static struct parisc_driver lasi_driver = { - .name = "serial_1", - .id_table = lasi_tbl, - .probe = serial_init_chip, -}; - -static struct parisc_driver serial_driver = { - .name = "serial", - .id_table = serial_tbl, - .probe = serial_init_chip, -}; - -static int __init probe_serial_gsc(void) -{ - register_parisc_driver(&lasi_driver); - register_parisc_driver(&serial_driver); - return 0; -} - -module_init(probe_serial_gsc); - -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_hp300.c b/drivers/serial/8250_hp300.c deleted file mode 100644 index c13438c93012..000000000000 --- a/drivers/serial/8250_hp300.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Driver for the 98626/98644/internal serial interface on hp300/hp400 - * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) - * - * Ported from 2.2 and modified to use the normal 8250 driver - * by Kars de Jong , May 2004. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "8250.h" - -#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) -#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? -#endif - -#ifdef CONFIG_HPAPCI -struct hp300_port -{ - struct hp300_port *next; /* next port */ - int line; /* line (tty) number */ -}; - -static struct hp300_port *hp300_ports; -#endif - -#ifdef CONFIG_HPDCA - -static int __devinit hpdca_init_one(struct dio_dev *d, - const struct dio_device_id *ent); -static void __devexit hpdca_remove_one(struct dio_dev *d); - -static struct dio_device_id hpdca_dio_tbl[] = { - { DIO_ID_DCA0 }, - { DIO_ID_DCA0REM }, - { DIO_ID_DCA1 }, - { DIO_ID_DCA1REM }, - { 0 } -}; - -static struct dio_driver hpdca_driver = { - .name = "hpdca", - .id_table = hpdca_dio_tbl, - .probe = hpdca_init_one, - .remove = __devexit_p(hpdca_remove_one), -}; - -#endif - -static unsigned int num_ports; - -extern int hp300_uart_scode; - -/* Offset to UART registers from base of DCA */ -#define UART_OFFSET 17 - -#define DCA_ID 0x01 /* ID (read), reset (write) */ -#define DCA_IC 0x03 /* Interrupt control */ - -/* Interrupt control */ -#define DCA_IC_IE 0x80 /* Master interrupt enable */ - -#define HPDCA_BAUD_BASE 153600 - -/* Base address of the Frodo part */ -#define FRODO_BASE (0x41c000) - -/* - * Where we find the 8250-like APCI ports, and how far apart they are. - */ -#define FRODO_APCIBASE 0x0 -#define FRODO_APCISPACE 0x20 -#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE)) - -#define HPAPCI_BAUD_BASE 500400 - -#ifdef CONFIG_SERIAL_8250_CONSOLE -/* - * Parse the bootinfo to find descriptions for headless console and - * debug serial ports and register them with the 8250 driver. - * This function should be called before serial_console_init() is called - * to make sure the serial console will be available for use. IA-64 kernel - * calls this function from setup_arch() after the EFI and ACPI tables have - * been parsed. - */ -int __init hp300_setup_serial_console(void) -{ - int scode; - struct uart_port port; - - memset(&port, 0, sizeof(port)); - - if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX) - return 0; - - if (DIO_SCINHOLE(hp300_uart_scode)) - return 0; - - scode = hp300_uart_scode; - - /* Memory mapped I/O */ - port.iotype = UPIO_MEM; - port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; - port.type = PORT_UNKNOWN; - - /* Check for APCI console */ - if (scode == 256) { -#ifdef CONFIG_HPAPCI - printk(KERN_INFO "Serial console is HP APCI 1\n"); - - port.uartclk = HPAPCI_BAUD_BASE * 16; - port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); - port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); - port.regshift = 2; - add_preferred_console("ttyS", port.line, "9600n8"); -#else - printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); - return 0; -#endif - } else { -#ifdef CONFIG_HPDCA - unsigned long pa = dio_scodetophysaddr(scode); - if (!pa) - return 0; - - printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode); - - port.uartclk = HPDCA_BAUD_BASE * 16; - port.mapbase = (pa + UART_OFFSET); - port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); - port.regshift = 1; - port.irq = DIO_IPL(pa + DIO_VIRADDRBASE); - - /* Enable board-interrupts */ - out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); - - if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) - add_preferred_console("ttyS", port.line, "9600n8"); -#else - printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); - return 0; -#endif - } - - if (early_serial_setup(&port) < 0) - printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n"); - return 0; -} -#endif /* CONFIG_SERIAL_8250_CONSOLE */ - -#ifdef CONFIG_HPDCA -static int __devinit hpdca_init_one(struct dio_dev *d, - const struct dio_device_id *ent) -{ - struct uart_port port; - int line; - -#ifdef CONFIG_SERIAL_8250_CONSOLE - if (hp300_uart_scode == d->scode) { - /* Already got it. */ - return 0; - } -#endif - memset(&port, 0, sizeof(struct uart_port)); - - /* Memory mapped I/O */ - port.iotype = UPIO_MEM; - port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; - port.irq = d->ipl; - port.uartclk = HPDCA_BAUD_BASE * 16; - port.mapbase = (d->resource.start + UART_OFFSET); - port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); - port.regshift = 1; - port.dev = &d->dev; - line = serial8250_register_port(&port); - - if (line < 0) { - printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" - " irq %d failed\n", d->scode, port.irq); - return -ENOMEM; - } - - /* Enable board-interrupts */ - out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); - dio_set_drvdata(d, (void *)line); - - /* Reset the DCA */ - out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); - udelay(100); - - num_ports++; - - return 0; -} -#endif - -static int __init hp300_8250_init(void) -{ - static int called; -#ifdef CONFIG_HPAPCI - int line; - unsigned long base; - struct uart_port uport; - struct hp300_port *port; - int i; -#endif - if (called) - return -ENODEV; - called = 1; - - if (!MACH_IS_HP300) - return -ENODEV; - -#ifdef CONFIG_HPDCA - dio_register_driver(&hpdca_driver); -#endif -#ifdef CONFIG_HPAPCI - if (hp300_model < HP_400) { - if (!num_ports) - return -ENODEV; - return 0; - } - /* These models have the Frodo chip. - * Port 0 is reserved for the Apollo Domain keyboard. - * Port 1 is either the console or the DCA. - */ - for (i = 1; i < 4; i++) { - /* Port 1 is the console on a 425e, on other machines it's - * mapped to DCA. - */ -#ifdef CONFIG_SERIAL_8250_CONSOLE - if (i == 1) - continue; -#endif - - /* Create new serial device */ - port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL); - if (!port) - return -ENOMEM; - - memset(&uport, 0, sizeof(struct uart_port)); - - base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); - - /* Memory mapped I/O */ - uport.iotype = UPIO_MEM; - uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ - | UPF_BOOT_AUTOCONF; - /* XXX - no interrupt support yet */ - uport.irq = 0; - uport.uartclk = HPAPCI_BAUD_BASE * 16; - uport.mapbase = base; - uport.membase = (char *)(base + DIO_VIRADDRBASE); - uport.regshift = 2; - - line = serial8250_register_port(&uport); - - if (line < 0) { - printk(KERN_NOTICE "8250_hp300: register_serial() APCI" - " %d irq %d failed\n", i, uport.irq); - kfree(port); - continue; - } - - port->line = line; - port->next = hp300_ports; - hp300_ports = port; - - num_ports++; - } -#endif - - /* Any boards found? */ - if (!num_ports) - return -ENODEV; - - return 0; -} - -#ifdef CONFIG_HPDCA -static void __devexit hpdca_remove_one(struct dio_dev *d) -{ - int line; - - line = (int) dio_get_drvdata(d); - if (d->resource.start) { - /* Disable board-interrupts */ - out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0); - } - serial8250_unregister_port(line); -} -#endif - -static void __exit hp300_8250_exit(void) -{ -#ifdef CONFIG_HPAPCI - struct hp300_port *port, *to_free; - - for (port = hp300_ports; port; ) { - serial8250_unregister_port(port->line); - to_free = port; - port = port->next; - kfree(to_free); - } - - hp300_ports = NULL; -#endif -#ifdef CONFIG_HPDCA - dio_unregister_driver(&hpdca_driver); -#endif -} - -module_init(hp300_8250_init); -module_exit(hp300_8250_exit); -MODULE_DESCRIPTION("HP DCA/APCI serial driver"); -MODULE_AUTHOR("Kars de Jong "); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_hub6.c b/drivers/serial/8250_hub6.c deleted file mode 100644 index 7609150e7d5e..000000000000 --- a/drivers/serial/8250_hub6.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * linux/drivers/serial/8250_hub6.c - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include - -#define HUB6(card,port) \ - { \ - .iobase = 0x302, \ - .irq = 3, \ - .uartclk = 1843200, \ - .iotype = UPIO_HUB6, \ - .flags = UPF_BOOT_AUTOCONF, \ - .hub6 = (card) << 6 | (port) << 3 | 1, \ - } - -static struct plat_serial8250_port hub6_data[] = { - HUB6(0, 0), - HUB6(0, 1), - HUB6(0, 2), - HUB6(0, 3), - HUB6(0, 4), - HUB6(0, 5), - HUB6(1, 0), - HUB6(1, 1), - HUB6(1, 2), - HUB6(1, 3), - HUB6(1, 4), - HUB6(1, 5), - { }, -}; - -static struct platform_device hub6_device = { - .name = "serial8250", - .id = PLAT8250_DEV_HUB6, - .dev = { - .platform_data = hub6_data, - }, -}; - -static int __init hub6_init(void) -{ - return platform_device_register(&hub6_device); -} - -module_init(hub6_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_mca.c b/drivers/serial/8250_mca.c deleted file mode 100644 index d10be944ad44..000000000000 --- a/drivers/serial/8250_mca.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * linux/drivers/serial/8250_mca.c - * - * Copyright (C) 2005 Russell King. - * Data taken from include/asm-i386/serial.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include - -/* - * FIXME: Should we be doing AUTO_IRQ here? - */ -#ifdef CONFIG_SERIAL_8250_DETECT_IRQ -#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ -#else -#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST -#endif - -#define PORT(_base,_irq) \ - { \ - .iobase = _base, \ - .irq = _irq, \ - .uartclk = 1843200, \ - .iotype = UPIO_PORT, \ - .flags = MCA_FLAGS, \ - } - -static struct plat_serial8250_port mca_data[] = { - PORT(0x3220, 3), - PORT(0x3228, 3), - PORT(0x4220, 3), - PORT(0x4228, 3), - PORT(0x5220, 3), - PORT(0x5228, 3), - { }, -}; - -static struct platform_device mca_device = { - .name = "serial8250", - .id = PLAT8250_DEV_MCA, - .dev = { - .platform_data = mca_data, - }, -}; - -static int __init mca_init(void) -{ - if (!MCA_bus) - return -ENODEV; - return platform_device_register(&mca_device); -} - -module_init(mca_init); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("8250 serial probe module for MCA ports"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c deleted file mode 100644 index 8b8930f700b5..000000000000 --- a/drivers/serial/8250_pci.c +++ /dev/null @@ -1,3850 +0,0 @@ -/* - * linux/drivers/char/8250_pci.c - * - * Probe module for 8250/16550-type PCI serial ports. - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "8250.h" - -#undef SERIAL_DEBUG_PCI - -/* - * init function returns: - * > 0 - number of ports - * = 0 - use board->num_ports - * < 0 - error - */ -struct pci_serial_quirk { - u32 vendor; - u32 device; - u32 subvendor; - u32 subdevice; - int (*init)(struct pci_dev *dev); - int (*setup)(struct serial_private *, - const struct pciserial_board *, - struct uart_port *, int); - void (*exit)(struct pci_dev *dev); -}; - -#define PCI_NUM_BAR_RESOURCES 6 - -struct serial_private { - struct pci_dev *dev; - unsigned int nr; - void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; - struct pci_serial_quirk *quirk; - int line[0]; -}; - -static void moan_device(const char *str, struct pci_dev *dev) -{ - printk(KERN_WARNING - "%s: %s\n" - "Please send the output of lspci -vv, this\n" - "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" - "manufacturer and name of serial board or\n" - "modem board to rmk+serial@arm.linux.org.uk.\n", - pci_name(dev), str, dev->vendor, dev->device, - dev->subsystem_vendor, dev->subsystem_device); -} - -static int -setup_port(struct serial_private *priv, struct uart_port *port, - int bar, int offset, int regshift) -{ - struct pci_dev *dev = priv->dev; - unsigned long base, len; - - if (bar >= PCI_NUM_BAR_RESOURCES) - return -EINVAL; - - base = pci_resource_start(dev, bar); - - if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { - len = pci_resource_len(dev, bar); - - if (!priv->remapped_bar[bar]) - priv->remapped_bar[bar] = ioremap_nocache(base, len); - if (!priv->remapped_bar[bar]) - return -ENOMEM; - - port->iotype = UPIO_MEM; - port->iobase = 0; - port->mapbase = base + offset; - port->membase = priv->remapped_bar[bar] + offset; - port->regshift = regshift; - } else { - port->iotype = UPIO_PORT; - port->iobase = base + offset; - port->mapbase = 0; - port->membase = NULL; - port->regshift = 0; - } - return 0; -} - -/* - * ADDI-DATA GmbH communication cards - */ -static int addidata_apci7800_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar = 0, offset = board->first_offset; - bar = FL_GET_BASE(board->flags); - - if (idx < 2) { - offset += idx * board->uart_offset; - } else if ((idx >= 2) && (idx < 4)) { - bar += 1; - offset += ((idx - 2) * board->uart_offset); - } else if ((idx >= 4) && (idx < 6)) { - bar += 2; - offset += ((idx - 4) * board->uart_offset); - } else if (idx >= 6) { - bar += 3; - offset += ((idx - 6) * board->uart_offset); - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * AFAVLAB uses a different mixture of BARs and offsets - * Not that ugly ;) -- HW - */ -static int -afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset; - - bar = FL_GET_BASE(board->flags); - if (idx < 4) - bar += idx; - else { - bar = 4; - offset += (idx - 4) * board->uart_offset; - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * HP's Remote Management Console. The Diva chip came in several - * different versions. N-class, L2000 and A500 have two Diva chips, each - * with 3 UARTs (the third UART on the second chip is unused). Superdome - * and Keystone have one Diva chip with 3 UARTs. Some later machines have - * one Diva chip, but it has been expanded to 5 UARTs. - */ -static int pci_hp_diva_init(struct pci_dev *dev) -{ - int rc = 0; - - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_TOSCA1: - case PCI_DEVICE_ID_HP_DIVA_HALFDOME: - case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - rc = 3; - break; - case PCI_DEVICE_ID_HP_DIVA_TOSCA2: - rc = 2; - break; - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - rc = 4; - break; - case PCI_DEVICE_ID_HP_DIVA_POWERBAR: - case PCI_DEVICE_ID_HP_DIVA_HURRICANE: - rc = 1; - break; - } - - return rc; -} - -/* - * HP's Diva chip puts the 4th/5th serial port further out, and - * some serial ports are supposed to be hidden on certain models. - */ -static int -pci_hp_diva_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int offset = board->first_offset; - unsigned int bar = FL_GET_BASE(board->flags); - - switch (priv->dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - if (idx == 3) - idx++; - break; - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - if (idx > 0) - idx++; - if (idx > 2) - idx++; - break; - } - if (idx > 2) - offset = 0x18; - - offset += idx * board->uart_offset; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * Added for EKF Intel i960 serial boards - */ -static int pci_inteli960ni_init(struct pci_dev *dev) -{ - unsigned long oldval; - - if (!(dev->subsystem_device & 0x1000)) - return -ENODEV; - - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void *)&oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return -ENODEV; - } - return 0; -} - -/* - * Some PCI serial cards using the PLX 9050 PCI interface chip require - * that the card interrupt be explicitly enabled or disabled. This - * seems to be mainly needed on card using the PLX which also use I/O - * mapped memory. - */ -static int pci_plx9050_init(struct pci_dev *dev) -{ - u8 irq_config; - void __iomem *p; - - if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar 0", dev); - return 0; - } - - irq_config = 0x41; - if (dev->vendor == PCI_VENDOR_ID_PANACOM || - dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS) - irq_config = 0x43; - - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the - * deep FIFOs - */ - irq_config = 0x5b; - /* - * enable/disable interrupts - */ - p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); - if (p == NULL) - return -ENOMEM; - writel(irq_config, p + 0x4c); - - /* - * Read the register back to ensure that it took effect. - */ - readl(p + 0x4c); - iounmap(p); - - return 0; -} - -static void __devexit pci_plx9050_exit(struct pci_dev *dev) -{ - u8 __iomem *p; - - if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) - return; - - /* - * disable interrupts - */ - p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); - if (p != NULL) { - writel(0, p + 0x4c); - - /* - * Read the register back to ensure that it took effect. - */ - readl(p + 0x4c); - iounmap(p); - } -} - -#define NI8420_INT_ENABLE_REG 0x38 -#define NI8420_INT_ENABLE_BIT 0x2000 - -static void __devexit pci_ni8420_exit(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return; - - /* Disable the CPU Interrupt */ - writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT), - p + NI8420_INT_ENABLE_REG); - iounmap(p); -} - - -/* MITE registers */ -#define MITE_IOWBSR1 0xc4 -#define MITE_IOWCR1 0xf4 -#define MITE_LCIMR1 0x08 -#define MITE_LCIMR2 0x10 - -#define MITE_LCIMR2_CLR_CPU_IE (1 << 30) - -static void __devexit pci_ni8430_exit(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return; - - /* Disable the CPU Interrupt */ - writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2); - iounmap(p); -} - -/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ -static int -sbs_setup(struct serial_private *priv, const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset; - - bar = 0; - - if (idx < 4) { - /* first four channels map to 0, 0x100, 0x200, 0x300 */ - offset += idx * board->uart_offset; - } else if (idx < 8) { - /* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */ - offset += idx * board->uart_offset + 0xC00; - } else /* we have only 8 ports on PMC-OCTALPRO */ - return 1; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* -* This does initialization for PMC OCTALPRO cards: -* maps the device memory, resets the UARTs (needed, bc -* if the module is removed and inserted again, the card -* is in the sleep mode) and enables global interrupt. -*/ - -/* global control register offset for SBS PMC-OctalPro */ -#define OCT_REG_CR_OFF 0x500 - -static int sbs_init(struct pci_dev *dev) -{ - u8 __iomem *p; - - p = pci_ioremap_bar(dev, 0); - - if (p == NULL) - return -ENOMEM; - /* Set bit-4 Control Register (UART RESET) in to reset the uarts */ - writeb(0x10, p + OCT_REG_CR_OFF); - udelay(50); - writeb(0x0, p + OCT_REG_CR_OFF); - - /* Set bit-2 (INTENABLE) of Control Register */ - writeb(0x4, p + OCT_REG_CR_OFF); - iounmap(p); - - return 0; -} - -/* - * Disables the global interrupt of PMC-OctalPro - */ - -static void __devexit sbs_exit(struct pci_dev *dev) -{ - u8 __iomem *p; - - p = pci_ioremap_bar(dev, 0); - /* FIXME: What if resource_len < OCT_REG_CR_OFF */ - if (p != NULL) - writeb(0, p + OCT_REG_CR_OFF); - iounmap(p); -} - -/* - * SIIG serial cards have an PCI interface chip which also controls - * the UART clocking frequency. Each UART can be clocked independently - * (except cards equiped with 4 UARTs) and initial clocking settings - * are stored in the EEPROM chip. It can cause problems because this - * version of serial driver doesn't support differently clocked UART's - * on single PCI card. To prevent this, initialization functions set - * high frequency clocking for all UART's on given card. It is safe (I - * hope) because it doesn't touch EEPROM settings to prevent conflicts - * with other OSes (like M$ DOS). - * - * SIIG support added by Andrey Panin , 10/1999 - * - * There is two family of SIIG serial cards with different PCI - * interface chip and different configuration methods: - * - 10x cards have control registers in IO and/or memory space; - * - 20x cards have control registers in standard PCI configuration space. - * - * Note: all 10x cards have PCI device ids 0x10.. - * all 20x cards have PCI device ids 0x20.. - * - * There are also Quartet Serial cards which use Oxford Semiconductor - * 16954 quad UART PCI chip clocked by 18.432 MHz quartz. - * - * Note: some SIIG cards are probed by the parport_serial object. - */ - -#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) - -static int pci_siig10x_init(struct pci_dev *dev) -{ - u16 data; - void __iomem *p; - - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; - } - - p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); - if (p == NULL) - return -ENOMEM; - - writew(readw(p + 0x28) & data, p + 0x28); - readw(p + 0x28); - iounmap(p); - return 0; -} - -#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) -#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) - -static int pci_siig20x_init(struct pci_dev *dev) -{ - u8 data; - - /* Change clock frequency for the first UART. */ - pci_read_config_byte(dev, 0x6f, &data); - pci_write_config_byte(dev, 0x6f, data & 0xef); - - /* If this card has 2 UART, we have to do the same with second UART. */ - if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || - ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { - pci_read_config_byte(dev, 0x73, &data); - pci_write_config_byte(dev, 0x73, data & 0xef); - } - return 0; -} - -static int pci_siig_init(struct pci_dev *dev) -{ - unsigned int type = dev->device & 0xff00; - - if (type == 0x1000) - return pci_siig10x_init(dev); - else if (type == 0x2000) - return pci_siig20x_init(dev); - - moan_device("Unknown SIIG card", dev); - return -ENODEV; -} - -static int pci_siig_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; - - if (idx > 3) { - bar = 4; - offset = (idx - 4) * 8; - } - - return setup_port(priv, port, bar, offset, 0); -} - -/* - * Timedia has an explosion of boards, and to avoid the PCI table from - * growing *huge*, we use this function to collapse some 70 entries - * in the PCI table into one, for sanity's and compactness's sake. - */ -static const unsigned short timedia_single_port[] = { - 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 -}; - -static const unsigned short timedia_dual_port[] = { - 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, - 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, - 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, - 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, - 0xD079, 0 -}; - -static const unsigned short timedia_quad_port[] = { - 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, - 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, - 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, - 0xB157, 0 -}; - -static const unsigned short timedia_eight_port[] = { - 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, - 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 -}; - -static const struct timedia_struct { - int num; - const unsigned short *ids; -} timedia_data[] = { - { 1, timedia_single_port }, - { 2, timedia_dual_port }, - { 4, timedia_quad_port }, - { 8, timedia_eight_port } -}; - -static int pci_timedia_init(struct pci_dev *dev) -{ - const unsigned short *ids; - int i, j; - - for (i = 0; i < ARRAY_SIZE(timedia_data); i++) { - ids = timedia_data[i].ids; - for (j = 0; ids[j]; j++) - if (dev->subsystem_device == ids[j]) - return timedia_data[i].num; - } - return 0; -} - -/* - * Timedia/SUNIX uses a mixture of BARs and offsets - * Ugh, this is ugly as all hell --- TYT - */ -static int -pci_timedia_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar = 0, offset = board->first_offset; - - switch (idx) { - case 0: - bar = 0; - break; - case 1: - offset = board->uart_offset; - bar = 0; - break; - case 2: - bar = 1; - break; - case 3: - offset = board->uart_offset; - /* FALLTHROUGH */ - case 4: /* BAR 2 */ - case 5: /* BAR 3 */ - case 6: /* BAR 4 */ - case 7: /* BAR 5 */ - bar = idx - 2; - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -/* - * Some Titan cards are also a little weird - */ -static int -titan_400l_800l_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset; - - switch (idx) { - case 0: - bar = 1; - break; - case 1: - bar = 2; - break; - default: - bar = 4; - offset = (idx - 2) * board->uart_offset; - } - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -static int pci_xircom_init(struct pci_dev *dev) -{ - msleep(100); - return 0; -} - -static int pci_ni8420_init(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return 0; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return -ENOMEM; - - /* Enable CPU Interrupt */ - writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT, - p + NI8420_INT_ENABLE_REG); - - iounmap(p); - return 0; -} - -#define MITE_IOWBSR1_WSIZE 0xa -#define MITE_IOWBSR1_WIN_OFFSET 0x800 -#define MITE_IOWBSR1_WENAB (1 << 7) -#define MITE_LCIMR1_IO_IE_0 (1 << 24) -#define MITE_LCIMR2_SET_CPU_IE (1 << 31) -#define MITE_IOWCR1_RAMSEL_MASK 0xfffffffe - -static int pci_ni8430_init(struct pci_dev *dev) -{ - void __iomem *p; - unsigned long base, len; - u32 device_window; - unsigned int bar = 0; - - if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { - moan_device("no memory in bar", dev); - return 0; - } - - base = pci_resource_start(dev, bar); - len = pci_resource_len(dev, bar); - p = ioremap_nocache(base, len); - if (p == NULL) - return -ENOMEM; - - /* Set device window address and size in BAR0 */ - device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) - | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; - writel(device_window, p + MITE_IOWBSR1); - - /* Set window access to go to RAMSEL IO address space */ - writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK), - p + MITE_IOWCR1); - - /* Enable IO Bus Interrupt 0 */ - writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1); - - /* Enable CPU Interrupt */ - writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2); - - iounmap(p); - return 0; -} - -/* UART Port Control Register */ -#define NI8430_PORTCON 0x0f -#define NI8430_PORTCON_TXVR_ENABLE (1 << 3) - -static int -pci_ni8430_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - void __iomem *p; - unsigned long base, len; - unsigned int bar, offset = board->first_offset; - - if (idx >= board->num_ports) - return 1; - - bar = FL_GET_BASE(board->flags); - offset += idx * board->uart_offset; - - base = pci_resource_start(priv->dev, bar); - len = pci_resource_len(priv->dev, bar); - p = ioremap_nocache(base, len); - - /* enable the transciever */ - writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, - p + offset + NI8430_PORTCON); - - iounmap(p); - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - - -static int pci_netmos_init(struct pci_dev *dev) -{ - /* subdevice 0x00PS means

parallel, serial */ - unsigned int num_serial = dev->subsystem_device & 0xf; - - if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || - (dev->device == PCI_DEVICE_ID_NETMOS_9865)) - return 0; - if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && - dev->subsystem_device == 0x0299) - return 0; - - if (num_serial == 0) - return -ENODEV; - return num_serial; -} - -/* - * These chips are available with optionally one parallel port and up to - * two serial ports. Unfortunately they all have the same product id. - * - * Basic configuration is done over a region of 32 I/O ports. The base - * ioport is called INTA or INTC, depending on docs/other drivers. - * - * The region of the 32 I/O ports is configured in POSIO0R... - */ - -/* registers */ -#define ITE_887x_MISCR 0x9c -#define ITE_887x_INTCBAR 0x78 -#define ITE_887x_UARTBAR 0x7c -#define ITE_887x_PS0BAR 0x10 -#define ITE_887x_POSIO0 0x60 - -/* I/O space size */ -#define ITE_887x_IOSIZE 32 -/* I/O space size (bits 26-24; 8 bytes = 011b) */ -#define ITE_887x_POSIO_IOSIZE_8 (3 << 24) -/* I/O space size (bits 26-24; 32 bytes = 101b) */ -#define ITE_887x_POSIO_IOSIZE_32 (5 << 24) -/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */ -#define ITE_887x_POSIO_SPEED (3 << 29) -/* enable IO_Space bit */ -#define ITE_887x_POSIO_ENABLE (1 << 31) - -static int pci_ite887x_init(struct pci_dev *dev) -{ - /* inta_addr are the configuration addresses of the ITE */ - static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, - 0x200, 0x280, 0 }; - int ret, i, type; - struct resource *iobase = NULL; - u32 miscr, uartbar, ioport; - - /* search for the base-ioport */ - i = 0; - while (inta_addr[i] && iobase == NULL) { - iobase = request_region(inta_addr[i], ITE_887x_IOSIZE, - "ite887x"); - if (iobase != NULL) { - /* write POSIO0R - speed | size | ioport */ - pci_write_config_dword(dev, ITE_887x_POSIO0, - ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | - ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]); - /* write INTCBAR - ioport */ - pci_write_config_dword(dev, ITE_887x_INTCBAR, - inta_addr[i]); - ret = inb(inta_addr[i]); - if (ret != 0xff) { - /* ioport connected */ - break; - } - release_region(iobase->start, ITE_887x_IOSIZE); - iobase = NULL; - } - i++; - } - - if (!inta_addr[i]) { - printk(KERN_ERR "ite887x: could not find iobase\n"); - return -ENODEV; - } - - /* start of undocumented type checking (see parport_pc.c) */ - type = inb(iobase->start + 0x18) & 0x0f; - - switch (type) { - case 0x2: /* ITE8871 (1P) */ - case 0xa: /* ITE8875 (1P) */ - ret = 0; - break; - case 0xe: /* ITE8872 (2S1P) */ - ret = 2; - break; - case 0x6: /* ITE8873 (1S) */ - ret = 1; - break; - case 0x8: /* ITE8874 (2S) */ - ret = 2; - break; - default: - moan_device("Unknown ITE887x", dev); - ret = -ENODEV; - } - - /* configure all serial ports */ - for (i = 0; i < ret; i++) { - /* read the I/O port from the device */ - pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)), - &ioport); - ioport &= 0x0000FF00; /* the actual base address */ - pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)), - ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | - ITE_887x_POSIO_IOSIZE_8 | ioport); - - /* write the ioport to the UARTBAR */ - pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar); - uartbar &= ~(0xffff << (16 * i)); /* clear half the reg */ - uartbar |= (ioport << (16 * i)); /* set the ioport */ - pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar); - - /* get current config */ - pci_read_config_dword(dev, ITE_887x_MISCR, &miscr); - /* disable interrupts (UARTx_Routing[3:0]) */ - miscr &= ~(0xf << (12 - 4 * i)); - /* activate the UART (UARTx_En) */ - miscr |= 1 << (23 - i); - /* write new config with activated UART */ - pci_write_config_dword(dev, ITE_887x_MISCR, miscr); - } - - if (ret <= 0) { - /* the device has no UARTs if we get here */ - release_region(iobase->start, ITE_887x_IOSIZE); - } - - return ret; -} - -static void __devexit pci_ite887x_exit(struct pci_dev *dev) -{ - u32 ioport; - /* the ioport is bit 0-15 in POSIO0R */ - pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport); - ioport &= 0xffff; - release_region(ioport, ITE_887x_IOSIZE); -} - -/* - * Oxford Semiconductor Inc. - * Check that device is part of the Tornado range of devices, then determine - * the number of ports available on the device. - */ -static int pci_oxsemi_tornado_init(struct pci_dev *dev) -{ - u8 __iomem *p; - unsigned long deviceID; - unsigned int number_uarts = 0; - - /* OxSemi Tornado devices are all 0xCxxx */ - if (dev->vendor == PCI_VENDOR_ID_OXSEMI && - (dev->device & 0xF000) != 0xC000) - return 0; - - p = pci_iomap(dev, 0, 5); - if (p == NULL) - return -ENOMEM; - - deviceID = ioread32(p); - /* Tornado device */ - if (deviceID == 0x07000200) { - number_uarts = ioread8(p + 4); - printk(KERN_DEBUG - "%d ports detected on Oxford PCI Express device\n", - number_uarts); - } - pci_iounmap(dev, p); - return number_uarts; -} - -static int -pci_default_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - unsigned int bar, offset = board->first_offset, maxnr; - - bar = FL_GET_BASE(board->flags); - if (board->flags & FL_BASE_BARS) - bar += idx; - else - offset += idx * board->uart_offset; - - maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> - (board->reg_shift + 3); - - if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) - return 1; - - return setup_port(priv, port, bar, offset, board->reg_shift); -} - -static int -ce4100_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - int ret; - - ret = setup_port(priv, port, 0, 0, board->reg_shift); - port->iotype = UPIO_MEM32; - port->type = PORT_XSCALE; - port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); - port->regshift = 2; - - return ret; -} - -static int skip_tx_en_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_port *port, int idx) -{ - port->flags |= UPF_NO_TXEN_TEST; - printk(KERN_DEBUG "serial8250: skipping TxEn test for device " - "[%04x:%04x] subsystem [%04x:%04x]\n", - priv->dev->vendor, - priv->dev->device, - priv->dev->subsystem_vendor, - priv->dev->subsystem_device); - - return pci_default_setup(priv, board, port, idx); -} - -/* This should be in linux/pci_ids.h */ -#define PCI_VENDOR_ID_SBSMODULARIO 0x124B -#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B -#define PCI_DEVICE_ID_OCTPRO 0x0001 -#define PCI_SUBDEVICE_ID_OCTPRO232 0x0108 -#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 -#define PCI_SUBDEVICE_ID_POCTAL232 0x0308 -#define PCI_SUBDEVICE_ID_POCTAL422 0x0408 -#define PCI_VENDOR_ID_ADVANTECH 0x13fe -#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 -#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 -#define PCI_DEVICE_ID_TITAN_200I 0x8028 -#define PCI_DEVICE_ID_TITAN_400I 0x8048 -#define PCI_DEVICE_ID_TITAN_800I 0x8088 -#define PCI_DEVICE_ID_TITAN_800EH 0xA007 -#define PCI_DEVICE_ID_TITAN_800EHB 0xA008 -#define PCI_DEVICE_ID_TITAN_400EH 0xA009 -#define PCI_DEVICE_ID_TITAN_100E 0xA010 -#define PCI_DEVICE_ID_TITAN_200E 0xA012 -#define PCI_DEVICE_ID_TITAN_400E 0xA013 -#define PCI_DEVICE_ID_TITAN_800E 0xA014 -#define PCI_DEVICE_ID_TITAN_200EI 0xA016 -#define PCI_DEVICE_ID_TITAN_200EISI 0xA017 -#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 - -/* Unknown vendors/cards - this should not be in linux/pci_ids.h */ -#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 - -/* - * Master list of serial port init/setup/exit quirks. - * This does not describe the general nature of the port. - * (ie, baud base, number and location of ports, etc) - * - * This list is ordered alphabetically by vendor then device. - * Specific entries must come before more generic entries. - */ -static struct pci_serial_quirk pci_serial_quirks[] __refdata = { - /* - * ADDI-DATA GmbH communication cards - */ - { - .vendor = PCI_VENDOR_ID_ADDIDATA_OLD, - .device = PCI_DEVICE_ID_ADDIDATA_APCI7800, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = addidata_apci7800_setup, - }, - /* - * AFAVLAB cards - these may be called via parport_serial - * It is not clear whether this applies to all products. - */ - { - .vendor = PCI_VENDOR_ID_AFAVLAB, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = afavlab_setup, - }, - /* - * HP Diva - */ - { - .vendor = PCI_VENDOR_ID_HP, - .device = PCI_DEVICE_ID_HP_DIVA, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_hp_diva_init, - .setup = pci_hp_diva_setup, - }, - /* - * Intel - */ - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_80960_RP, - .subvendor = 0xe4bf, - .subdevice = PCI_ANY_ID, - .init = pci_inteli960ni_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_8257X_SOL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = skip_tx_en_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_82573L_SOL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = skip_tx_en_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_82573E_SOL, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = skip_tx_en_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_CE4100_UART, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = ce4100_serial_setup, - }, - /* - * ITE - */ - { - .vendor = PCI_VENDOR_ID_ITE, - .device = PCI_DEVICE_ID_ITE_8872, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ite887x_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ite887x_exit), - }, - /* - * National Instruments - */ - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI23216, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2328, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2324, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2322, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2324I, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PCI2322I, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_23216, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_2328, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_2324, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8420_2322, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8422_2324, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_DEVICE_ID_NI_PXI8422_2322, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8420_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_ni8420_exit), - }, - { - .vendor = PCI_VENDOR_ID_NI, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_ni8430_init, - .setup = pci_ni8430_setup, - .exit = __devexit_p(pci_ni8430_exit), - }, - /* - * Panacom - */ - { - .vendor = PCI_VENDOR_ID_PANACOM, - .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PANACOM, - .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - /* - * PLX - */ - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9030, - .subvendor = PCI_SUBVENDOR_ID_PERLE, - .subdevice = PCI_ANY_ID, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_SUBVENDOR_ID_EXSYS, - .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, - .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_9050, - .subvendor = PCI_VENDOR_ID_PLX, - .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - { - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_PLX_ROMULUS, - .subvendor = PCI_VENDOR_ID_PLX, - .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, - .init = pci_plx9050_init, - .setup = pci_default_setup, - .exit = __devexit_p(pci_plx9050_exit), - }, - /* - * SBS Technologies, Inc., PMC-OCTALPRO 232 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_OCTPRO232, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SBS Technologies, Inc., PMC-OCTALPRO 422 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_OCTPRO422, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SBS Technologies, Inc., P-Octal 232 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_POCTAL232, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SBS Technologies, Inc., P-Octal 422 - */ - { - .vendor = PCI_VENDOR_ID_SBSMODULARIO, - .device = PCI_DEVICE_ID_OCTPRO, - .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, - .subdevice = PCI_SUBDEVICE_ID_POCTAL422, - .init = sbs_init, - .setup = sbs_setup, - .exit = __devexit_p(sbs_exit), - }, - /* - * SIIG cards - these may be called via parport_serial - */ - { - .vendor = PCI_VENDOR_ID_SIIG, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_siig_init, - .setup = pci_siig_setup, - }, - /* - * Titan cards - */ - { - .vendor = PCI_VENDOR_ID_TITAN, - .device = PCI_DEVICE_ID_TITAN_400L, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = titan_400l_800l_setup, - }, - { - .vendor = PCI_VENDOR_ID_TITAN, - .device = PCI_DEVICE_ID_TITAN_800L, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = titan_400l_800l_setup, - }, - /* - * Timedia cards - */ - { - .vendor = PCI_VENDOR_ID_TIMEDIA, - .device = PCI_DEVICE_ID_TIMEDIA_1889, - .subvendor = PCI_VENDOR_ID_TIMEDIA, - .subdevice = PCI_ANY_ID, - .init = pci_timedia_init, - .setup = pci_timedia_setup, - }, - { - .vendor = PCI_VENDOR_ID_TIMEDIA, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_timedia_setup, - }, - /* - * Xircom cards - */ - { - .vendor = PCI_VENDOR_ID_XIRCOM, - .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_xircom_init, - .setup = pci_default_setup, - }, - /* - * Netmos cards - these may be called via parport_serial - */ - { - .vendor = PCI_VENDOR_ID_NETMOS, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_netmos_init, - .setup = pci_default_setup, - }, - /* - * For Oxford Semiconductor and Mainpine - */ - { - .vendor = PCI_VENDOR_ID_OXSEMI, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, - }, - { - .vendor = PCI_VENDOR_ID_MAINPINE, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .init = pci_oxsemi_tornado_init, - .setup = pci_default_setup, - }, - /* - * Default "match everything" terminator entry - */ - { - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pci_default_setup, - } -}; - -static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) -{ - return quirk_id == PCI_ANY_ID || quirk_id == dev_id; -} - -static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) -{ - struct pci_serial_quirk *quirk; - - for (quirk = pci_serial_quirks; ; quirk++) - if (quirk_id_matches(quirk->vendor, dev->vendor) && - quirk_id_matches(quirk->device, dev->device) && - quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && - quirk_id_matches(quirk->subdevice, dev->subsystem_device)) - break; - return quirk; -} - -static inline int get_pci_irq(struct pci_dev *dev, - const struct pciserial_board *board) -{ - if (board->flags & FL_NOIRQ) - return 0; - else - return dev->irq; -} - -/* - * This is the configuration table for all of the PCI serial boards - * which we support. It is directly indexed by the pci_board_num_t enum - * value, which is encoded in the pci_device_id PCI probe table's - * driver_data member. - * - * The makeup of these names are: - * pbn_bn{_bt}_n_baud{_offsetinhex} - * - * bn = PCI BAR number - * bt = Index using PCI BARs - * n = number of serial ports - * baud = baud rate - * offsetinhex = offset for each sequential port (in hex) - * - * This table is sorted by (in order): bn, bt, baud, offsetindex, n. - * - * Please note: in theory if n = 1, _bt infix should make no difference. - * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 - */ -enum pci_board_num_t { - pbn_default = 0, - - pbn_b0_1_115200, - pbn_b0_2_115200, - pbn_b0_4_115200, - pbn_b0_5_115200, - pbn_b0_8_115200, - - pbn_b0_1_921600, - pbn_b0_2_921600, - pbn_b0_4_921600, - - pbn_b0_2_1130000, - - pbn_b0_4_1152000, - - pbn_b0_2_1843200, - pbn_b0_4_1843200, - - pbn_b0_2_1843200_200, - pbn_b0_4_1843200_200, - pbn_b0_8_1843200_200, - - pbn_b0_1_4000000, - - pbn_b0_bt_1_115200, - pbn_b0_bt_2_115200, - pbn_b0_bt_4_115200, - pbn_b0_bt_8_115200, - - pbn_b0_bt_1_460800, - pbn_b0_bt_2_460800, - pbn_b0_bt_4_460800, - - pbn_b0_bt_1_921600, - pbn_b0_bt_2_921600, - pbn_b0_bt_4_921600, - pbn_b0_bt_8_921600, - - pbn_b1_1_115200, - pbn_b1_2_115200, - pbn_b1_4_115200, - pbn_b1_8_115200, - pbn_b1_16_115200, - - pbn_b1_1_921600, - pbn_b1_2_921600, - pbn_b1_4_921600, - pbn_b1_8_921600, - - pbn_b1_2_1250000, - - pbn_b1_bt_1_115200, - pbn_b1_bt_2_115200, - pbn_b1_bt_4_115200, - - pbn_b1_bt_2_921600, - - pbn_b1_1_1382400, - pbn_b1_2_1382400, - pbn_b1_4_1382400, - pbn_b1_8_1382400, - - pbn_b2_1_115200, - pbn_b2_2_115200, - pbn_b2_4_115200, - pbn_b2_8_115200, - - pbn_b2_1_460800, - pbn_b2_4_460800, - pbn_b2_8_460800, - pbn_b2_16_460800, - - pbn_b2_1_921600, - pbn_b2_4_921600, - pbn_b2_8_921600, - - pbn_b2_8_1152000, - - pbn_b2_bt_1_115200, - pbn_b2_bt_2_115200, - pbn_b2_bt_4_115200, - - pbn_b2_bt_2_921600, - pbn_b2_bt_4_921600, - - pbn_b3_2_115200, - pbn_b3_4_115200, - pbn_b3_8_115200, - - pbn_b4_bt_2_921600, - pbn_b4_bt_4_921600, - pbn_b4_bt_8_921600, - - /* - * Board-specific versions. - */ - pbn_panacom, - pbn_panacom2, - pbn_panacom4, - pbn_exsys_4055, - pbn_plx_romulus, - pbn_oxsemi, - pbn_oxsemi_1_4000000, - pbn_oxsemi_2_4000000, - pbn_oxsemi_4_4000000, - pbn_oxsemi_8_4000000, - pbn_intel_i960, - pbn_sgi_ioc3, - pbn_computone_4, - pbn_computone_6, - pbn_computone_8, - pbn_sbsxrsio, - pbn_exar_XR17C152, - pbn_exar_XR17C154, - pbn_exar_XR17C158, - pbn_exar_ibm_saturn, - pbn_pasemi_1682M, - pbn_ni8430_2, - pbn_ni8430_4, - pbn_ni8430_8, - pbn_ni8430_16, - pbn_ADDIDATA_PCIe_1_3906250, - pbn_ADDIDATA_PCIe_2_3906250, - pbn_ADDIDATA_PCIe_4_3906250, - pbn_ADDIDATA_PCIe_8_3906250, - pbn_ce4100_1_115200, -}; - -/* - * uart_offset - the space between channels - * reg_shift - describes how the UART registers are mapped - * to PCI memory by the card. - * For example IER register on SBS, Inc. PMC-OctPro is located at - * offset 0x10 from the UART base, while UART_IER is defined as 1 - * in include/linux/serial_reg.h, - * see first lines of serial_in() and serial_out() in 8250.c -*/ - -static struct pciserial_board pci_boards[] __devinitdata = { - [pbn_default] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_1_115200] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_2_115200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_4_115200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_5_115200] = { - .flags = FL_BASE0, - .num_ports = 5, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_8_115200] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_1_921600] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_2_921600] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_4_921600] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b0_2_1130000] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1130000, - .uart_offset = 8, - }, - - [pbn_b0_4_1152000] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1152000, - .uart_offset = 8, - }, - - [pbn_b0_2_1843200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1843200, - .uart_offset = 8, - }, - [pbn_b0_4_1843200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1843200, - .uart_offset = 8, - }, - - [pbn_b0_2_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_4_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_8_1843200_200] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 1843200, - .uart_offset = 0x200, - }, - [pbn_b0_1_4000000] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 4000000, - .uart_offset = 8, - }, - - [pbn_b0_bt_1_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_bt_2_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_bt_4_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b0_bt_8_115200] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b0_bt_1_460800] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b0_bt_2_460800] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b0_bt_4_460800] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 460800, - .uart_offset = 8, - }, - - [pbn_b0_bt_1_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_bt_2_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_bt_4_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b0_bt_8_921600] = { - .flags = FL_BASE0|FL_BASE_BARS, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b1_1_115200] = { - .flags = FL_BASE1, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_2_115200] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_4_115200] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_8_115200] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_16_115200] = { - .flags = FL_BASE1, - .num_ports = 16, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b1_1_921600] = { - .flags = FL_BASE1, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_2_921600] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_4_921600] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_8_921600] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b1_2_1250000] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 1250000, - .uart_offset = 8, - }, - - [pbn_b1_bt_1_115200] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_bt_2_115200] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b1_bt_4_115200] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b1_bt_2_921600] = { - .flags = FL_BASE1|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b1_1_1382400] = { - .flags = FL_BASE1, - .num_ports = 1, - .base_baud = 1382400, - .uart_offset = 8, - }, - [pbn_b1_2_1382400] = { - .flags = FL_BASE1, - .num_ports = 2, - .base_baud = 1382400, - .uart_offset = 8, - }, - [pbn_b1_4_1382400] = { - .flags = FL_BASE1, - .num_ports = 4, - .base_baud = 1382400, - .uart_offset = 8, - }, - [pbn_b1_8_1382400] = { - .flags = FL_BASE1, - .num_ports = 8, - .base_baud = 1382400, - .uart_offset = 8, - }, - - [pbn_b2_1_115200] = { - .flags = FL_BASE2, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_2_115200] = { - .flags = FL_BASE2, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_4_115200] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_8_115200] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b2_1_460800] = { - .flags = FL_BASE2, - .num_ports = 1, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b2_4_460800] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b2_8_460800] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 460800, - .uart_offset = 8, - }, - [pbn_b2_16_460800] = { - .flags = FL_BASE2, - .num_ports = 16, - .base_baud = 460800, - .uart_offset = 8, - }, - - [pbn_b2_1_921600] = { - .flags = FL_BASE2, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b2_4_921600] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b2_8_921600] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b2_8_1152000] = { - .flags = FL_BASE2, - .num_ports = 8, - .base_baud = 1152000, - .uart_offset = 8, - }, - - [pbn_b2_bt_1_115200] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 1, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_bt_2_115200] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b2_bt_4_115200] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b2_bt_2_921600] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b2_bt_4_921600] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - - [pbn_b3_2_115200] = { - .flags = FL_BASE3, - .num_ports = 2, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b3_4_115200] = { - .flags = FL_BASE3, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_b3_8_115200] = { - .flags = FL_BASE3, - .num_ports = 8, - .base_baud = 115200, - .uart_offset = 8, - }, - - [pbn_b4_bt_2_921600] = { - .flags = FL_BASE4, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b4_bt_4_921600] = { - .flags = FL_BASE4, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8, - }, - [pbn_b4_bt_8_921600] = { - .flags = FL_BASE4, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 8, - }, - - /* - * Entries following this are board-specific. - */ - - /* - * Panacom - IOMEM - */ - [pbn_panacom] = { - .flags = FL_BASE2, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x400, - .reg_shift = 7, - }, - [pbn_panacom2] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x400, - .reg_shift = 7, - }, - [pbn_panacom4] = { - .flags = FL_BASE2|FL_BASE_BARS, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x400, - .reg_shift = 7, - }, - - [pbn_exsys_4055] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 115200, - .uart_offset = 8, - }, - - /* I think this entry is broken - the first_offset looks wrong --rmk */ - [pbn_plx_romulus] = { - .flags = FL_BASE2, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 8 << 2, - .reg_shift = 2, - .first_offset = 0x03, - }, - - /* - * This board uses the size of PCI Base region 0 to - * signal now many ports are available - */ - [pbn_oxsemi] = { - .flags = FL_BASE0|FL_REGION_SZ_CAP, - .num_ports = 32, - .base_baud = 115200, - .uart_offset = 8, - }, - [pbn_oxsemi_1_4000000] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_oxsemi_2_4000000] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_oxsemi_4_4000000] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_oxsemi_8_4000000] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 4000000, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - - - /* - * EKF addition for i960 Boards form EKF with serial port. - * Max 256 ports. - */ - [pbn_intel_i960] = { - .flags = FL_BASE0, - .num_ports = 32, - .base_baud = 921600, - .uart_offset = 8 << 2, - .reg_shift = 2, - .first_offset = 0x10000, - }, - [pbn_sgi_ioc3] = { - .flags = FL_BASE0|FL_NOIRQ, - .num_ports = 1, - .base_baud = 458333, - .uart_offset = 8, - .reg_shift = 0, - .first_offset = 0x20178, - }, - - /* - * Computone - uses IOMEM. - */ - [pbn_computone_4] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x40, - .reg_shift = 2, - .first_offset = 0x200, - }, - [pbn_computone_6] = { - .flags = FL_BASE0, - .num_ports = 6, - .base_baud = 921600, - .uart_offset = 0x40, - .reg_shift = 2, - .first_offset = 0x200, - }, - [pbn_computone_8] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 0x40, - .reg_shift = 2, - .first_offset = 0x200, - }, - [pbn_sbsxrsio] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 460800, - .uart_offset = 256, - .reg_shift = 4, - }, - /* - * Exar Corp. XR17C15[248] Dual/Quad/Octal UART - * Only basic 16550A support. - * XR17C15[24] are not tested, but they should work. - */ - [pbn_exar_XR17C152] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17C154] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_XR17C158] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 921600, - .uart_offset = 0x200, - }, - [pbn_exar_ibm_saturn] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .uart_offset = 0x200, - }, - - /* - * PA Semi PWRficient PA6T-1682M on-chip UART - */ - [pbn_pasemi_1682M] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 8333333, - }, - /* - * National Instruments 843x - */ - [pbn_ni8430_16] = { - .flags = FL_BASE0, - .num_ports = 16, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - [pbn_ni8430_8] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - [pbn_ni8430_4] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - [pbn_ni8430_2] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 3686400, - .uart_offset = 0x10, - .first_offset = 0x800, - }, - /* - * ADDI-DATA GmbH PCI-Express communication cards - */ - [pbn_ADDIDATA_PCIe_1_3906250] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ADDIDATA_PCIe_2_3906250] = { - .flags = FL_BASE0, - .num_ports = 2, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ADDIDATA_PCIe_4_3906250] = { - .flags = FL_BASE0, - .num_ports = 4, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ADDIDATA_PCIe_8_3906250] = { - .flags = FL_BASE0, - .num_ports = 8, - .base_baud = 3906250, - .uart_offset = 0x200, - .first_offset = 0x1000, - }, - [pbn_ce4100_1_115200] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 921600, - .reg_shift = 2, - }, -}; - -static const struct pci_device_id softmodem_blacklist[] = { - { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ - { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ - { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ -}; - -/* - * Given a complete unknown PCI device, try to use some heuristics to - * guess what the configuration might be, based on the pitiful PCI - * serial specs. Returns 0 on success, 1 on failure. - */ -static int __devinit -serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) -{ - const struct pci_device_id *blacklist; - int num_iomem, num_port, first_port = -1, i; - - /* - * If it is not a communications device or the programming - * interface is greater than 6, give up. - * - * (Should we try to make guesses for multiport serial devices - * later?) - */ - if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && - ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || - (dev->class & 0xff) > 6) - return -ENODEV; - - /* - * Do not access blacklisted devices that are known not to - * feature serial ports. - */ - for (blacklist = softmodem_blacklist; - blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist); - blacklist++) { - if (dev->vendor == blacklist->vendor && - dev->device == blacklist->device) - return -ENODEV; - } - - num_iomem = num_port = 0; - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (pci_resource_flags(dev, i) & IORESOURCE_IO) { - num_port++; - if (first_port == -1) - first_port = i; - } - if (pci_resource_flags(dev, i) & IORESOURCE_MEM) - num_iomem++; - } - - /* - * If there is 1 or 0 iomem regions, and exactly one port, - * use it. We guess the number of ports based on the IO - * region size. - */ - if (num_iomem <= 1 && num_port == 1) { - board->flags = first_port; - board->num_ports = pci_resource_len(dev, first_port) / 8; - return 0; - } - - /* - * Now guess if we've got a board which indexes by BARs. - * Each IO BAR should be 8 bytes, and they should follow - * consecutively. - */ - first_port = -1; - num_port = 0; - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (pci_resource_flags(dev, i) & IORESOURCE_IO && - pci_resource_len(dev, i) == 8 && - (first_port == -1 || (first_port + num_port) == i)) { - num_port++; - if (first_port == -1) - first_port = i; - } - } - - if (num_port > 1) { - board->flags = first_port | FL_BASE_BARS; - board->num_ports = num_port; - return 0; - } - - return -ENODEV; -} - -static inline int -serial_pci_matches(const struct pciserial_board *board, - const struct pciserial_board *guessed) -{ - return - board->num_ports == guessed->num_ports && - board->base_baud == guessed->base_baud && - board->uart_offset == guessed->uart_offset && - board->reg_shift == guessed->reg_shift && - board->first_offset == guessed->first_offset; -} - -struct serial_private * -pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) -{ - struct uart_port serial_port; - struct serial_private *priv; - struct pci_serial_quirk *quirk; - int rc, nr_ports, i; - - nr_ports = board->num_ports; - - /* - * Find an init and setup quirks. - */ - quirk = find_quirk(dev); - - /* - * Run the new-style initialization function. - * The initialization function returns: - * <0 - error - * 0 - use board->num_ports - * >0 - number of ports - */ - if (quirk->init) { - rc = quirk->init(dev); - if (rc < 0) { - priv = ERR_PTR(rc); - goto err_out; - } - if (rc) - nr_ports = rc; - } - - priv = kzalloc(sizeof(struct serial_private) + - sizeof(unsigned int) * nr_ports, - GFP_KERNEL); - if (!priv) { - priv = ERR_PTR(-ENOMEM); - goto err_deinit; - } - - priv->dev = dev; - priv->quirk = quirk; - - memset(&serial_port, 0, sizeof(struct uart_port)); - serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; - serial_port.uartclk = board->base_baud * 16; - serial_port.irq = get_pci_irq(dev, board); - serial_port.dev = &dev->dev; - - for (i = 0; i < nr_ports; i++) { - if (quirk->setup(priv, board, &serial_port, i)) - break; - -#ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", - serial_port.iobase, serial_port.irq, serial_port.iotype); -#endif - - priv->line[i] = serial8250_register_port(&serial_port); - if (priv->line[i] < 0) { - printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); - break; - } - } - priv->nr = i; - return priv; - -err_deinit: - if (quirk->exit) - quirk->exit(dev); -err_out: - return priv; -} -EXPORT_SYMBOL_GPL(pciserial_init_ports); - -void pciserial_remove_ports(struct serial_private *priv) -{ - struct pci_serial_quirk *quirk; - int i; - - for (i = 0; i < priv->nr; i++) - serial8250_unregister_port(priv->line[i]); - - for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { - if (priv->remapped_bar[i]) - iounmap(priv->remapped_bar[i]); - priv->remapped_bar[i] = NULL; - } - - /* - * Find the exit quirks. - */ - quirk = find_quirk(priv->dev); - if (quirk->exit) - quirk->exit(priv->dev); - - kfree(priv); -} -EXPORT_SYMBOL_GPL(pciserial_remove_ports); - -void pciserial_suspend_ports(struct serial_private *priv) -{ - int i; - - for (i = 0; i < priv->nr; i++) - if (priv->line[i] >= 0) - serial8250_suspend_port(priv->line[i]); -} -EXPORT_SYMBOL_GPL(pciserial_suspend_ports); - -void pciserial_resume_ports(struct serial_private *priv) -{ - int i; - - /* - * Ensure that the board is correctly configured. - */ - if (priv->quirk->init) - priv->quirk->init(priv->dev); - - for (i = 0; i < priv->nr; i++) - if (priv->line[i] >= 0) - serial8250_resume_port(priv->line[i]); -} -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 -pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) -{ - struct serial_private *priv; - const struct pciserial_board *board; - struct pciserial_board tmp; - int rc; - - if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { - printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", - ent->driver_data); - return -EINVAL; - } - - board = &pci_boards[ent->driver_data]; - - rc = pci_enable_device(dev); - if (rc) - return rc; - - if (ent->driver_data == pbn_default) { - /* - * Use a copy of the pci_board entry for this; - * avoid changing entries in the table. - */ - memcpy(&tmp, board, sizeof(struct pciserial_board)); - board = &tmp; - - /* - * We matched one of our class entries. Try to - * determine the parameters of this board. - */ - rc = serial_pci_guess_board(dev, &tmp); - if (rc) - goto disable; - } else { - /* - * We matched an explicit entry. If we are able to - * detect this boards settings with our heuristic, - * then we no longer need this entry. - */ - memcpy(&tmp, &pci_boards[pbn_default], - sizeof(struct pciserial_board)); - rc = serial_pci_guess_board(dev, &tmp); - if (rc == 0 && serial_pci_matches(board, &tmp)) - moan_device("Redundant entry in serial pci_table.", - dev); - } - - priv = pciserial_init_ports(dev, board); - if (!IS_ERR(priv)) { - pci_set_drvdata(dev, priv); - return 0; - } - - rc = PTR_ERR(priv); - - disable: - pci_disable_device(dev); - return rc; -} - -static void __devexit pciserial_remove_one(struct pci_dev *dev) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - pci_set_drvdata(dev, NULL); - - pciserial_remove_ports(priv); - - pci_disable_device(dev); -} - -#ifdef CONFIG_PM -static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state) -{ - struct serial_private *priv = pci_get_drvdata(dev); - - if (priv) - pciserial_suspend_ports(priv); - - pci_save_state(dev); - pci_set_power_state(dev, pci_choose_state(dev, state)); - return 0; -} - -static int pciserial_resume_one(struct pci_dev *dev) -{ - int err; - struct serial_private *priv = pci_get_drvdata(dev); - - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - - if (priv) { - /* - * The device may have been disabled. Re-enable it. - */ - err = pci_enable_device(dev); - /* FIXME: We cannot simply error out here */ - if (err) - printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); - pciserial_resume_ports(priv); - } - return 0; -} -#endif - -static struct pci_device_id serial_pci_tbl[] = { - /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ - { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, - PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, - pbn_b1_8_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, - pbn_b1_4_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, - pbn_b1_2_1382400 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, - pbn_b1_2_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, - pbn_b1_8_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, - pbn_b1_4_921600 }, - { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0, - pbn_b1_2_1250000 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0, - pbn_b0_2_1843200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0, - pbn_b0_4_1843200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_AFAVLAB, - PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, - pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0, - pbn_b0_2_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0, - pbn_b0_4_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_SUBVENDOR_ID_CONNECT_TECH, - PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, - pbn_b0_8_1843200_200 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, - 0, 0, pbn_exar_ibm_saturn }, - - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_1_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_115200 }, - - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - /* - * VScom SPCOM800, from sl@s.pl - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_8_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_4_921600 }, - /* Unknown card - subdevice 0x1584 */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_VENDOR_ID_PLX, - PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_KEYSPAN, - PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, - pbn_panacom }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom4 }, - { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_panacom2 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_VENDOR_ID_ESDGMBH, - PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0, - pbn_b2_4_115200 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIFAST, - PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, - pbn_b2_16_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, - pbn_b2_4_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_CHASE_PCIRAS, - PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, - pbn_b2_8_460800 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, - PCI_SUBVENDOR_ID_EXSYS, - PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, - pbn_exsys_4055 }, - /* - * Megawolf Romulus PCI Serial Card, from Mike Hudson - * (Exoray@isys.ca) - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, - 0x10b5, 0x106a, 0, 0, - pbn_plx_romulus }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_4_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_2_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, - 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, - 0, 0, - pbn_b0_4_1152000 }, - { PCI_VENDOR_ID_OXSEMI, 0x9505, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - - /* - * The below card is a little controversial since it is the - * subject of a PCI vendor/device ID clash. (See - * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). - * For now just used the hex ID 0x950a. - */ - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0, - pbn_b0_2_115200 }, - { PCI_VENDOR_ID_OXSEMI, 0x950a, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_1130000 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, - PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_115200 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, - PCI_ANY_ID , PCI_ANY_ID, 0, 0, - pbn_b2_8_1152000 }, - - /* - * Oxford Semiconductor Inc. Tornado PCI express device range. - */ - { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - /* - * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado - */ - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ - PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, - pbn_oxsemi_8_4000000 }, - /* - * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, - * from skokodyn@yahoo.com - */ - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0, - pbn_sbsxrsio }, - { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, - PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0, - pbn_sbsxrsio }, - - /* - * Digitan DS560-558, from jimd@esoft.com - */ - { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_115200 }, - - /* - * Titan Electronic cards - * The 400L and 800L have a custom setup quirk. - */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_2_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b4_bt_8_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_1_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_4_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_8_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi_2_4000000 }, - - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_460800 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_921600 }, - - /* - * Computone devices submitted by Doug McNash dmcnash@computone.com - */ - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, - 0, 0, pbn_computone_4 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, - 0, 0, pbn_computone_8 }, - { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, - PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, - 0, 0, pbn_computone_6 }, - - { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_oxsemi }, - { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_921600 }, - - /* - * AFAVLAB serial card, from Harald Welte - */ - { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_115200 }, - { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_8_115200 }, - - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_4_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_460800 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_1_460800 }, - - /* - * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408). - * Cards are identified by their subsystem vendor IDs, which - * (in hex) match the model number. - * - * Note that JC140x are RS422/485 cards which require ox950 - * ACR = 0x10, and as such are not currently fully supported. - */ - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1204, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, -/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1402, 0x0002, 0, 0, - pbn_b0_2_921600 }, */ -/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, - 0x1404, 0x0004, 0, 0, - pbn_b0_4_921600 }, */ - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, - 0x1204, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, - 0x1208, 0x0004, 0, 0, - pbn_b0_4_921600 }, - /* - * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com - */ - { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_1382400 }, - - /* - * Dell Remote Access Card III - Tim_T_Murphy@Dell.com - */ - { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_1_1382400 }, - - /* - * RAStel 2 port modem, gerg@moreton.com.au - */ - { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_bt_2_115200 }, - - /* - * EKF addition for i960 Boards form EKF with serial port - */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, - 0xE4BF, PCI_ANY_ID, 0, 0, - pbn_intel_i960 }, - - /* - * Xircom Cardbus/Ethernet combos - */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - /* - * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) - */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - - /* - * Untested PCI modems, sent in from various folks... - */ - - /* - * Elsa Model 56K PCI Modem, from Andreas Rath - */ - { PCI_VENDOR_ID_ROCKWELL, 0x1004, - 0x1048, 0x1500, 0, 0, - pbn_b1_1_115200 }, - - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, - 0xFF00, 0, 0, 0, - pbn_sgi_ioc3 }, - - /* - * HP Diva card - */ - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, - PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0, - pbn_b1_1_115200 }, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_5_115200 }, - { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b2_1_115200 }, - - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_2_115200 }, - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_4_115200 }, - { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b3_8_115200 }, - - /* - * Exar Corp. XR17C15[248] Dual/Quad/Octal UART - */ - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C152 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C154 }, - { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, - PCI_ANY_ID, PCI_ANY_ID, - 0, - 0, pbn_exar_XR17C158 }, - - /* - * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) - */ - { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, - /* - * ITE - */ - { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, - pbn_b1_bt_1_115200 }, - - /* - * IntaShield IS-200 - */ - { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0811 */ - pbn_b2_2_115200 }, - /* - * IntaShield IS-400 - */ - { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */ - pbn_b2_4_115200 }, - /* - * Perle PCI-RAS cards - */ - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4, - 0, 0, pbn_b2_4_921600 }, - { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, - PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8, - 0, 0, pbn_b2_8_921600 }, - - /* - * Mainpine series cards: Fairly standard layout but fools - * parts of the autodetect in some cases and uses otherwise - * unmatched communications subclasses in the PCI Express case - */ - - { /* RockForceDUO */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0200, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUATRO */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0300, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceDUO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0400, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUATRO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0500, - 0, 0, pbn_b0_4_115200 }, - { /* RockForce+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0600, - 0, 0, pbn_b0_2_115200 }, - { /* RockForce+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0700, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceOCTO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0800, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceDUO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0C00, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceQUARTRO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x0D00, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceOCTO+ */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x1D00, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceD1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2000, - 0, 0, pbn_b0_1_115200 }, - { /* RockForceF1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2100, - 0, 0, pbn_b0_1_115200 }, - { /* RockForceD2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2200, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceF2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2300, - 0, 0, pbn_b0_2_115200 }, - { /* RockForceD4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2400, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceF4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2500, - 0, 0, pbn_b0_4_115200 }, - { /* RockForceD8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2600, - 0, 0, pbn_b0_8_115200 }, - { /* RockForceF8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x2700, - 0, 0, pbn_b0_8_115200 }, - { /* IQ Express D1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3000, - 0, 0, pbn_b0_1_115200 }, - { /* IQ Express F1 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3100, - 0, 0, pbn_b0_1_115200 }, - { /* IQ Express D2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3200, - 0, 0, pbn_b0_2_115200 }, - { /* IQ Express F2 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3300, - 0, 0, pbn_b0_2_115200 }, - { /* IQ Express D4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3400, - 0, 0, pbn_b0_4_115200 }, - { /* IQ Express F4 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3500, - 0, 0, pbn_b0_4_115200 }, - { /* IQ Express D8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3C00, - 0, 0, pbn_b0_8_115200 }, - { /* IQ Express F8 */ - PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, - PCI_VENDOR_ID_MAINPINE, 0x3D00, - 0, 0, pbn_b0_8_115200 }, - - - /* - * PA Semi PA6T-1682M on-chip UART - */ - { PCI_VENDOR_ID_PASEMI, 0xa004, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_pasemi_1682M }, - - /* - * National Instruments - */ - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_16_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_16_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_8_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_4_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b1_bt_2_115200 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_8 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_8 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_16 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_16 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_2 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ni8430_4 }, - - /* - * ADDI-DATA GmbH communication cards - */ - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA_OLD, - PCI_DEVICE_ID_ADDIDATA_APCI7800, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b1_8_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300_2, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7500_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_4_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7420_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_2_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7300_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCI7800_3, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_b0_8_115200 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7500, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_4_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7420, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_2_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7300, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_1_3906250 }, - - { PCI_VENDOR_ID_ADDIDATA, - PCI_DEVICE_ID_ADDIDATA_APCIe7800, - PCI_ANY_ID, - PCI_ANY_ID, - 0, - 0, - pbn_ADDIDATA_PCIe_8_3906250 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_VENDOR_ID_IBM, 0x0299, - 0, 0, pbn_b0_bt_2_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - /* - * Best Connectivity PCI Multi I/O cards - */ - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x1000, - 0, 0, pbn_b0_1_115200 }, - - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, - 0xA000, 0x3004, - 0, 0, pbn_b0_bt_4_115200 }, - /* Intel CE4100 */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_ce4100_1_115200 }, - - - /* - * These entries match devices with class COMMUNICATION_SERIAL, - * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL - */ - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_SERIAL << 8, - 0xffff00, pbn_default }, - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MODEM << 8, - 0xffff00, pbn_default }, - { PCI_ANY_ID, PCI_ANY_ID, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, - 0xffff00, pbn_default }, - { 0, } -}; - -static struct pci_driver serial_pci_driver = { - .name = "serial", - .probe = pciserial_init_one, - .remove = __devexit_p(pciserial_remove_one), -#ifdef CONFIG_PM - .suspend = pciserial_suspend_one, - .resume = pciserial_resume_one, -#endif - .id_table = serial_pci_tbl, -}; - -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_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); -MODULE_DEVICE_TABLE(pci, serial_pci_tbl); diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c deleted file mode 100644 index 4822cb50cd0f..000000000000 --- a/drivers/serial/8250_pnp.c +++ /dev/null @@ -1,523 +0,0 @@ -/* - * linux/drivers/char/8250_pnp.c - * - * Probe module for 8250/16550-type ISAPNP serial ports. - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2001 Russell King, All Rights Reserved. - * - * Ported to the Linux PnP Layer - (C) Adam Belay. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "8250.h" - -#define UNKNOWN_DEV 0x3000 - - -static const struct pnp_device_id pnp_dev_table[] = { - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "AAC000F", 0 }, - /* Anchor Datacomm BV */ - /* SXPro 144 External Data Fax Modem Plug & Play */ - { "ADC0001", 0 }, - /* SXPro 288 External Data Fax Modem Plug & Play */ - { "ADC0002", 0 }, - /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ - { "AEI0250", 0 }, - /* Actiontec ISA PNP 56K X2 Fax Modem */ - { "AEI1240", 0 }, - /* Rockwell 56K ACF II Fax+Data+Voice Modem */ - { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, - /* AZT3005 PnP SOUND DEVICE */ - { "AZT4001", 0 }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { "BDP3336", 0 }, - /* Boca Research */ - /* Boca Complete Ofc Communicator 14.4 Data-FAX */ - { "BRI0A49", 0 }, - /* Boca Research 33,600 ACF Modem */ - { "BRI1400", 0 }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { "BRI3400", 0 }, - /* Boca 33.6 Kbps Internal FD34FSVD */ - { "BRI0A49", 0 }, - /* Best Data Products Inc. Smart One 336F PnP Modem */ - { "BDP3336", 0 }, - /* Computer Peripherals Inc */ - /* EuroViVa CommCenter-33.6 SP PnP */ - { "CPI4050", 0 }, - /* Creative Labs */ - /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ - { "CTL3001", 0 }, - /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ - { "CTL3011", 0 }, - /* Davicom ISA 33.6K Modem */ - { "DAV0336", 0 }, - /* Creative */ - /* Creative Modem Blaster Flash56 DI5601-1 */ - { "DMB1032", 0 }, - /* Creative Modem Blaster V.90 DI5660 */ - { "DMB2001", 0 }, - /* E-Tech */ - /* E-Tech CyberBULLET PC56RVP */ - { "ETT0002", 0 }, - /* FUJITSU */ - /* Fujitsu 33600 PnP-I2 R Plug & Play */ - { "FUJ0202", 0 }, - /* Fujitsu FMV-FX431 Plug & Play */ - { "FUJ0205", 0 }, - /* Fujitsu 33600 PnP-I4 R Plug & Play */ - { "FUJ0206", 0 }, - /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ - { "FUJ0209", 0 }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "GVC000F", 0 }, - /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ - { "GVC0303", 0 }, - /* Hayes */ - /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ - { "HAY0001", 0 }, - /* Hayes Optima 336 V.34 + FAX + Voice PnP */ - { "HAY000C", 0 }, - /* Hayes Optima 336B V.34 + FAX + Voice PnP */ - { "HAY000D", 0 }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5670", 0 }, - /* Hayes Accura 56K Ext Fax Modem PnP */ - { "HAY5674", 0 }, - /* Hayes Accura 56K Fax Modem PnP */ - { "HAY5675", 0 }, - /* Hayes 288, V.34 + FAX */ - { "HAYF000", 0 }, - /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ - { "HAYF001", 0 }, - /* IBM */ - /* IBM Thinkpad 701 Internal Modem Voice */ - { "IBM0033", 0 }, - /* Intertex */ - /* Intertex 28k8 33k6 Voice EXT PnP */ - { "IXDC801", 0 }, - /* Intertex 33k6 56k Voice EXT PnP */ - { "IXDC901", 0 }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDD801", 0 }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDD901", 0 }, - /* Intertex 28k8 33k6 Voice SP INT PnP */ - { "IXDF401", 0 }, - /* Intertex 28k8 33k6 Voice SP EXT PnP */ - { "IXDF801", 0 }, - /* Intertex 33k6 56k Voice SP EXT PnP */ - { "IXDF901", 0 }, - /* Kortex International */ - /* KORTEX 28800 Externe PnP */ - { "KOR4522", 0 }, - /* KXPro 33.6 Vocal ASVD PnP */ - { "KORF661", 0 }, - /* Lasat */ - /* LASAT Internet 33600 PnP */ - { "LAS4040", 0 }, - /* Lasat Safire 560 PnP */ - { "LAS4540", 0 }, - /* Lasat Safire 336 PnP */ - { "LAS5440", 0 }, - /* Microcom, Inc. */ - /* Microcom TravelPorte FAST V.34 Plug & Play */ - { "MNP0281", 0 }, - /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ - { "MNP0336", 0 }, - /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ - { "MNP0339", 0 }, - /* Microcom DeskPorte 28.8P Plug & Play */ - { "MNP0342", 0 }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0500", 0 }, - /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ - { "MNP0501", 0 }, - /* Microcom DeskPorte 28.8S Internal Plug & Play */ - { "MNP0502", 0 }, - /* Motorola */ - /* Motorola BitSURFR Plug & Play */ - { "MOT1105", 0 }, - /* Motorola TA210 Plug & Play */ - { "MOT1111", 0 }, - /* Motorola HMTA 200 (ISDN) Plug & Play */ - { "MOT1114", 0 }, - /* Motorola BitSURFR Plug & Play */ - { "MOT1115", 0 }, - /* Motorola Lifestyle 28.8 Internal */ - { "MOT1190", 0 }, - /* Motorola V.3400 Plug & Play */ - { "MOT1501", 0 }, - /* Motorola Lifestyle 28.8 V.34 Plug & Play */ - { "MOT1502", 0 }, - /* Motorola Power 28.8 V.34 Plug & Play */ - { "MOT1505", 0 }, - /* Motorola ModemSURFR External 28.8 Plug & Play */ - { "MOT1509", 0 }, - /* Motorola Premier 33.6 Desktop Plug & Play */ - { "MOT150A", 0 }, - /* Motorola VoiceSURFR 56K External PnP */ - { "MOT150F", 0 }, - /* Motorola ModemSURFR 56K External PnP */ - { "MOT1510", 0 }, - /* Motorola ModemSURFR 56K Internal PnP */ - { "MOT1550", 0 }, - /* Motorola ModemSURFR Internal 28.8 Plug & Play */ - { "MOT1560", 0 }, - /* Motorola Premier 33.6 Internal Plug & Play */ - { "MOT1580", 0 }, - /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ - { "MOT15B0", 0 }, - /* Motorola VoiceSURFR 56K Internal PnP */ - { "MOT15F0", 0 }, - /* Com 1 */ - /* Deskline K56 Phone System PnP */ - { "MVX00A1", 0 }, - /* PC Rider K56 Phone System PnP */ - { "MVX00F2", 0 }, - /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ - { "nEC8241", 0 }, - /* Pace 56 Voice Internal Plug & Play Modem */ - { "PMC2430", 0 }, - /* Generic */ - /* Generic standard PC COM port */ - { "PNP0500", 0 }, - /* Generic 16550A-compatible COM port */ - { "PNP0501", 0 }, - /* Compaq 14400 Modem */ - { "PNPC000", 0 }, - /* Compaq 2400/9600 Modem */ - { "PNPC001", 0 }, - /* Dial-Up Networking Serial Cable between 2 PCs */ - { "PNPC031", 0 }, - /* Dial-Up Networking Parallel Cable between 2 PCs */ - { "PNPC032", 0 }, - /* Standard 9600 bps Modem */ - { "PNPC100", 0 }, - /* Standard 14400 bps Modem */ - { "PNPC101", 0 }, - /* Standard 28800 bps Modem*/ - { "PNPC102", 0 }, - /* Standard Modem*/ - { "PNPC103", 0 }, - /* Standard 9600 bps Modem*/ - { "PNPC104", 0 }, - /* Standard 14400 bps Modem*/ - { "PNPC105", 0 }, - /* Standard 28800 bps Modem*/ - { "PNPC106", 0 }, - /* Standard Modem */ - { "PNPC107", 0 }, - /* Standard 9600 bps Modem */ - { "PNPC108", 0 }, - /* Standard 14400 bps Modem */ - { "PNPC109", 0 }, - /* Standard 28800 bps Modem */ - { "PNPC10A", 0 }, - /* Standard Modem */ - { "PNPC10B", 0 }, - /* Standard 9600 bps Modem */ - { "PNPC10C", 0 }, - /* Standard 14400 bps Modem */ - { "PNPC10D", 0 }, - /* Standard 28800 bps Modem */ - { "PNPC10E", 0 }, - /* Standard Modem */ - { "PNPC10F", 0 }, - /* Standard PCMCIA Card Modem */ - { "PNP2000", 0 }, - /* Rockwell */ - /* Modular Technology */ - /* Rockwell 33.6 DPF Internal PnP */ - /* Modular Technology 33.6 Internal PnP */ - { "ROK0030", 0 }, - /* Kortex International */ - /* KORTEX 14400 Externe PnP */ - { "ROK0100", 0 }, - /* Rockwell 28.8 */ - { "ROK4120", 0 }, - /* Viking Components, Inc */ - /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ - { "ROK4920", 0 }, - /* Rockwell */ - /* British Telecom */ - /* Modular Technology */ - /* Rockwell 33.6 DPF External PnP */ - /* BT Prologue 33.6 External PnP */ - /* Modular Technology 33.6 External PnP */ - { "RSS00A0", 0 }, - /* Viking 56K FAX INT */ - { "RSS0262", 0 }, - /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ - { "RSS0250", 0 }, - /* SupraExpress 28.8 Data/Fax PnP modem */ - { "SUP1310", 0 }, - /* SupraExpress 336i PnP Voice Modem */ - { "SUP1381", 0 }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1421", 0 }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1590", 0 }, - /* SupraExpress 336i Sp ASVD */ - { "SUP1620", 0 }, - /* SupraExpress 33.6 Data/Fax PnP modem */ - { "SUP1760", 0 }, - /* SupraExpress 56i Sp Intl */ - { "SUP2171", 0 }, - /* Phoebe Micro */ - /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ - { "TEX0011", 0 }, - /* Archtek America Corp. */ - /* Archtek SmartLink Modem 3334BT Plug & Play */ - { "UAC000F", 0 }, - /* 3Com Corp. */ - /* Gateway Telepath IIvi 33.6 */ - { "USR0000", 0 }, - /* U.S. Robotics Sporster 33.6K Fax INT PnP */ - { "USR0002", 0 }, - /* Sportster Vi 14.4 PnP FAX Voicemail */ - { "USR0004", 0 }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR0006", 0 }, - /* U.S. Robotics 33.6K Voice EXT PnP */ - { "USR0007", 0 }, - /* U.S. Robotics Courier V.Everything INT PnP */ - { "USR0009", 0 }, - /* U.S. Robotics 33.6K Voice INT PnP */ - { "USR2002", 0 }, - /* U.S. Robotics 56K Voice INT PnP */ - { "USR2070", 0 }, - /* U.S. Robotics 56K Voice EXT PnP */ - { "USR2080", 0 }, - /* U.S. Robotics 56K FAX INT */ - { "USR3031", 0 }, - /* U.S. Robotics 56K FAX INT */ - { "USR3050", 0 }, - /* U.S. Robotics 56K Voice INT PnP */ - { "USR3070", 0 }, - /* U.S. Robotics 56K Voice EXT PnP */ - { "USR3080", 0 }, - /* U.S. Robotics 56K Voice INT PnP */ - { "USR3090", 0 }, - /* U.S. Robotics 56K Message */ - { "USR9100", 0 }, - /* U.S. Robotics 56K FAX EXT PnP*/ - { "USR9160", 0 }, - /* U.S. Robotics 56K FAX INT PnP*/ - { "USR9170", 0 }, - /* U.S. Robotics 56K Voice EXT PnP*/ - { "USR9180", 0 }, - /* U.S. Robotics 56K Voice INT PnP*/ - { "USR9190", 0 }, - /* Wacom tablets */ - { "WACFXXX", 0 }, - /* Compaq touchscreen */ - { "FPI2002", 0 }, - /* Fujitsu Stylistic touchscreens */ - { "FUJ02B2", 0 }, - { "FUJ02B3", 0 }, - /* Fujitsu Stylistic LT touchscreens */ - { "FUJ02B4", 0 }, - /* Passive Fujitsu Stylistic touchscreens */ - { "FUJ02B6", 0 }, - { "FUJ02B7", 0 }, - { "FUJ02B8", 0 }, - { "FUJ02B9", 0 }, - { "FUJ02BC", 0 }, - /* Fujitsu Wacom Tablet PC device */ - { "FUJ02E5", 0 }, - /* Fujitsu P-series tablet PC device */ - { "FUJ02E6", 0 }, - /* Fujitsu Wacom 2FGT Tablet PC device */ - { "FUJ02E7", 0 }, - /* Fujitsu Wacom 1FGT Tablet PC device */ - { "FUJ02E9", 0 }, - /* - * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in - * disguise) - */ - { "LTS0001", 0 }, - /* Rockwell's (PORALiNK) 33600 INT PNP */ - { "WCI0003", 0 }, - /* Unknown PnP modems */ - { "PNPCXXX", UNKNOWN_DEV }, - /* More unknown PnP modems */ - { "PNPDXXX", UNKNOWN_DEV }, - { "", 0 } -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -static char *modem_names[] __devinitdata = { - "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", - "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL -}; - -static int __devinit check_name(char *name) -{ - char **tmp; - - for (tmp = modem_names; *tmp; tmp++) - if (strstr(name, *tmp)) - return 1; - - return 0; -} - -static int __devinit check_resources(struct pnp_dev *dev) -{ - resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; - int i; - - for (i = 0; i < ARRAY_SIZE(base); i++) { - if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) - return 1; - } - - return 0; -} - -/* - * Given a complete unknown PnP device, try to use some heuristics to - * detect modems. Currently use such heuristic set: - * - dev->name or dev->bus->name must contain "modem" substring; - * - device must have only one IO region (8 byte long) with base address - * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. - * - * Such detection looks very ugly, but can detect at least some of numerous - * PnP modems, alternatively we must hardcode all modems in pnp_devices[] - * table. - */ -static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) -{ - if (!(check_name(pnp_dev_name(dev)) || - (dev->card && check_name(dev->card->name)))) - return -ENODEV; - - if (check_resources(dev)) - return 0; - - return -ENODEV; -} - -static int __devinit -serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) -{ - struct uart_port port; - int ret, line, flags = dev_id->driver_data; - - if (flags & UNKNOWN_DEV) { - ret = serial_pnp_guess_board(dev, &flags); - if (ret < 0) - return ret; - } - - memset(&port, 0, sizeof(struct uart_port)); - if (pnp_irq_valid(dev, 0)) - port.irq = pnp_irq(dev, 0); - if (pnp_port_valid(dev, 0)) { - port.iobase = pnp_port_start(dev, 0); - port.iotype = UPIO_PORT; - } else if (pnp_mem_valid(dev, 0)) { - port.mapbase = pnp_mem_start(dev, 0); - port.iotype = UPIO_MEM; - port.flags = UPF_IOREMAP; - } else - return -ENODEV; - -#ifdef SERIAL_DEBUG_PNP - printk(KERN_DEBUG - "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", - port.iobase, port.mapbase, port.irq, port.iotype); -#endif - - port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) - port.flags |= UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &dev->dev; - - line = serial8250_register_port(&port); - if (line < 0) - return -ENODEV; - - pnp_set_drvdata(dev, (void *)((long)line + 1)); - return 0; -} - -static void __devexit serial_pnp_remove(struct pnp_dev *dev) -{ - long line = (long)pnp_get_drvdata(dev); - if (line) - serial8250_unregister_port(line - 1); -} - -#ifdef CONFIG_PM -static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state) -{ - long line = (long)pnp_get_drvdata(dev); - - if (!line) - return -ENODEV; - serial8250_suspend_port(line - 1); - return 0; -} - -static int serial_pnp_resume(struct pnp_dev *dev) -{ - long line = (long)pnp_get_drvdata(dev); - - if (!line) - return -ENODEV; - serial8250_resume_port(line - 1); - return 0; -} -#else -#define serial_pnp_suspend NULL -#define serial_pnp_resume NULL -#endif /* CONFIG_PM */ - -static struct pnp_driver serial_pnp_driver = { - .name = "serial", - .probe = serial_pnp_probe, - .remove = __devexit_p(serial_pnp_remove), - .suspend = serial_pnp_suspend, - .resume = serial_pnp_resume, - .id_table = pnp_dev_table, -}; - -static int __init serial8250_pnp_init(void) -{ - return pnp_register_driver(&serial_pnp_driver); -} - -static void __exit serial8250_pnp_exit(void) -{ - pnp_unregister_driver(&serial_pnp_driver); -} - -module_init(serial8250_pnp_init); -module_exit(serial8250_pnp_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver"); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig deleted file mode 100644 index c1df7676a73d..000000000000 --- a/drivers/serial/Kconfig +++ /dev/null @@ -1,1598 +0,0 @@ -# -# Serial device configuration -# - -menu "Serial drivers" - depends on HAS_IOMEM - -# -# The new 8250/16550 serial drivers -config SERIAL_8250 - tristate "8250/16550 and compatible serial support" - select SERIAL_CORE - ---help--- - This selects whether you want to include the driver for the standard - serial ports. The standard answer is Y. People who might say N - here are those that are setting up dedicated Ethernet WWW/FTP - servers, or users that have one of the various bus mice instead of a - serial mouse and don't intend to use their machine's standard serial - port for anything. (Note that the Cyclades and Stallion multi - serial port drivers do not need this driver built in for them to - work.) - - To compile this driver as a module, choose M here: the - module will be called 8250. - [WARNING: Do not compile this driver as a module if you are using - non-standard serial ports, since the configuration information will - be lost when the driver is unloaded. This limitation may be lifted - in the future.] - - BTW1: If you have a mouseman serial mouse which is not recognized by - the X window system, try running gpm first. - - BTW2: If you intend to use a software modem (also called Winmodem) - under Linux, forget it. These modems are crippled and require - proprietary drivers which are only available under Windows. - - Most people will say Y or M here, so that they can use serial mice, - modems and similar devices connecting to the standard serial ports. - -config SERIAL_8250_CONSOLE - bool "Console on 8250/16550 and compatible serial port" - depends on SERIAL_8250=y - select SERIAL_CORE_CONSOLE - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). This could be useful if some terminal or printer is connected - to that serial port. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyS1". (Try "man bootparam" or see the documentation of - your boot loader (grub or lilo or loadlin) about how to pass options - to the kernel at boot time.) - - If you don't have a VGA card installed and you say Y here, the - kernel will automatically use the first serial line, /dev/ttyS0, as - system console. - - You can set that using a kernel command line option such as - "console=uart8250,io,0x3f8,9600n8" - "console=uart8250,mmio,0xff5e0000,115200n8". - and it will switch to normal serial console when the corresponding - port is ready. - "earlycon=uart8250,io,0x3f8,9600n8" - "earlycon=uart8250,mmio,0xff5e0000,115200n8". - it will not only setup early console. - - If unsure, say N. - -config FIX_EARLYCON_MEM - bool - depends on X86 - default y - -config SERIAL_8250_GSC - tristate - depends on SERIAL_8250 && GSC - default SERIAL_8250 - -config SERIAL_8250_PCI - tristate "8250/16550 PCI device support" if EMBEDDED - depends on SERIAL_8250 && PCI - default SERIAL_8250 - help - This builds standard PCI serial support. You may be able to - disable this feature if you only need legacy serial support. - Saves about 9K. - -config SERIAL_8250_PNP - tristate "8250/16550 PNP device support" if EMBEDDED - depends on SERIAL_8250 && PNP - default SERIAL_8250 - help - This builds standard PNP serial support. You may be able to - disable this feature if you only need legacy serial support. - -config SERIAL_8250_HP300 - tristate - depends on SERIAL_8250 && HP300 - default SERIAL_8250 - -config SERIAL_8250_CS - tristate "8250/16550 PCMCIA device support" - depends on PCMCIA && SERIAL_8250 - ---help--- - Say Y here to enable support for 16-bit PCMCIA serial devices, - including serial port cards, modems, and the modem functions of - multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are - credit-card size devices often used with laptops.) - - To compile this driver as a module, choose M here: the - module will be called serial_cs. - - If unsure, say N. - -config SERIAL_8250_NR_UARTS - int "Maximum number of 8250/16550 serial ports" - depends on SERIAL_8250 - default "4" - help - Set this to the number of serial ports you want the driver - to support. This includes any ports discovered via ACPI or - PCI enumeration and any ports that may be added at run-time - via hot-plug, or any ISA multi-port serial cards. - -config SERIAL_8250_RUNTIME_UARTS - int "Number of 8250/16550 serial ports to register at runtime" - depends on SERIAL_8250 - range 0 SERIAL_8250_NR_UARTS - default "4" - help - Set this to the maximum number of serial ports you want - the kernel to register at boot time. This can be overridden - with the module parameter "nr_uarts", or boot-time parameter - 8250.nr_uarts - -config SERIAL_8250_EXTENDED - bool "Extended 8250/16550 serial driver options" - depends on SERIAL_8250 - help - If you wish to use any non-standard features of the standard "dumb" - driver, say Y here. This includes HUB6 support, shared serial - interrupts, special multiport support, support for more than the - four COM 1/2/3/4 boards, etc. - - Note that the answer to this question won't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about serial driver options. If unsure, say N. - -config SERIAL_8250_MANY_PORTS - bool "Support more than 4 legacy serial ports" - depends on SERIAL_8250_EXTENDED && !IA64 - help - Say Y here if you have dumb serial boards other than the four - standard COM 1/2/3/4 ports. This may happen if you have an AST - FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available - from ), or other custom - serial port hardware which acts similar to standard serial port - hardware. If you only use the standard COM 1/2/3/4 ports, you can - say N here to save some memory. You can also say Y if you have an - "intelligent" multiport card such as Cyclades, Digiboards, etc. - -# -# Multi-port serial cards -# - -config SERIAL_8250_FOURPORT - tristate "Support Fourport cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have an AST FourPort serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_fourport. - -config SERIAL_8250_ACCENT - tristate "Support Accent cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have an Accent Async serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_accent. - -config SERIAL_8250_BOCA - tristate "Support Boca cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have a Boca serial board. Please read the Boca - mini-HOWTO, available from - - To compile this driver as a module, choose M here: the module - will be called 8250_boca. - -config SERIAL_8250_EXAR_ST16C554 - tristate "Support Exar ST16C554/554D Quad UART" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - The Uplogix Envoy TU301 uses this Exar Quad UART. If you are - tinkering with your Envoy TU301, or have a machine with this UART, - say Y here. - - To compile this driver as a module, choose M here: the module - will be called 8250_exar_st16c554. - -config SERIAL_8250_HUB6 - tristate "Support Hub6 cards" - depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS - help - Say Y here if you have a HUB6 serial board. - - To compile this driver as a module, choose M here: the module - will be called 8250_hub6. - -config SERIAL_8250_SHARE_IRQ - bool "Support for sharing serial interrupts" - depends on SERIAL_8250_EXTENDED - help - Some serial boards have hardware support which allows multiple dumb - serial ports on the same board to share a single IRQ. To enable - support for this in the serial driver, say Y here. - -config SERIAL_8250_DETECT_IRQ - bool "Autodetect IRQ on standard ports (unsafe)" - depends on SERIAL_8250_EXTENDED - help - Say Y here if you want the kernel to try to guess which IRQ - to use for your serial port. - - This is considered unsafe; it is far better to configure the IRQ in - a boot script using the setserial command. - - If unsure, say N. - -config SERIAL_8250_RSA - bool "Support RSA serial ports" - depends on SERIAL_8250_EXTENDED - help - ::: To be written ::: - -config SERIAL_8250_MCA - tristate "Support 8250-type ports on MCA buses" - depends on SERIAL_8250 != n && MCA - help - Say Y here if you have a MCA serial ports. - - To compile this driver as a module, choose M here: the module - will be called 8250_mca. - -config SERIAL_8250_ACORN - tristate "Acorn expansion card serial port support" - depends on ARCH_ACORN && SERIAL_8250 - help - If you have an Atomwide Serial card or Serial Port card for an Acorn - system, say Y to this option. The driver can handle 1, 2, or 3 port - cards. If unsure, say N. - -config SERIAL_8250_RM9K - bool "Support for MIPS RM9xxx integrated serial port" - depends on SERIAL_8250 != n && SERIAL_RM9000 - select SERIAL_8250_SHARE_IRQ - help - Selecting this option will add support for the integrated serial - port hardware found on MIPS RM9122 and similar processors. - If unsure, say N. - -comment "Non-8250 serial port support" - -config SERIAL_AMBA_PL010 - tristate "ARM AMBA PL010 serial port support" - depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE) - select SERIAL_CORE - help - This selects the ARM(R) AMBA(R) PrimeCell PL010 UART. If you have - an Integrator/AP or Integrator/PP2 platform, or if you have a - Cirrus Logic EP93xx CPU, say Y or M here. - - If unsure, say N. - -config SERIAL_AMBA_PL010_CONSOLE - bool "Support for console on AMBA serial port" - depends on SERIAL_AMBA_PL010=y - select SERIAL_CORE_CONSOLE - ---help--- - Say Y here if you wish to use an AMBA PrimeCell UART as the system - console (the system console is the device which receives all kernel - messages and warnings and which allows logins in single user mode). - - Even if you say Y here, the currently visible framebuffer console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyAM0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_AMBA_PL011 - tristate "ARM AMBA PL011 serial port support" - depends on ARM_AMBA - select SERIAL_CORE - help - This selects the ARM(R) AMBA(R) PrimeCell PL011 UART. If you have - an Integrator/PP2, Integrator/CP or Versatile platform, say Y or M - here. - - If unsure, say N. - -config SERIAL_AMBA_PL011_CONSOLE - bool "Support for console on AMBA serial port" - depends on SERIAL_AMBA_PL011=y - select SERIAL_CORE_CONSOLE - ---help--- - Say Y here if you wish to use an AMBA PrimeCell UART as the system - console (the system console is the device which receives all kernel - messages and warnings and which allows logins in single user mode). - - Even if you say Y here, the currently visible framebuffer console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyAMA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_SB1250_DUART - tristate "BCM1xxx on-chip DUART serial support" - depends on SIBYTE_SB1xxx_SOC=y - select SERIAL_CORE - default y - ---help--- - Support for the asynchronous serial interface (DUART) included in - the BCM1250 and derived System-On-a-Chip (SOC) devices. Note that - the letter D in DUART stands for "dual", which is how the device - is implemented. Depending on the SOC configuration there may be - one or more DUARTs available of which all are handled. - - If unsure, say Y. To compile this driver as a module, choose M here: - the module will be called sb1250-duart. - -config SERIAL_SB1250_DUART_CONSOLE - bool "Support for console on a BCM1xxx DUART serial port" - depends on SERIAL_SB1250_DUART=y - select SERIAL_CORE_CONSOLE - default y - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). - - If unsure, say Y. - -config SERIAL_ATMEL - bool "AT91 / AT32 on-chip serial port support" - depends on (ARM && ARCH_AT91) || AVR32 - select SERIAL_CORE - help - This enables the driver for the on-chip UARTs of the Atmel - AT91 and AT32 processors. - -config SERIAL_ATMEL_CONSOLE - bool "Support for console on AT91 / AT32 serial port" - depends on SERIAL_ATMEL=y - select SERIAL_CORE_CONSOLE - help - Say Y here if you wish to use an on-chip UART on a Atmel - AT91 or AT32 processor as the system console (the system - console is the device which receives all kernel messages and - warnings and which allows logins in single user mode). - -config SERIAL_ATMEL_PDC - bool "Support DMA transfers on AT91 / AT32 serial port" - depends on SERIAL_ATMEL - default y - help - Say Y here if you wish to use the PDC to do DMA transfers to - and from the Atmel AT91 / AT32 serial port. In order to - actually use DMA transfers, make sure that the use_dma_tx - and use_dma_rx members in the atmel_uart_data struct is set - appropriately for each port. - - Note that break and error handling currently doesn't work - properly when DMA is enabled. Make sure that ports where - this matters don't use DMA. - -config SERIAL_ATMEL_TTYAT - bool "Install as device ttyATn instead of ttySn" - depends on SERIAL_ATMEL=y - help - Say Y here if you wish to have the internal AT91 / AT32 UARTs - appear as /dev/ttyATn (major 204, minor starting at 154) - instead of the normal /dev/ttySn (major 4, minor starting at - 64). This is necessary if you also want other UARTs, such as - external 8250/16C550 compatible UARTs. - The ttySn nodes are legally reserved for the 8250 serial driver - but are often misused by other serial drivers. - - To use this, you should create suitable ttyATn device nodes in - /dev/, and pass "console=ttyATn" to the kernel. - - Say Y if you have an external 8250/16C550 UART. If unsure, say N. - -config SERIAL_KS8695 - bool "Micrel KS8695 (Centaur) serial port support" - depends on ARCH_KS8695 - select SERIAL_CORE - help - This selects the Micrel Centaur KS8695 UART. Say Y here. - -config SERIAL_KS8695_CONSOLE - bool "Support for console on KS8695 (Centaur) serial port" - depends on SERIAL_KS8695=y - select SERIAL_CORE_CONSOLE - help - Say Y here if you wish to use a KS8695 (Centaur) UART as the - system console (the system console is the device which - receives all kernel messages and warnings and which allows - logins in single user mode). - -config SERIAL_CLPS711X - tristate "CLPS711X serial port support" - depends on ARM && ARCH_CLPS711X - select SERIAL_CORE - help - ::: To be written ::: - -config SERIAL_CLPS711X_CONSOLE - bool "Support for console on CLPS711X serial port" - depends on SERIAL_CLPS711X=y - select SERIAL_CORE_CONSOLE - help - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyCL1". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_SAMSUNG - tristate "Samsung SoC serial support" - depends on ARM && PLAT_SAMSUNG - select SERIAL_CORE - help - Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, - providing /dev/ttySAC0, 1 and 2 (note, some machines may not - provide all of these ports, depending on how the serial port - pins are configured. - -config SERIAL_SAMSUNG_UARTS_4 - bool - depends on ARM && PLAT_SAMSUNG - default y if CPU_S3C2443 - help - Internal node for the common case of 4 Samsung compatible UARTs - -config SERIAL_SAMSUNG_UARTS - int - depends on ARM && PLAT_SAMSUNG - default 2 if ARCH_S3C2400 - default 6 if ARCH_S5P6450 - default 4 if SERIAL_SAMSUNG_UARTS_4 - default 3 - help - Select the number of available UART ports for the Samsung S3C - serial driver - -config SERIAL_SAMSUNG_DEBUG - bool "Samsung SoC serial debug" - depends on SERIAL_SAMSUNG && DEBUG_LL - help - Add support for debugging the serial driver. Since this is - generally being used as a console, we use our own output - routines that go via the low-level debug printascii() - function. - -config SERIAL_SAMSUNG_CONSOLE - bool "Support for console on Samsung SoC serial port" - depends on SERIAL_SAMSUNG=y - select SERIAL_CORE_CONSOLE - help - Allow selection of the S3C24XX on-board serial ports for use as - an virtual console. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttySACx". (Try "man bootparam" or see the documentation of - your boot loader about how to pass options to the kernel at - boot time.) - -config SERIAL_S3C2400 - tristate "Samsung S3C2410 Serial port support" - depends on ARM && SERIAL_SAMSUNG && CPU_S3C2400 - default y if CPU_S3C2400 - help - Serial port support for the Samsung S3C2400 SoC - -config SERIAL_S3C2410 - tristate "Samsung S3C2410 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S3C2410 - default y if CPU_S3C2410 - help - Serial port support for the Samsung S3C2410 SoC - -config SERIAL_S3C2412 - tristate "Samsung S3C2412/S3C2413 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S3C2412 - default y if CPU_S3C2412 - help - Serial port support for the Samsung S3C2412 and S3C2413 SoC - -config SERIAL_S3C2440 - tristate "Samsung S3C2440/S3C2442/S3C2416 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442 || CPU_S3C2416) - default y if CPU_S3C2440 - default y if CPU_S3C2442 - select SERIAL_SAMSUNG_UARTS_4 if CPU_S3C2416 - help - Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC - -config SERIAL_S3C24A0 - tristate "Samsung S3C24A0 Serial port support" - depends on SERIAL_SAMSUNG && CPU_S3C24A0 - default y if CPU_S3C24A0 - help - Serial port support for the Samsung S3C24A0 SoC - -config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410/S5P6440/S5P6450/S5PC100 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5P6450 || CPU_S5PC100) - select SERIAL_SAMSUNG_UARTS_4 - default y - help - Serial port support for the Samsung S3C6400, S3C6410, S5P6440, S5P6450 - and S5PC100 SoCs - -config SERIAL_S5PV210 - tristate "Samsung S5PV210 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310) - select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310) - default y - help - Serial port support for Samsung's S5P Family of SoC's - - -config SERIAL_MAX3100 - tristate "MAX3100 support" - depends on SPI - select SERIAL_CORE - help - MAX3100 chip support - -config SERIAL_MAX3107 - tristate "MAX3107 support" - depends on SPI - select SERIAL_CORE - help - MAX3107 chip support - -config SERIAL_MAX3107_AAVA - tristate "MAX3107 AAVA platform support" - depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB - select SERIAL_CORE - help - Support for the MAX3107 chip configuration found on the AAVA - platform. Includes the extra initialisation and GPIO support - neded for this device. - -config SERIAL_DZ - bool "DECstation DZ serial driver" - depends on MACH_DECSTATION && 32BIT - select SERIAL_CORE - default y - ---help--- - DZ11-family serial controllers for DECstations and VAXstations, - including the DC7085, M7814, and M7819. - -config SERIAL_DZ_CONSOLE - bool "Support console on DECstation DZ serial driver" - depends on SERIAL_DZ=y - select SERIAL_CORE_CONSOLE - default y - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). - - Note that the firmware uses ttyS3 as the serial console on - DECstations that use this driver. - - If unsure, say Y. - -config SERIAL_ZS - tristate "DECstation Z85C30 serial support" - depends on MACH_DECSTATION - select SERIAL_CORE - default y - ---help--- - Support for the Zilog 85C350 serial communications controller used - for serial ports in newer DECstation systems. These include the - DECsystem 5900 and all models of the DECstation and DECsystem 5000 - systems except from model 200. - - If unsure, say Y. To compile this driver as a module, choose M here: - the module will be called zs. - -config SERIAL_ZS_CONSOLE - bool "Support for console on a DECstation Z85C30 serial port" - depends on SERIAL_ZS=y - select SERIAL_CORE_CONSOLE - default y - ---help--- - If you say Y here, it will be possible to use a serial port as the - system console (the system console is the device which receives all - kernel messages and warnings and which allows logins in single user - mode). - - Note that the firmware uses ttyS1 as the serial console on the - Maxine and ttyS3 on the others using this driver. - - If unsure, say Y. - -config SERIAL_21285 - tristate "DC21285 serial port support" - depends on ARM && FOOTBRIDGE - select SERIAL_CORE - help - If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ - PCI bridge you can enable its onboard serial port by enabling this - option. - -config SERIAL_21285_CONSOLE - bool "Console on DC21285 serial port" - depends on SERIAL_21285=y - select SERIAL_CORE_CONSOLE - help - If you have enabled the serial port on the 21285 footbridge you can - make it the console by answering Y to this option. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyFB". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_MPSC - bool "Marvell MPSC serial port support" - depends on PPC32 && MV64X60 - select SERIAL_CORE - help - Say Y here if you want to use the Marvell MPSC serial controller. - -config SERIAL_MPSC_CONSOLE - bool "Support for console on Marvell MPSC serial port" - depends on SERIAL_MPSC - select SERIAL_CORE_CONSOLE - help - Say Y here if you want to support a serial console on a Marvell MPSC. - -config SERIAL_PXA - bool "PXA serial port support" - depends on ARCH_PXA || ARCH_MMP - select SERIAL_CORE - help - If you have a machine based on an Intel XScale PXA2xx CPU you - can enable its onboard serial ports by enabling this option. - -config SERIAL_PXA_CONSOLE - bool "Console on PXA serial port" - depends on SERIAL_PXA - select SERIAL_CORE_CONSOLE - help - If you have enabled the serial port on the Intel XScale PXA - CPU you can make it the console by answering Y to this option. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttySA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_SA1100 - bool "SA1100 serial port support" - depends on ARM && ARCH_SA1100 - select SERIAL_CORE - help - If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you - can enable its onboard serial port by enabling this option. - Please read for further - info. - -config SERIAL_SA1100_CONSOLE - bool "Console on SA1100 serial port" - depends on SERIAL_SA1100 - select SERIAL_CORE_CONSOLE - help - If you have enabled the serial port on the SA1100/SA1110 StrongARM - CPU you can make it the console by answering Y to this option. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttySA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_MRST_MAX3110 - tristate "SPI UART driver for Max3110" - depends on SPI_DW_PCI - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - help - This is the UART protocol driver for the MAX3110 device on - the Intel Moorestown platform. On other systems use the max3100 - driver. - -config SERIAL_MFD_HSU - tristate "Medfield High Speed UART support" - depends on PCI - select SERIAL_CORE - -config SERIAL_MFD_HSU_CONSOLE - boolean "Medfile HSU serial console support" - depends on SERIAL_MFD_HSU=y - select SERIAL_CORE_CONSOLE - -config SERIAL_BFIN - tristate "Blackfin serial port support" - depends on BLACKFIN - select SERIAL_CORE - select SERIAL_BFIN_UART0 if (BF531 || BF532 || BF533 || BF561) - help - Add support for the built-in UARTs on the Blackfin. - - To compile this driver as a module, choose M here: the - module will be called bfin_5xx. - -config SERIAL_BFIN_CONSOLE - bool "Console on Blackfin serial port" - depends on SERIAL_BFIN=y - select SERIAL_CORE_CONSOLE - -choice - prompt "UART Mode" - depends on SERIAL_BFIN - default SERIAL_BFIN_DMA - help - This driver supports the built-in serial ports of the Blackfin family - of CPUs - -config SERIAL_BFIN_DMA - bool "DMA mode" - depends on !DMA_UNCACHED_NONE && KGDB_SERIAL_CONSOLE=n - help - This driver works under DMA mode. If this option is selected, the - blackfin simple dma driver is also enabled. - -config SERIAL_BFIN_PIO - bool "PIO mode" - help - This driver works under PIO mode. - -endchoice - -config SERIAL_BFIN_UART0 - bool "Enable UART0" - depends on SERIAL_BFIN - help - Enable UART0 - -config BFIN_UART0_CTSRTS - bool "Enable UART0 hardware flow control" - depends on SERIAL_BFIN_UART0 - help - Enable hardware flow control in the driver. - -config SERIAL_BFIN_UART1 - bool "Enable UART1" - depends on SERIAL_BFIN && (!BF531 && !BF532 && !BF533 && !BF561) - help - Enable UART1 - -config BFIN_UART1_CTSRTS - bool "Enable UART1 hardware flow control" - depends on SERIAL_BFIN_UART1 - help - Enable hardware flow control in the driver. - -config SERIAL_BFIN_UART2 - bool "Enable UART2" - depends on SERIAL_BFIN && (BF54x || BF538 || BF539) - help - Enable UART2 - -config BFIN_UART2_CTSRTS - bool "Enable UART2 hardware flow control" - depends on SERIAL_BFIN_UART2 - help - Enable hardware flow control in the driver. - -config SERIAL_BFIN_UART3 - bool "Enable UART3" - depends on SERIAL_BFIN && (BF54x) - help - Enable UART3 - -config BFIN_UART3_CTSRTS - bool "Enable UART3 hardware flow control" - depends on SERIAL_BFIN_UART3 - help - Enable hardware flow control in the driver. - -config SERIAL_IMX - bool "IMX serial port support" - depends on ARM && (ARCH_IMX || ARCH_MXC) - select SERIAL_CORE - select RATIONAL - help - If you have a machine based on a Motorola IMX CPU you - can enable its onboard serial port by enabling this option. - -config SERIAL_IMX_CONSOLE - bool "Console on IMX serial port" - depends on SERIAL_IMX - select SERIAL_CORE_CONSOLE - help - If you have enabled the serial port on the Motorola IMX - CPU you can make it the console by answering Y to this option. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttySA0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_UARTLITE - tristate "Xilinx uartlite serial port support" - depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE - select SERIAL_CORE - help - Say Y here if you want to use the Xilinx uartlite serial controller. - - To compile this driver as a module, choose M here: the - module will be called uartlite. - -config SERIAL_UARTLITE_CONSOLE - bool "Support for console on Xilinx uartlite serial port" - depends on SERIAL_UARTLITE=y - select SERIAL_CORE_CONSOLE - help - Say Y here if you wish to use a Xilinx uartlite as the system - console (the system console is the device which receives all kernel - messages and warnings and which allows logins in single user mode). - -config SERIAL_SUNCORE - bool - depends on SPARC - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - default y - -config SERIAL_SUNZILOG - tristate "Sun Zilog8530 serial support" - depends on SPARC - help - This driver supports the Zilog8530 serial ports found on many Sparc - systems. Say Y or M if you want to be able to these serial ports. - -config SERIAL_SUNZILOG_CONSOLE - bool "Console on Sun Zilog8530 serial port" - depends on SERIAL_SUNZILOG=y - help - If you would like to be able to use the Zilog8530 serial port - on your Sparc system as the console, you can do so by answering - Y to this option. - -config SERIAL_SUNSU - tristate "Sun SU serial support" - depends on SPARC && PCI - help - This driver supports the 8250 serial ports that run the keyboard and - mouse on (PCI) UltraSPARC systems. Say Y or M if you want to be able - to these serial ports. - -config SERIAL_SUNSU_CONSOLE - bool "Console on Sun SU serial port" - depends on SERIAL_SUNSU=y - help - If you would like to be able to use the SU serial port - on your Sparc system as the console, you can do so by answering - Y to this option. - -config SERIAL_MUX - tristate "Serial MUX support" - depends on GSC - select SERIAL_CORE - default y - ---help--- - Saying Y here will enable the hardware MUX serial driver for - the Nova, K class systems and D class with a 'remote control card'. - The hardware MUX is not 8250/16550 compatible therefore the - /dev/ttyB0 device is shared between the Serial MUX and the PDC - software console. The following steps need to be completed to use - the Serial MUX: - - 1. create the device entry (mknod /dev/ttyB0 c 11 0) - 2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0 - 3. Add device ttyB0 to /etc/securetty (if you want to log on as - root on this console.) - 4. Change the kernel command console parameter to: console=ttyB0 - -config SERIAL_MUX_CONSOLE - bool "Support for console on serial MUX" - depends on SERIAL_MUX=y - select SERIAL_CORE_CONSOLE - default y - -config PDC_CONSOLE - bool "PDC software console support" - depends on PARISC && !SERIAL_MUX && VT - default n - help - Saying Y here will enable the software based PDC console to be - used as the system console. This is useful for machines in - which the hardware based console has not been written yet. The - following steps must be competed to use the PDC console: - - 1. create the device entry (mknod /dev/ttyB0 c 11 0) - 2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0 - 3. Add device ttyB0 to /etc/securetty (if you want to log on as - root on this console.) - 4. Change the kernel command console parameter to: console=ttyB0 - -config SERIAL_SUNSAB - tristate "Sun Siemens SAB82532 serial support" - depends on SPARC && PCI - help - This driver supports the Siemens SAB82532 DUSCC serial ports on newer - (PCI) UltraSPARC systems. Say Y or M if you want to be able to these - serial ports. - -config SERIAL_SUNSAB_CONSOLE - bool "Console on Sun Siemens SAB82532 serial port" - depends on SERIAL_SUNSAB=y - help - If you would like to be able to use the SAB82532 serial port - on your Sparc system as the console, you can do so by answering - Y to this option. - -config SERIAL_SUNHV - bool "Sun4v Hypervisor Console support" - depends on SPARC64 - help - This driver supports the console device found on SUN4V Sparc - systems. Say Y if you want to be able to use this device. - -config SERIAL_IP22_ZILOG - tristate "SGI Zilog8530 serial support" - depends on SGI_HAS_ZILOG - select SERIAL_CORE - help - This driver supports the Zilog8530 serial ports found on SGI - systems. Say Y or M if you want to be able to these serial ports. - -config SERIAL_IP22_ZILOG_CONSOLE - bool "Console on SGI Zilog8530 serial port" - depends on SERIAL_IP22_ZILOG=y - select SERIAL_CORE_CONSOLE - -config SERIAL_SH_SCI - tristate "SuperH SCI(F) serial port support" - depends on HAVE_CLK && (SUPERH || H8300 || ARCH_SHMOBILE) - select SERIAL_CORE - -config SERIAL_SH_SCI_NR_UARTS - int "Maximum number of SCI(F) serial ports" - depends on SERIAL_SH_SCI - default "2" - -config SERIAL_SH_SCI_CONSOLE - bool "Support for console on SuperH SCI(F)" - depends on SERIAL_SH_SCI=y - select SERIAL_CORE_CONSOLE - -config SERIAL_SH_SCI_DMA - bool "DMA support" - depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL - -config SERIAL_PNX8XXX - bool "Enable PNX8XXX SoCs' UART Support" - depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) - select SERIAL_CORE - help - If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 - and you want to use serial ports, say Y. Otherwise, say N. - -config SERIAL_PNX8XXX_CONSOLE - bool "Enable PNX8XX0 serial console" - depends on SERIAL_PNX8XXX - select SERIAL_CORE_CONSOLE - help - If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 - and you want to use serial console, say Y. Otherwise, say N. - -config SERIAL_CORE - tristate - -config SERIAL_CORE_CONSOLE - bool - -config CONSOLE_POLL - bool - -config SERIAL_68328 - bool "68328 serial support" - depends on M68328 || M68EZ328 || M68VZ328 - help - This driver supports the built-in serial port of the Motorola 68328 - (standard, EZ and VZ varieties). - -config SERIAL_68328_RTS_CTS - bool "Support RTS/CTS on 68328 serial port" - depends on SERIAL_68328 - -config SERIAL_MCF - bool "Coldfire serial support" - depends on COLDFIRE - select SERIAL_CORE - help - This serial driver supports the Freescale Coldfire serial ports. - -config SERIAL_MCF_BAUDRATE - int "Default baudrate for Coldfire serial ports" - depends on SERIAL_MCF - default 19200 - help - This setting lets you define what the default baudrate is for the - ColdFire serial ports. The usual default varies from board to board, - and this setting is a way of catering for that. - -config SERIAL_MCF_CONSOLE - bool "Coldfire serial console support" - depends on SERIAL_MCF - select SERIAL_CORE_CONSOLE - help - Enable a ColdFire internal serial port to be the system console. - -config SERIAL_68360_SMC - bool "68360 SMC uart support" - depends on M68360 - help - This driver supports the SMC serial ports of the Motorola 68360 CPU. - -config SERIAL_68360_SCC - bool "68360 SCC uart support" - depends on M68360 - help - This driver supports the SCC serial ports of the Motorola 68360 CPU. - -config SERIAL_68360 - bool - depends on SERIAL_68360_SMC || SERIAL_68360_SCC - default y - -config SERIAL_PMACZILOG - tristate "Mac or PowerMac z85c30 ESCC support" - depends on (M68K && MAC) || (PPC_OF && PPC_PMAC) - select SERIAL_CORE - help - This driver supports the Zilog z85C30 serial ports found on - (Power)Mac machines. - Say Y or M if you want to be able to these serial ports. - -config SERIAL_PMACZILOG_TTYS - bool "Use ttySn device nodes for Zilog z85c30" - depends on SERIAL_PMACZILOG - help - The pmac_zilog driver for the z85C30 chip on many powermacs - historically used the device numbers for /dev/ttySn. The - 8250 serial port driver also uses these numbers, which means - the two drivers being unable to coexist; you could not use - both z85C30 and 8250 type ports at the same time. - - If this option is not selected, the pmac_zilog driver will - use the device numbers allocated for /dev/ttyPZn. This allows - the pmac_zilog and 8250 drivers to co-exist, but may cause - existing userspace setups to break. Programs that need to - access the built-in serial ports on powermacs will need to - be reconfigured to use /dev/ttyPZn instead of /dev/ttySn. - - If you enable this option, any z85c30 ports in the system will - be registered as ttyS0 onwards as in the past, and you will be - unable to use the 8250 module for PCMCIA or other 16C550-style - UARTs. - - Say N unless you need the z85c30 ports on your (Power)Mac - to appear as /dev/ttySn. - -config SERIAL_PMACZILOG_CONSOLE - bool "Console on Mac or PowerMac z85c30 serial port" - depends on SERIAL_PMACZILOG=y - select SERIAL_CORE_CONSOLE - help - If you would like to be able to use the z85c30 serial port - on your (Power)Mac as the console, you can do so by answering - Y to this option. - -config SERIAL_LH7A40X - tristate "Sharp LH7A40X embedded UART support" - depends on ARM && ARCH_LH7A40X - select SERIAL_CORE - help - This enables support for the three on-board UARTs of the - Sharp LH7A40X series CPUs. Choose Y or M. - -config SERIAL_LH7A40X_CONSOLE - bool "Support for console on Sharp LH7A40X serial port" - depends on SERIAL_LH7A40X=y - select SERIAL_CORE_CONSOLE - help - Say Y here if you wish to use one of the serial ports as the - system console--the system console is the device which - receives all kernel messages and warnings and which allows - logins in single user mode. - - Even if you say Y here, the currently visible framebuffer console - (/dev/tty0) will still be used as the default system console, but - you can alter that using a kernel command line, for example - "console=ttyAM1". - -config SERIAL_CPM - tristate "CPM SCC/SMC serial port support" - depends on CPM2 || 8xx - select SERIAL_CORE - help - This driver supports the SCC and SMC serial ports on Motorola - embedded PowerPC that contain a CPM1 (8xx) or CPM2 (8xxx) - -config SERIAL_CPM_CONSOLE - bool "Support for console on CPM SCC/SMC serial port" - depends on SERIAL_CPM=y - select SERIAL_CORE_CONSOLE - help - Say Y here if you wish to use a SCC or SMC CPM UART as the system - console (the system console is the device which receives all kernel - messages and warnings and which allows logins in single user mode). - - Even if you say Y here, the currently visible framebuffer console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyCPM0". (Try "man bootparam" or see the documentation of - your boot loader (lilo or loadlin) about how to pass options to the - kernel at boot time.) - -config SERIAL_SGI_L1_CONSOLE - bool "SGI Altix L1 serial console support" - depends on IA64_GENERIC || IA64_SGI_SN2 - select SERIAL_CORE - select SERIAL_CORE_CONSOLE - help - If you have an SGI Altix and you would like to use the system - controller serial port as your console (you want this!), - say Y. Otherwise, say N. - -config SERIAL_MPC52xx - tristate "Freescale MPC52xx/MPC512x family PSC serial support" - depends on PPC_MPC52xx || PPC_MPC512x - select SERIAL_CORE - help - This driver supports MPC52xx and MPC512x PSC serial ports. If you would - like to use them, you must answer Y or M to this option. Note that - for use as console, it must be included in kernel and not as a - module. - -config SERIAL_MPC52xx_CONSOLE - bool "Console on a Freescale MPC52xx/MPC512x family PSC serial port" - depends on SERIAL_MPC52xx=y - select SERIAL_CORE_CONSOLE - help - Select this options if you'd like to use one of the PSC serial port - of the Freescale MPC52xx family as a console. - -config SERIAL_MPC52xx_CONSOLE_BAUD - int "Freescale MPC52xx/MPC512x family PSC serial port baud" - depends on SERIAL_MPC52xx_CONSOLE=y - default "9600" - help - Select the MPC52xx console baud rate. - This value is only used if the bootloader doesn't pass in the - console baudrate - -config SERIAL_ICOM - tristate "IBM Multiport Serial Adapter" - depends on PCI && (PPC_ISERIES || PPC_PSERIES) - select SERIAL_CORE - select FW_LOADER - help - This driver is for a family of multiport serial adapters - including 2 port RVX, 2 port internal modem, 4 port internal - modem and a split 1 port RVX and 1 port internal modem. - - This driver can also be built as a module. If so, the module - will be called icom. - -config SERIAL_M32R_SIO - bool "M32R SIO I/F" - depends on M32R - default y - select SERIAL_CORE - help - Say Y here if you want to use the M32R serial controller. - -config SERIAL_M32R_SIO_CONSOLE - bool "use SIO console" - depends on SERIAL_M32R_SIO=y - select SERIAL_CORE_CONSOLE - help - Say Y here if you want to support a serial console. - - If you use an M3T-M32700UT or an OPSPUT platform, - please say also y for SERIAL_M32R_PLDSIO. - -config SERIAL_M32R_PLDSIO - bool "M32R SIO I/F on a PLD" - depends on SERIAL_M32R_SIO=y && (PLAT_OPSPUT || PLAT_USRV || PLAT_M32700UT) - default n - help - Say Y here if you want to use the M32R serial controller - on a PLD (Programmable Logic Device). - - If you use an M3T-M32700UT or an OPSPUT platform, - please say Y. - -config SERIAL_TXX9 - bool "TMPTX39XX/49XX SIO support" - depends on HAS_TXX9_SERIAL - select SERIAL_CORE - default y - -config HAS_TXX9_SERIAL - bool - -config SERIAL_TXX9_NR_UARTS - int "Maximum number of TMPTX39XX/49XX SIO ports" - depends on SERIAL_TXX9 - default "6" - -config SERIAL_TXX9_CONSOLE - bool "TMPTX39XX/49XX SIO Console support" - depends on SERIAL_TXX9=y - select SERIAL_CORE_CONSOLE - -config SERIAL_TXX9_STDSERIAL - bool "TX39XX/49XX SIO act as standard serial" - depends on !SERIAL_8250 && SERIAL_TXX9 - -config SERIAL_VR41XX - tristate "NEC VR4100 series Serial Interface Unit support" - depends on CPU_VR41XX - select SERIAL_CORE - help - If you have a NEC VR4100 series processor and you want to use - Serial Interface Unit(SIU) or Debug Serial Interface Unit(DSIU) - (not include VR4111/VR4121 DSIU), say Y. Otherwise, say N. - -config SERIAL_VR41XX_CONSOLE - bool "Enable NEC VR4100 series Serial Interface Unit console" - depends on SERIAL_VR41XX=y - select SERIAL_CORE_CONSOLE - help - If you have a NEC VR4100 series processor and you want to use - a console on a serial port, say Y. Otherwise, say N. - -config SERIAL_JSM - tristate "Digi International NEO PCI Support" - depends on PCI - select SERIAL_CORE - help - This is a driver for Digi International's Neo series - of cards which provide multiple serial ports. You would need - something like this to connect more than two modems to your Linux - box, for instance in order to become a dial-in server. This driver - supports PCI boards only. - - If you have a card like this, say Y here, otherwise say N. - - To compile this driver as a module, choose M here: the - module will be called jsm. - -config SERIAL_SGI_IOC4 - tristate "SGI IOC4 controller serial support" - depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC4 - select SERIAL_CORE - help - If you have an SGI Altix with an IOC4 based Base IO card - and wish to use the serial ports on this card, say Y. - Otherwise, say N. - -config SERIAL_SGI_IOC3 - tristate "SGI Altix IOC3 serial support" - depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC3 - select SERIAL_CORE - help - If you have an SGI Altix with an IOC3 serial card, - say Y or M. Otherwise, say N. - -config SERIAL_MSM - bool "MSM on-chip serial port support" - depends on ARM && ARCH_MSM - select SERIAL_CORE - -config SERIAL_MSM_CONSOLE - bool "MSM serial console support" - depends on SERIAL_MSM=y - select SERIAL_CORE_CONSOLE - -config SERIAL_VT8500 - bool "VIA VT8500 on-chip serial port support" - depends on ARM && ARCH_VT8500 - select SERIAL_CORE - -config SERIAL_VT8500_CONSOLE - bool "VIA VT8500 serial console support" - depends on SERIAL_VT8500=y - select SERIAL_CORE_CONSOLE - -config SERIAL_NETX - tristate "NetX serial port support" - depends on ARM && ARCH_NETX - select SERIAL_CORE - help - If you have a machine based on a Hilscher NetX SoC you - can enable its onboard serial port by enabling this option. - - To compile this driver as a module, choose M here: the - module will be called netx-serial. - -config SERIAL_NETX_CONSOLE - bool "Console on NetX serial port" - depends on SERIAL_NETX=y - select SERIAL_CORE_CONSOLE - help - If you have enabled the serial port on the Hilscher NetX SoC - you can make it the console by answering Y to this option. - -config SERIAL_OF_PLATFORM - tristate "Serial port on Open Firmware platform bus" - depends on OF - depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL - help - If you have a PowerPC based system that has serial ports - on a platform specific bus, you should enable this option. - Currently, only 8250 compatible ports are supported, but - others can easily be added. - -config SERIAL_OMAP - tristate "OMAP serial port support" - depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 - select SERIAL_CORE - help - If you have a machine based on an Texas Instruments OMAP CPU you - can enable its onboard serial ports by enabling this option. - - By enabling this option you take advantage of dma feature available - with the omap-serial driver. DMA support can be enabled from platform - data. - -config SERIAL_OMAP_CONSOLE - bool "Console on OMAP serial port" - depends on SERIAL_OMAP - select SERIAL_CORE_CONSOLE - help - Select this option if you would like to use omap serial port as - console. - - Even if you say Y here, the currently visible virtual console - (/dev/tty0) will still be used as the system console by default, but - you can alter that using a kernel command line option such as - "console=ttyOx". (Try "man bootparam" or see the documentation of - your boot loader about how to pass options to the kernel at - boot time.) - -config SERIAL_OF_PLATFORM_NWPSERIAL - tristate "NWP serial port driver" - depends on PPC_OF && PPC_DCR - select SERIAL_OF_PLATFORM - select SERIAL_CORE_CONSOLE - select SERIAL_CORE - help - This driver supports the cell network processor nwp serial - device. - -config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE - bool "Console on NWP serial port" - depends on SERIAL_OF_PLATFORM_NWPSERIAL=y - select SERIAL_CORE_CONSOLE - help - Support for Console on the NWP serial ports. - -config SERIAL_QE - tristate "Freescale QUICC Engine serial port support" - depends on QUICC_ENGINE - select SERIAL_CORE - select FW_LOADER - default n - help - This driver supports the QE serial ports on Freescale embedded - PowerPC that contain a QUICC Engine. - -config SERIAL_SC26XX - tristate "SC2681/SC2692 serial port support" - depends on SNI_RM - select SERIAL_CORE - help - This is a driver for the onboard serial ports of - older RM400 machines. - -config SERIAL_SC26XX_CONSOLE - bool "Console on SC2681/SC2692 serial port" - depends on SERIAL_SC26XX - select SERIAL_CORE_CONSOLE - help - Support for Console on SC2681/SC2692 serial ports. - -config SERIAL_BFIN_SPORT - tristate "Blackfin SPORT emulate UART" - depends on BLACKFIN - select SERIAL_CORE - help - Enable SPORT emulate UART on Blackfin series. - - To compile this driver as a module, choose M here: the - module will be called bfin_sport_uart. - -config SERIAL_BFIN_SPORT_CONSOLE - bool "Console on Blackfin sport emulated uart" - depends on SERIAL_BFIN_SPORT=y - select SERIAL_CORE_CONSOLE - -config SERIAL_BFIN_SPORT0_UART - bool "Enable UART over SPORT0" - depends on SERIAL_BFIN_SPORT && !(BF542 || BF544) - help - Enable UART over SPORT0 - -config SERIAL_BFIN_SPORT0_UART_CTSRTS - bool "Enable UART over SPORT0 hardware flow control" - depends on SERIAL_BFIN_SPORT0_UART - help - Enable hardware flow control in the driver. - -config SERIAL_BFIN_SPORT1_UART - bool "Enable UART over SPORT1" - depends on SERIAL_BFIN_SPORT - help - Enable UART over SPORT1 - -config SERIAL_BFIN_SPORT1_UART_CTSRTS - bool "Enable UART over SPORT1 hardware flow control" - depends on SERIAL_BFIN_SPORT1_UART - help - Enable hardware flow control in the driver. - -config SERIAL_BFIN_SPORT2_UART - bool "Enable UART over SPORT2" - depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) - help - Enable UART over SPORT2 - -config SERIAL_BFIN_SPORT2_UART_CTSRTS - bool "Enable UART over SPORT2 hardware flow control" - depends on SERIAL_BFIN_SPORT2_UART - help - Enable hardware flow control in the driver. - -config SERIAL_BFIN_SPORT3_UART - bool "Enable UART over SPORT3" - depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) - help - Enable UART over SPORT3 - -config SERIAL_BFIN_SPORT3_UART_CTSRTS - bool "Enable UART over SPORT3 hardware flow control" - depends on SERIAL_BFIN_SPORT3_UART - help - Enable hardware flow control in the driver. - -config SERIAL_TIMBERDALE - tristate "Support for timberdale UART" - select SERIAL_CORE - ---help--- - Add support for UART controller on timberdale. - -config SERIAL_BCM63XX - tristate "bcm63xx serial port support" - select SERIAL_CORE - depends on BCM63XX - help - If you have a bcm63xx CPU, you can enable its onboard - serial port by enabling this options. - - To compile this driver as a module, choose M here: the - module will be called bcm963xx_uart. - -config SERIAL_BCM63XX_CONSOLE - bool "Console on bcm63xx serial port" - depends on SERIAL_BCM63XX=y - select SERIAL_CORE_CONSOLE - help - If you have enabled the serial port on the bcm63xx CPU - you can make it the console by answering Y to this option. - -config SERIAL_GRLIB_GAISLER_APBUART - tristate "GRLIB APBUART serial support" - depends on OF - ---help--- - Add support for the GRLIB APBUART serial port. - -config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE - bool "Console on GRLIB APBUART serial port" - depends on SERIAL_GRLIB_GAISLER_APBUART=y - select SERIAL_CORE_CONSOLE - help - Support for running a console on the GRLIB APBUART - -config SERIAL_ALTERA_JTAGUART - tristate "Altera JTAG UART support" - select SERIAL_CORE - help - This driver supports the Altera JTAG UART port. - -config SERIAL_ALTERA_JTAGUART_CONSOLE - bool "Altera JTAG UART console support" - depends on SERIAL_ALTERA_JTAGUART=y - select SERIAL_CORE_CONSOLE - help - Enable a Altera JTAG UART port to be the system console. - -config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS - bool "Bypass output when no connection" - depends on SERIAL_ALTERA_JTAGUART_CONSOLE - select SERIAL_CORE_CONSOLE - help - Bypass console output and keep going even if there is no - JTAG terminal connection with the host. - -config SERIAL_ALTERA_UART - tristate "Altera UART support" - select SERIAL_CORE - help - This driver supports the Altera softcore UART port. - -config SERIAL_ALTERA_UART_MAXPORTS - int "Maximum number of Altera UART ports" - depends on SERIAL_ALTERA_UART - default 4 - help - This setting lets you define the maximum number of the Altera - UART ports. The usual default varies from board to board, and - this setting is a way of catering for that. - -config SERIAL_ALTERA_UART_BAUDRATE - int "Default baudrate for Altera UART ports" - depends on SERIAL_ALTERA_UART - default 115200 - help - This setting lets you define what the default baudrate is for the - Altera UART ports. The usual default varies from board to board, - and this setting is a way of catering for that. - -config SERIAL_ALTERA_UART_CONSOLE - bool "Altera UART console support" - depends on SERIAL_ALTERA_UART=y - select SERIAL_CORE_CONSOLE - help - Enable a Altera UART port to be the system console. - -config SERIAL_IFX6X60 - tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)" - depends on GPIOLIB && SPI && EXPERIMENTAL - help - Support for the IFX6x60 modem devices on Intel MID platforms. - -config SERIAL_PCH_UART - tristate "Intel EG20T PCH UART" - depends on PCI && DMADEVICES - select SERIAL_CORE - select PCH_DMA - help - This driver is for PCH(Platform controller Hub) UART of Intel EG20T - which is an IOH(Input/Output Hub) for x86 embedded processor. - Enabling PCH_DMA, this PCH UART works as DMA mode. -endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile deleted file mode 100644 index 8ea92e9c73b0..000000000000 --- a/drivers/serial/Makefile +++ /dev/null @@ -1,94 +0,0 @@ -# -# Makefile for the kernel serial device drivers. -# - -obj-$(CONFIG_SERIAL_CORE) += serial_core.o -obj-$(CONFIG_SERIAL_21285) += 21285.o - -# These Sparc drivers have to appear before others such as 8250 -# which share ttySx minor node space. Otherwise console device -# names change and other unplesantries. -obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o -obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o -obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o -obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o -obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o - -obj-$(CONFIG_SERIAL_8250) += 8250.o -obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o -obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o -obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o -obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o -obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o -obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o -obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o -obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o -obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o -obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o -obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o -obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o -obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o -obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o -obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o -obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o -obj-$(CONFIG_SERIAL_PXA) += pxa.o -obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o -obj-$(CONFIG_SERIAL_SA1100) += sa1100.o -obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o -obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o -obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o -obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o -obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o -obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o -obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o -obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o -obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o -obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o -obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o -obj-$(CONFIG_SERIAL_MAX3100) += max3100.o -obj-$(CONFIG_SERIAL_MAX3107) += max3107.o -obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o -obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o -obj-$(CONFIG_SERIAL_MUX) += mux.o -obj-$(CONFIG_SERIAL_68328) += 68328serial.o -obj-$(CONFIG_SERIAL_68360) += 68360serial.o -obj-$(CONFIG_SERIAL_MCF) += mcf.o -obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o -obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o -obj-$(CONFIG_SERIAL_DZ) += dz.o -obj-$(CONFIG_SERIAL_ZS) += zs.o -obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o -obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o -obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ -obj-$(CONFIG_SERIAL_IMX) += imx.o -obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o -obj-$(CONFIG_SERIAL_ICOM) += icom.o -obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o -obj-$(CONFIG_SERIAL_MPSC) += mpsc.o -obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o -obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o -obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o -obj-$(CONFIG_SERIAL_JSM) += jsm/ -obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o -obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o -obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o -obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o -obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o -obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o -obj-$(CONFIG_SERIAL_MSM) += msm_serial.o -obj-$(CONFIG_SERIAL_NETX) += netx-serial.o -obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o -obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o -obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o -obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o -obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o -obj-$(CONFIG_SERIAL_QE) += ucc_uart.o -obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o -obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o -obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o -obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o -obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o -obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o -obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o -obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o -obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o diff --git a/drivers/serial/altera_jtaguart.c b/drivers/serial/altera_jtaguart.c deleted file mode 100644 index f9b49b5ff5e1..000000000000 --- a/drivers/serial/altera_jtaguart.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * altera_jtaguart.c -- Altera JTAG UART driver - * - * Based on mcf.c -- Freescale ColdFire UART driver - * - * (C) Copyright 2003-2007, Greg Ungerer - * (C) Copyright 2008, Thomas Chou - * (C) Copyright 2010, Tobias Klauser - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "altera_jtaguart" - -/* - * Altera JTAG UART register definitions according to the Altera JTAG UART - * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf - */ - -#define ALTERA_JTAGUART_SIZE 8 - -#define ALTERA_JTAGUART_DATA_REG 0 - -#define ALTERA_JTAGUART_DATA_DATA_MSK 0x000000FF -#define ALTERA_JTAGUART_DATA_RVALID_MSK 0x00008000 -#define ALTERA_JTAGUART_DATA_RAVAIL_MSK 0xFFFF0000 -#define ALTERA_JTAGUART_DATA_RAVAIL_OFF 16 - -#define ALTERA_JTAGUART_CONTROL_REG 4 - -#define ALTERA_JTAGUART_CONTROL_RE_MSK 0x00000001 -#define ALTERA_JTAGUART_CONTROL_WE_MSK 0x00000002 -#define ALTERA_JTAGUART_CONTROL_RI_MSK 0x00000100 -#define ALTERA_JTAGUART_CONTROL_RI_OFF 8 -#define ALTERA_JTAGUART_CONTROL_WI_MSK 0x00000200 -#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 -#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 -#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF 16 - -/* - * Local per-uart structure. - */ -struct altera_jtaguart { - struct uart_port port; - unsigned int sigs; /* Local copy of line sigs */ - unsigned long imr; /* Local IMR mirror */ -}; - -static unsigned int altera_jtaguart_tx_empty(struct uart_port *port) -{ - return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & - ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0; -} - -static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; -} - -static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs) -{ -} - -static void altera_jtaguart_start_tx(struct uart_port *port) -{ - struct altera_jtaguart *pp = - container_of(port, struct altera_jtaguart, port); - - pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK; - writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); -} - -static void altera_jtaguart_stop_tx(struct uart_port *port) -{ - struct altera_jtaguart *pp = - container_of(port, struct altera_jtaguart, port); - - pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; - writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); -} - -static void altera_jtaguart_stop_rx(struct uart_port *port) -{ - struct altera_jtaguart *pp = - container_of(port, struct altera_jtaguart, port); - - pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK; - writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); -} - -static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state) -{ -} - -static void altera_jtaguart_enable_ms(struct uart_port *port) -{ -} - -static void altera_jtaguart_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - /* Just copy the old termios settings back */ - if (old) - tty_termios_copy_hw(termios, old); -} - -static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) -{ - struct uart_port *port = &pp->port; - unsigned char ch, flag; - unsigned long status; - - while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) & - ALTERA_JTAGUART_DATA_RVALID_MSK) { - ch = status & ALTERA_JTAGUART_DATA_DATA_MSK; - flag = TTY_NORMAL; - port->icount.rx++; - - if (uart_handle_sysrq_char(port, ch)) - continue; - uart_insert_char(port, 0, 0, ch, flag); - } - - tty_flip_buffer_push(port->state->port.tty); -} - -static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) -{ - struct uart_port *port = &pp->port; - struct circ_buf *xmit = &port->state->xmit; - unsigned int pending, count; - - if (port->x_char) { - /* Send special char - probably flow control */ - writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG); - port->x_char = 0; - port->icount.tx++; - return; - } - - pending = uart_circ_chars_pending(xmit); - if (pending > 0) { - count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & - ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >> - ALTERA_JTAGUART_CONTROL_WSPACE_OFF; - if (count > pending) - count = pending; - if (count > 0) { - pending -= count; - while (count--) { - writel(xmit->buf[xmit->tail], - port->membase + ALTERA_JTAGUART_DATA_REG); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - if (pending < WAKEUP_CHARS) - uart_write_wakeup(port); - } - } - - if (pending == 0) { - pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; - writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); - } -} - -static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) -{ - struct uart_port *port = data; - struct altera_jtaguart *pp = - container_of(port, struct altera_jtaguart, port); - unsigned int isr; - - isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> - ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr; - - spin_lock(&port->lock); - - if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) - altera_jtaguart_rx_chars(pp); - if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) - altera_jtaguart_tx_chars(pp); - - spin_unlock(&port->lock); - - return IRQ_RETVAL(isr); -} - -static void altera_jtaguart_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_ALTERA_JTAGUART; - - /* Clear mask, so no surprise interrupts. */ - writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG); -} - -static int altera_jtaguart_startup(struct uart_port *port) -{ - struct altera_jtaguart *pp = - container_of(port, struct altera_jtaguart, port); - unsigned long flags; - int ret; - - ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, - DRV_NAME, port); - if (ret) { - pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " - "interrupt vector=%d\n", port->line, port->irq); - return ret; - } - - spin_lock_irqsave(&port->lock, flags); - - /* Enable RX interrupts now */ - pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK; - writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); - - spin_unlock_irqrestore(&port->lock, flags); - - return 0; -} - -static void altera_jtaguart_shutdown(struct uart_port *port) -{ - struct altera_jtaguart *pp = - container_of(port, struct altera_jtaguart, port); - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - /* Disable all interrupts now */ - pp->imr = 0; - writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); - - spin_unlock_irqrestore(&port->lock, flags); - - free_irq(port->irq, port); -} - -static const char *altera_jtaguart_type(struct uart_port *port) -{ - return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL; -} - -static int altera_jtaguart_request_port(struct uart_port *port) -{ - /* UARTs always present */ - return 0; -} - -static void altera_jtaguart_release_port(struct uart_port *port) -{ - /* Nothing to release... */ -} - -static int altera_jtaguart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART) - return -EINVAL; - return 0; -} - -/* - * Define the basic serial functions we support. - */ -static struct uart_ops altera_jtaguart_ops = { - .tx_empty = altera_jtaguart_tx_empty, - .get_mctrl = altera_jtaguart_get_mctrl, - .set_mctrl = altera_jtaguart_set_mctrl, - .start_tx = altera_jtaguart_start_tx, - .stop_tx = altera_jtaguart_stop_tx, - .stop_rx = altera_jtaguart_stop_rx, - .enable_ms = altera_jtaguart_enable_ms, - .break_ctl = altera_jtaguart_break_ctl, - .startup = altera_jtaguart_startup, - .shutdown = altera_jtaguart_shutdown, - .set_termios = altera_jtaguart_set_termios, - .type = altera_jtaguart_type, - .request_port = altera_jtaguart_request_port, - .release_port = altera_jtaguart_release_port, - .config_port = altera_jtaguart_config_port, - .verify_port = altera_jtaguart_verify_port, -}; - -#define ALTERA_JTAGUART_MAXPORTS 1 -static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS]; - -#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) - -int __init early_altera_jtaguart_setup(struct altera_jtaguart_platform_uart - *platp) -{ - struct uart_port *port; - int i; - - for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { - port = &altera_jtaguart_ports[i].port; - - port->line = i; - port->type = PORT_ALTERA_JTAGUART; - port->mapbase = platp[i].mapbase; - port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->flags = ASYNC_BOOT_AUTOCONF; - port->ops = &altera_jtaguart_ops; - } - - return 0; -} - -#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) -static void altera_jtaguart_console_putc(struct console *co, const char c) -{ - struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; - unsigned long status; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) & - ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { - if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { - spin_unlock_irqrestore(&port->lock, flags); - return; /* no connection activity */ - } - spin_unlock_irqrestore(&port->lock, flags); - cpu_relax(); - spin_lock_irqsave(&port->lock, flags); - } - writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); - spin_unlock_irqrestore(&port->lock, flags); -} -#else -static void altera_jtaguart_console_putc(struct console *co, const char c) -{ - struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & - ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { - spin_unlock_irqrestore(&port->lock, flags); - cpu_relax(); - spin_lock_irqsave(&port->lock, flags); - } - writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); - spin_unlock_irqrestore(&port->lock, flags); -} -#endif - -static void altera_jtaguart_console_write(struct console *co, const char *s, - unsigned int count) -{ - for (; count; count--, s++) { - altera_jtaguart_console_putc(co, *s); - if (*s == '\n') - altera_jtaguart_console_putc(co, '\r'); - } -} - -static int __init altera_jtaguart_console_setup(struct console *co, - char *options) -{ - struct uart_port *port; - - if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS) - return -EINVAL; - port = &altera_jtaguart_ports[co->index].port; - if (port->membase == 0) - return -ENODEV; - return 0; -} - -static struct uart_driver altera_jtaguart_driver; - -static struct console altera_jtaguart_console = { - .name = "ttyJ", - .write = altera_jtaguart_console_write, - .device = uart_console_device, - .setup = altera_jtaguart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &altera_jtaguart_driver, -}; - -static int __init altera_jtaguart_console_init(void) -{ - register_console(&altera_jtaguart_console); - return 0; -} - -console_initcall(altera_jtaguart_console_init); - -#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) - -#else - -#define ALTERA_JTAGUART_CONSOLE NULL - -#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ - -static struct uart_driver altera_jtaguart_driver = { - .owner = THIS_MODULE, - .driver_name = "altera_jtaguart", - .dev_name = "ttyJ", - .major = ALTERA_JTAGUART_MAJOR, - .minor = ALTERA_JTAGUART_MINOR, - .nr = ALTERA_JTAGUART_MAXPORTS, - .cons = ALTERA_JTAGUART_CONSOLE, -}; - -static int __devinit altera_jtaguart_probe(struct platform_device *pdev) -{ - struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; - struct uart_port *port; - int i; - - for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { - port = &altera_jtaguart_ports[i].port; - - port->line = i; - port->type = PORT_ALTERA_JTAGUART; - port->mapbase = platp[i].mapbase; - port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->ops = &altera_jtaguart_ops; - port->flags = ASYNC_BOOT_AUTOCONF; - - uart_add_one_port(&altera_jtaguart_driver, port); - } - - return 0; -} - -static int __devexit altera_jtaguart_remove(struct platform_device *pdev) -{ - struct uart_port *port; - int i; - - for (i = 0; i < ALTERA_JTAGUART_MAXPORTS; i++) { - port = &altera_jtaguart_ports[i].port; - if (port) - uart_remove_one_port(&altera_jtaguart_driver, port); - } - - return 0; -} - -static struct platform_driver altera_jtaguart_platform_driver = { - .probe = altera_jtaguart_probe, - .remove = __devexit_p(altera_jtaguart_remove), - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init altera_jtaguart_init(void) -{ - int rc; - - rc = uart_register_driver(&altera_jtaguart_driver); - if (rc) - return rc; - rc = platform_driver_register(&altera_jtaguart_platform_driver); - if (rc) { - uart_unregister_driver(&altera_jtaguart_driver); - return rc; - } - return 0; -} - -static void __exit altera_jtaguart_exit(void) -{ - platform_driver_unregister(&altera_jtaguart_platform_driver); - uart_unregister_driver(&altera_jtaguart_driver); -} - -module_init(altera_jtaguart_init); -module_exit(altera_jtaguart_exit); - -MODULE_DESCRIPTION("Altera JTAG UART driver"); -MODULE_AUTHOR("Thomas Chou "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c deleted file mode 100644 index 721216292a50..000000000000 --- a/drivers/serial/altera_uart.c +++ /dev/null @@ -1,608 +0,0 @@ -/* - * altera_uart.c -- Altera UART driver - * - * Based on mcf.c -- Freescale ColdFire UART driver - * - * (C) Copyright 2003-2007, Greg Ungerer - * (C) Copyright 2008, Thomas Chou - * (C) Copyright 2010, Tobias Klauser - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "altera_uart" -#define SERIAL_ALTERA_MAJOR 204 -#define SERIAL_ALTERA_MINOR 213 - -/* - * Altera UART register definitions according to the Nios UART datasheet: - * http://www.altera.com/literature/ds/ds_nios_uart.pdf - */ - -#define ALTERA_UART_SIZE 32 - -#define ALTERA_UART_RXDATA_REG 0 -#define ALTERA_UART_TXDATA_REG 4 -#define ALTERA_UART_STATUS_REG 8 -#define ALTERA_UART_CONTROL_REG 12 -#define ALTERA_UART_DIVISOR_REG 16 -#define ALTERA_UART_EOP_REG 20 - -#define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */ -#define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */ -#define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */ -#define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */ -#define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */ -#define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */ -#define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */ -#define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */ -#define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */ -#define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */ -#define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */ -#define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */ - - /* Enable interrupt on... */ -#define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */ -#define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */ -#define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */ -#define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */ -#define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */ -#define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */ -#define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */ -#define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */ -#define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/ - -#define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */ -#define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */ -#define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */ -#define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */ - -/* - * Local per-uart structure. - */ -struct altera_uart { - struct uart_port port; - struct timer_list tmr; - unsigned int sigs; /* Local copy of line sigs */ - unsigned short imr; /* Local IMR mirror */ -}; - -static u32 altera_uart_readl(struct uart_port *port, int reg) -{ - struct altera_uart_platform_uart *platp = port->private_data; - - return readl(port->membase + (reg << platp->bus_shift)); -} - -static void altera_uart_writel(struct uart_port *port, u32 dat, int reg) -{ - struct altera_uart_platform_uart *platp = port->private_data; - - writel(dat, port->membase + (reg << platp->bus_shift)); -} - -static unsigned int altera_uart_tx_empty(struct uart_port *port) -{ - return (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & - ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0; -} - -static unsigned int altera_uart_get_mctrl(struct uart_port *port) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - unsigned int sigs; - - sigs = (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & - ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0; - sigs |= (pp->sigs & TIOCM_RTS); - - return sigs; -} - -static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - - pp->sigs = sigs; - if (sigs & TIOCM_RTS) - pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; - else - pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); -} - -static void altera_uart_start_tx(struct uart_port *port) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - - pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); -} - -static void altera_uart_stop_tx(struct uart_port *port) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - - pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); -} - -static void altera_uart_stop_rx(struct uart_port *port) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - - pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); -} - -static void altera_uart_break_ctl(struct uart_port *port, int break_state) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - if (break_state == -1) - pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; - else - pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void altera_uart_enable_ms(struct uart_port *port) -{ -} - -static void altera_uart_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - unsigned int baud, baudclk; - - baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - baudclk = port->uartclk / baud; - - if (old) - tty_termios_copy_hw(termios, old); - tty_termios_encode_baud_rate(termios, baud, baud); - - spin_lock_irqsave(&port->lock, flags); - uart_update_timeout(port, termios->c_cflag, baud); - altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void altera_uart_rx_chars(struct altera_uart *pp) -{ - struct uart_port *port = &pp->port; - unsigned char ch, flag; - unsigned short status; - - while ((status = altera_uart_readl(port, ALTERA_UART_STATUS_REG)) & - ALTERA_UART_STATUS_RRDY_MSK) { - ch = altera_uart_readl(port, ALTERA_UART_RXDATA_REG); - flag = TTY_NORMAL; - port->icount.rx++; - - if (status & ALTERA_UART_STATUS_E_MSK) { - altera_uart_writel(port, status, - ALTERA_UART_STATUS_REG); - - if (status & ALTERA_UART_STATUS_BRK_MSK) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (status & ALTERA_UART_STATUS_PE_MSK) { - port->icount.parity++; - } else if (status & ALTERA_UART_STATUS_ROE_MSK) { - port->icount.overrun++; - } else if (status & ALTERA_UART_STATUS_FE_MSK) { - port->icount.frame++; - } - - status &= port->read_status_mask; - - if (status & ALTERA_UART_STATUS_BRK_MSK) - flag = TTY_BREAK; - else if (status & ALTERA_UART_STATUS_PE_MSK) - flag = TTY_PARITY; - else if (status & ALTERA_UART_STATUS_FE_MSK) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - continue; - uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch, - flag); - } - - tty_flip_buffer_push(port->state->port.tty); -} - -static void altera_uart_tx_chars(struct altera_uart *pp) -{ - struct uart_port *port = &pp->port; - struct circ_buf *xmit = &port->state->xmit; - - if (port->x_char) { - /* Send special char - probably flow control */ - altera_uart_writel(port, port->x_char, ALTERA_UART_TXDATA_REG); - port->x_char = 0; - port->icount.tx++; - return; - } - - while (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & - ALTERA_UART_STATUS_TRDY_MSK) { - if (xmit->head == xmit->tail) - break; - altera_uart_writel(port, xmit->buf[xmit->tail], - ALTERA_UART_TXDATA_REG); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (xmit->head == xmit->tail) { - pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; - altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); - } -} - -static irqreturn_t altera_uart_interrupt(int irq, void *data) -{ - struct uart_port *port = data; - struct altera_uart *pp = container_of(port, struct altera_uart, port); - unsigned int isr; - - isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr; - - spin_lock(&port->lock); - if (isr & ALTERA_UART_STATUS_RRDY_MSK) - altera_uart_rx_chars(pp); - if (isr & ALTERA_UART_STATUS_TRDY_MSK) - altera_uart_tx_chars(pp); - spin_unlock(&port->lock); - - return IRQ_RETVAL(isr); -} - -static void altera_uart_timer(unsigned long data) -{ - struct uart_port *port = (void *)data; - struct altera_uart *pp = container_of(port, struct altera_uart, port); - - altera_uart_interrupt(0, port); - mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); -} - -static void altera_uart_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_ALTERA_UART; - - /* Clear mask, so no surprise interrupts. */ - altera_uart_writel(port, 0, ALTERA_UART_CONTROL_REG); - /* Clear status register */ - altera_uart_writel(port, 0, ALTERA_UART_STATUS_REG); -} - -static int altera_uart_startup(struct uart_port *port) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - unsigned long flags; - int ret; - - if (!port->irq) { - setup_timer(&pp->tmr, altera_uart_timer, (unsigned long)port); - mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); - return 0; - } - - ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, - DRV_NAME, port); - if (ret) { - pr_err(DRV_NAME ": unable to attach Altera UART %d " - "interrupt vector=%d\n", port->line, port->irq); - return ret; - } - - spin_lock_irqsave(&port->lock, flags); - - /* Enable RX interrupts now */ - pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; - writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); - - spin_unlock_irqrestore(&port->lock, flags); - - return 0; -} - -static void altera_uart_shutdown(struct uart_port *port) -{ - struct altera_uart *pp = container_of(port, struct altera_uart, port); - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - /* Disable all interrupts now */ - pp->imr = 0; - writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); - - spin_unlock_irqrestore(&port->lock, flags); - - if (port->irq) - free_irq(port->irq, port); - else - del_timer_sync(&pp->tmr); -} - -static const char *altera_uart_type(struct uart_port *port) -{ - return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL; -} - -static int altera_uart_request_port(struct uart_port *port) -{ - /* UARTs always present */ - return 0; -} - -static void altera_uart_release_port(struct uart_port *port) -{ - /* Nothing to release... */ -} - -static int altera_uart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART)) - return -EINVAL; - return 0; -} - -/* - * Define the basic serial functions we support. - */ -static struct uart_ops altera_uart_ops = { - .tx_empty = altera_uart_tx_empty, - .get_mctrl = altera_uart_get_mctrl, - .set_mctrl = altera_uart_set_mctrl, - .start_tx = altera_uart_start_tx, - .stop_tx = altera_uart_stop_tx, - .stop_rx = altera_uart_stop_rx, - .enable_ms = altera_uart_enable_ms, - .break_ctl = altera_uart_break_ctl, - .startup = altera_uart_startup, - .shutdown = altera_uart_shutdown, - .set_termios = altera_uart_set_termios, - .type = altera_uart_type, - .request_port = altera_uart_request_port, - .release_port = altera_uart_release_port, - .config_port = altera_uart_config_port, - .verify_port = altera_uart_verify_port, -}; - -static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; - -#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) - -int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) -{ - struct uart_port *port; - int i; - - for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { - port = &altera_uart_ports[i].port; - - port->line = i; - port->type = PORT_ALTERA_UART; - port->mapbase = platp[i].mapbase; - port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->uartclk = platp[i].uartclk; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &altera_uart_ops; - port->private_data = platp; - } - - return 0; -} - -static void altera_uart_console_putc(struct uart_port *port, const char c) -{ - while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) & - ALTERA_UART_STATUS_TRDY_MSK)) - cpu_relax(); - - writel(c, port->membase + ALTERA_UART_TXDATA_REG); -} - -static void altera_uart_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_port *port = &(altera_uart_ports + co->index)->port; - - for (; count; count--, s++) { - altera_uart_console_putc(port, *s); - if (*s == '\n') - altera_uart_console_putc(port, '\r'); - } -} - -static int __init altera_uart_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) - return -EINVAL; - port = &altera_uart_ports[co->index].port; - if (!port->membase) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver altera_uart_driver; - -static struct console altera_uart_console = { - .name = "ttyAL", - .write = altera_uart_console_write, - .device = uart_console_device, - .setup = altera_uart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &altera_uart_driver, -}; - -static int __init altera_uart_console_init(void) -{ - register_console(&altera_uart_console); - return 0; -} - -console_initcall(altera_uart_console_init); - -#define ALTERA_UART_CONSOLE (&altera_uart_console) - -#else - -#define ALTERA_UART_CONSOLE NULL - -#endif /* CONFIG_ALTERA_UART_CONSOLE */ - -/* - * Define the altera_uart UART driver structure. - */ -static struct uart_driver altera_uart_driver = { - .owner = THIS_MODULE, - .driver_name = DRV_NAME, - .dev_name = "ttyAL", - .major = SERIAL_ALTERA_MAJOR, - .minor = SERIAL_ALTERA_MINOR, - .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS, - .cons = ALTERA_UART_CONSOLE, -}; - -static int __devinit altera_uart_probe(struct platform_device *pdev) -{ - struct altera_uart_platform_uart *platp = pdev->dev.platform_data; - struct uart_port *port; - struct resource *res_mem; - struct resource *res_irq; - int i = pdev->id; - - /* -1 emphasizes that the platform must have one port, no .N suffix */ - if (i == -1) - i = 0; - - if (i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) - return -EINVAL; - - port = &altera_uart_ports[i].port; - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res_mem) - port->mapbase = res_mem->start; - else if (platp->mapbase) - port->mapbase = platp->mapbase; - else - return -EINVAL; - - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res_irq) - port->irq = res_irq->start; - else if (platp->irq) - port->irq = platp->irq; - - port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); - if (!port->membase) - return -ENOMEM; - - port->line = i; - port->type = PORT_ALTERA_UART; - port->iotype = SERIAL_IO_MEM; - port->uartclk = platp->uartclk; - port->ops = &altera_uart_ops; - port->flags = UPF_BOOT_AUTOCONF; - port->private_data = platp; - - uart_add_one_port(&altera_uart_driver, port); - - return 0; -} - -static int __devexit altera_uart_remove(struct platform_device *pdev) -{ - struct uart_port *port = &altera_uart_ports[pdev->id].port; - - uart_remove_one_port(&altera_uart_driver, port); - return 0; -} - -static struct platform_driver altera_uart_platform_driver = { - .probe = altera_uart_probe, - .remove = __devexit_p(altera_uart_remove), - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, - .pm = NULL, - }, -}; - -static int __init altera_uart_init(void) -{ - int rc; - - rc = uart_register_driver(&altera_uart_driver); - if (rc) - return rc; - rc = platform_driver_register(&altera_uart_platform_driver); - if (rc) { - uart_unregister_driver(&altera_uart_driver); - return rc; - } - return 0; -} - -static void __exit altera_uart_exit(void) -{ - platform_driver_unregister(&altera_uart_platform_driver); - uart_unregister_driver(&altera_uart_driver); -} - -module_init(altera_uart_init); -module_exit(altera_uart_exit); - -MODULE_DESCRIPTION("Altera UART driver"); -MODULE_AUTHOR("Thomas Chou "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); -MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_ALTERA_MAJOR); diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c deleted file mode 100644 index 2904aa044126..000000000000 --- a/drivers/serial/amba-pl010.c +++ /dev/null @@ -1,825 +0,0 @@ -/* - * linux/drivers/char/amba.c - * - * Driver for AMBA serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright 1999 ARM Limited - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * - * 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 - * - * This is a generic driver for ARM AMBA-type serial ports. They - * have a lot of 16550-like features, but are not register compatible. - * Note that although they do have CTS, DCD and DSR inputs, they do - * not have an RI input, nor do they have DTR or RTS outputs. If - * required, these have to be supplied via some other means (eg, GPIO) - * and hooked into this driver. - */ - -#if defined(CONFIG_SERIAL_AMBA_PL010_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define UART_NR 8 - -#define SERIAL_AMBA_MAJOR 204 -#define SERIAL_AMBA_MINOR 16 -#define SERIAL_AMBA_NR UART_NR - -#define AMBA_ISR_PASS_LIMIT 256 - -#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0) -#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0) - -#define UART_DUMMY_RSR_RX 256 -#define UART_PORT_SIZE 64 - -/* - * We wrap our port structure around the generic uart_port. - */ -struct uart_amba_port { - struct uart_port port; - struct clk *clk; - struct amba_device *dev; - struct amba_pl010_data *data; - unsigned int old_status; -}; - -static void pl010_stop_tx(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; - - cr = readb(uap->port.membase + UART010_CR); - cr &= ~UART010_CR_TIE; - writel(cr, uap->port.membase + UART010_CR); -} - -static void pl010_start_tx(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; - - cr = readb(uap->port.membase + UART010_CR); - cr |= UART010_CR_TIE; - writel(cr, uap->port.membase + UART010_CR); -} - -static void pl010_stop_rx(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; - - cr = readb(uap->port.membase + UART010_CR); - cr &= ~(UART010_CR_RIE | UART010_CR_RTIE); - writel(cr, uap->port.membase + UART010_CR); -} - -static void pl010_enable_ms(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; - - cr = readb(uap->port.membase + UART010_CR); - cr |= UART010_CR_MSIE; - writel(cr, uap->port.membase + UART010_CR); -} - -static void pl010_rx_chars(struct uart_amba_port *uap) -{ - struct tty_struct *tty = uap->port.state->port.tty; - unsigned int status, ch, flag, rsr, max_count = 256; - - status = readb(uap->port.membase + UART01x_FR); - while (UART_RX_DATA(status) && max_count--) { - ch = readb(uap->port.membase + UART01x_DR); - flag = TTY_NORMAL; - - uap->port.icount.rx++; - - /* - * Note that the error handling code is - * out of the main execution path - */ - rsr = readb(uap->port.membase + UART01x_RSR) | UART_DUMMY_RSR_RX; - if (unlikely(rsr & UART01x_RSR_ANY)) { - writel(0, uap->port.membase + UART01x_ECR); - - if (rsr & UART01x_RSR_BE) { - rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE); - uap->port.icount.brk++; - if (uart_handle_break(&uap->port)) - goto ignore_char; - } else if (rsr & UART01x_RSR_PE) - uap->port.icount.parity++; - else if (rsr & UART01x_RSR_FE) - uap->port.icount.frame++; - if (rsr & UART01x_RSR_OE) - uap->port.icount.overrun++; - - rsr &= uap->port.read_status_mask; - - if (rsr & UART01x_RSR_BE) - flag = TTY_BREAK; - else if (rsr & UART01x_RSR_PE) - flag = TTY_PARITY; - else if (rsr & UART01x_RSR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&uap->port, ch)) - goto ignore_char; - - uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag); - - ignore_char: - status = readb(uap->port.membase + UART01x_FR); - } - spin_unlock(&uap->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&uap->port.lock); -} - -static void pl010_tx_chars(struct uart_amba_port *uap) -{ - struct circ_buf *xmit = &uap->port.state->xmit; - int count; - - if (uap->port.x_char) { - writel(uap->port.x_char, uap->port.membase + UART01x_DR); - uap->port.icount.tx++; - uap->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { - pl010_stop_tx(&uap->port); - return; - } - - count = uap->port.fifosize >> 1; - do { - writel(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uap->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uap->port); - - if (uart_circ_empty(xmit)) - pl010_stop_tx(&uap->port); -} - -static void pl010_modem_status(struct uart_amba_port *uap) -{ - unsigned int status, delta; - - writel(0, uap->port.membase + UART010_ICR); - - status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; - - delta = status ^ uap->old_status; - uap->old_status = status; - - if (!delta) - return; - - if (delta & UART01x_FR_DCD) - uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); - - if (delta & UART01x_FR_DSR) - uap->port.icount.dsr++; - - if (delta & UART01x_FR_CTS) - uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); - - wake_up_interruptible(&uap->port.state->port.delta_msr_wait); -} - -static irqreturn_t pl010_int(int irq, void *dev_id) -{ - struct uart_amba_port *uap = dev_id; - unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; - int handled = 0; - - spin_lock(&uap->port.lock); - - status = readb(uap->port.membase + UART010_IIR); - if (status) { - do { - if (status & (UART010_IIR_RTIS | UART010_IIR_RIS)) - pl010_rx_chars(uap); - if (status & UART010_IIR_MIS) - pl010_modem_status(uap); - if (status & UART010_IIR_TIS) - pl010_tx_chars(uap); - - if (pass_counter-- == 0) - break; - - status = readb(uap->port.membase + UART010_IIR); - } while (status & (UART010_IIR_RTIS | UART010_IIR_RIS | - UART010_IIR_TIS)); - handled = 1; - } - - spin_unlock(&uap->port.lock); - - return IRQ_RETVAL(handled); -} - -static unsigned int pl010_tx_empty(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int status = readb(uap->port.membase + UART01x_FR); - return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT; -} - -static unsigned int pl010_get_mctrl(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int result = 0; - unsigned int status; - - status = readb(uap->port.membase + UART01x_FR); - if (status & UART01x_FR_DCD) - result |= TIOCM_CAR; - if (status & UART01x_FR_DSR) - result |= TIOCM_DSR; - if (status & UART01x_FR_CTS) - result |= TIOCM_CTS; - - return result; -} - -static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - if (uap->data) - uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl); -} - -static void pl010_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned long flags; - unsigned int lcr_h; - - spin_lock_irqsave(&uap->port.lock, flags); - lcr_h = readb(uap->port.membase + UART010_LCRH); - if (break_state == -1) - lcr_h |= UART01x_LCRH_BRK; - else - lcr_h &= ~UART01x_LCRH_BRK; - writel(lcr_h, uap->port.membase + UART010_LCRH); - spin_unlock_irqrestore(&uap->port.lock, flags); -} - -static int pl010_startup(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - int retval; - - /* - * Try to enable the clock producer. - */ - retval = clk_enable(uap->clk); - if (retval) - goto out; - - uap->port.uartclk = clk_get_rate(uap->clk); - - /* - * Allocate the IRQ - */ - retval = request_irq(uap->port.irq, pl010_int, 0, "uart-pl010", uap); - if (retval) - goto clk_dis; - - /* - * initialise the old status of the modem signals - */ - uap->old_status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; - - /* - * Finally, enable interrupts - */ - writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE, - uap->port.membase + UART010_CR); - - return 0; - - clk_dis: - clk_disable(uap->clk); - out: - return retval; -} - -static void pl010_shutdown(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - /* - * Free the interrupt - */ - free_irq(uap->port.irq, uap); - - /* - * disable all interrupts, disable the port - */ - writel(0, uap->port.membase + UART010_CR); - - /* disable break condition and fifos */ - writel(readb(uap->port.membase + UART010_LCRH) & - ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN), - uap->port.membase + UART010_LCRH); - - /* - * Shut down the clock producer - */ - clk_disable(uap->clk); -} - -static void -pl010_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int lcr_h, old_cr; - unsigned long flags; - unsigned int baud, quot; - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, uap->port.uartclk/16); - quot = uart_get_divisor(port, baud); - - switch (termios->c_cflag & CSIZE) { - case CS5: - lcr_h = UART01x_LCRH_WLEN_5; - break; - case CS6: - lcr_h = UART01x_LCRH_WLEN_6; - break; - case CS7: - lcr_h = UART01x_LCRH_WLEN_7; - break; - default: // CS8 - lcr_h = UART01x_LCRH_WLEN_8; - break; - } - if (termios->c_cflag & CSTOPB) - lcr_h |= UART01x_LCRH_STP2; - if (termios->c_cflag & PARENB) { - lcr_h |= UART01x_LCRH_PEN; - if (!(termios->c_cflag & PARODD)) - lcr_h |= UART01x_LCRH_EPS; - } - if (uap->port.fifosize > 1) - lcr_h |= UART01x_LCRH_FEN; - - spin_lock_irqsave(&uap->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - uap->port.read_status_mask = UART01x_RSR_OE; - if (termios->c_iflag & INPCK) - uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - uap->port.read_status_mask |= UART01x_RSR_BE; - - /* - * Characters to ignore - */ - uap->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - uap->port.ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; - if (termios->c_iflag & IGNBRK) { - uap->port.ignore_status_mask |= UART01x_RSR_BE; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - uap->port.ignore_status_mask |= UART01x_RSR_OE; - } - - /* - * Ignore all characters if CREAD is not set. - */ - if ((termios->c_cflag & CREAD) == 0) - uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX; - - /* first, disable everything */ - old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE; - - if (UART_ENABLE_MS(port, termios->c_cflag)) - old_cr |= UART010_CR_MSIE; - - writel(0, uap->port.membase + UART010_CR); - - /* Set baud rate */ - quot -= 1; - writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM); - writel(quot & 0xff, uap->port.membase + UART010_LCRL); - - /* - * ----------v----------v----------v----------v----- - * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L - * ----------^----------^----------^----------^----- - */ - writel(lcr_h, uap->port.membase + UART010_LCRH); - writel(old_cr, uap->port.membase + UART010_CR); - - spin_unlock_irqrestore(&uap->port.lock, flags); -} - -static void pl010_set_ldisc(struct uart_port *port, int new) -{ - if (new == N_PPS) { - port->flags |= UPF_HARDPPS_CD; - pl010_enable_ms(port); - } else - port->flags &= ~UPF_HARDPPS_CD; -} - -static const char *pl010_type(struct uart_port *port) -{ - return port->type == PORT_AMBA ? "AMBA" : NULL; -} - -/* - * Release the memory region(s) being used by 'port' - */ -static void pl010_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, UART_PORT_SIZE); -} - -/* - * Request the memory region(s) being used by 'port' - */ -static int pl010_request_port(struct uart_port *port) -{ - return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010") - != NULL ? 0 : -EBUSY; -} - -/* - * Configure/autoconfigure the port. - */ -static void pl010_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_AMBA; - pl010_request_port(port); - } -} - -/* - * verify the new serial_struct (for TIOCSSERIAL). - */ -static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - int ret = 0; - if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) - ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) - ret = -EINVAL; - if (ser->baud_base < 9600) - ret = -EINVAL; - return ret; -} - -static struct uart_ops amba_pl010_pops = { - .tx_empty = pl010_tx_empty, - .set_mctrl = pl010_set_mctrl, - .get_mctrl = pl010_get_mctrl, - .stop_tx = pl010_stop_tx, - .start_tx = pl010_start_tx, - .stop_rx = pl010_stop_rx, - .enable_ms = pl010_enable_ms, - .break_ctl = pl010_break_ctl, - .startup = pl010_startup, - .shutdown = pl010_shutdown, - .set_termios = pl010_set_termios, - .set_ldisc = pl010_set_ldisc, - .type = pl010_type, - .release_port = pl010_release_port, - .request_port = pl010_request_port, - .config_port = pl010_config_port, - .verify_port = pl010_verify_port, -}; - -static struct uart_amba_port *amba_ports[UART_NR]; - -#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE - -static void pl010_console_putchar(struct uart_port *port, int ch) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int status; - - do { - status = readb(uap->port.membase + UART01x_FR); - barrier(); - } while (!UART_TX_READY(status)); - writel(ch, uap->port.membase + UART01x_DR); -} - -static void -pl010_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_amba_port *uap = amba_ports[co->index]; - unsigned int status, old_cr; - - clk_enable(uap->clk); - - /* - * First save the CR then disable the interrupts - */ - old_cr = readb(uap->port.membase + UART010_CR); - writel(UART01x_CR_UARTEN, uap->port.membase + UART010_CR); - - uart_console_write(&uap->port, s, count, pl010_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the TCR - */ - do { - status = readb(uap->port.membase + UART01x_FR); - barrier(); - } while (status & UART01x_FR_BUSY); - writel(old_cr, uap->port.membase + UART010_CR); - - clk_disable(uap->clk); -} - -static void __init -pl010_console_get_options(struct uart_amba_port *uap, int *baud, - int *parity, int *bits) -{ - if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) { - unsigned int lcr_h, quot; - lcr_h = readb(uap->port.membase + UART010_LCRH); - - *parity = 'n'; - if (lcr_h & UART01x_LCRH_PEN) { - if (lcr_h & UART01x_LCRH_EPS) - *parity = 'e'; - else - *parity = 'o'; - } - - if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) - *bits = 7; - else - *bits = 8; - - quot = readb(uap->port.membase + UART010_LCRL) | - readb(uap->port.membase + UART010_LCRM) << 8; - *baud = uap->port.uartclk / (16 * (quot + 1)); - } -} - -static int __init pl010_console_setup(struct console *co, char *options) -{ - struct uart_amba_port *uap; - 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. - */ - if (co->index >= UART_NR) - co->index = 0; - uap = amba_ports[co->index]; - if (!uap) - return -ENODEV; - - uap->port.uartclk = clk_get_rate(uap->clk); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - pl010_console_get_options(uap, &baud, &parity, &bits); - - return uart_set_options(&uap->port, co, baud, parity, bits, flow); -} - -static struct uart_driver amba_reg; -static struct console amba_console = { - .name = "ttyAM", - .write = pl010_console_write, - .device = uart_console_device, - .setup = pl010_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &amba_reg, -}; - -#define AMBA_CONSOLE &amba_console -#else -#define AMBA_CONSOLE NULL -#endif - -static struct uart_driver amba_reg = { - .owner = THIS_MODULE, - .driver_name = "ttyAM", - .dev_name = "ttyAM", - .major = SERIAL_AMBA_MAJOR, - .minor = SERIAL_AMBA_MINOR, - .nr = UART_NR, - .cons = AMBA_CONSOLE, -}; - -static int pl010_probe(struct amba_device *dev, struct amba_id *id) -{ - struct uart_amba_port *uap; - void __iomem *base; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == NULL) - break; - - if (i == ARRAY_SIZE(amba_ports)) { - ret = -EBUSY; - goto out; - } - - uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); - if (!uap) { - ret = -ENOMEM; - goto out; - } - - base = ioremap(dev->res.start, resource_size(&dev->res)); - if (!base) { - ret = -ENOMEM; - goto free; - } - - uap->clk = clk_get(&dev->dev, NULL); - if (IS_ERR(uap->clk)) { - ret = PTR_ERR(uap->clk); - goto unmap; - } - - uap->port.dev = &dev->dev; - uap->port.mapbase = dev->res.start; - uap->port.membase = base; - uap->port.iotype = UPIO_MEM; - uap->port.irq = dev->irq[0]; - uap->port.fifosize = 16; - uap->port.ops = &amba_pl010_pops; - uap->port.flags = UPF_BOOT_AUTOCONF; - uap->port.line = i; - uap->dev = dev; - uap->data = dev->dev.platform_data; - - amba_ports[i] = uap; - - amba_set_drvdata(dev, uap); - ret = uart_add_one_port(&amba_reg, &uap->port); - if (ret) { - amba_set_drvdata(dev, NULL); - amba_ports[i] = NULL; - clk_put(uap->clk); - unmap: - iounmap(base); - free: - kfree(uap); - } - out: - return ret; -} - -static int pl010_remove(struct amba_device *dev) -{ - struct uart_amba_port *uap = amba_get_drvdata(dev); - int i; - - amba_set_drvdata(dev, NULL); - - uart_remove_one_port(&amba_reg, &uap->port); - - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == uap) - amba_ports[i] = NULL; - - iounmap(uap->port.membase); - clk_put(uap->clk); - kfree(uap); - return 0; -} - -static int pl010_suspend(struct amba_device *dev, pm_message_t state) -{ - struct uart_amba_port *uap = amba_get_drvdata(dev); - - if (uap) - uart_suspend_port(&amba_reg, &uap->port); - - return 0; -} - -static int pl010_resume(struct amba_device *dev) -{ - struct uart_amba_port *uap = amba_get_drvdata(dev); - - if (uap) - uart_resume_port(&amba_reg, &uap->port); - - return 0; -} - -static struct amba_id pl010_ids[] = { - { - .id = 0x00041010, - .mask = 0x000fffff, - }, - { 0, 0 }, -}; - -static struct amba_driver pl010_driver = { - .drv = { - .name = "uart-pl010", - }, - .id_table = pl010_ids, - .probe = pl010_probe, - .remove = pl010_remove, - .suspend = pl010_suspend, - .resume = pl010_resume, -}; - -static int __init pl010_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: AMBA driver\n"); - - ret = uart_register_driver(&amba_reg); - if (ret == 0) { - ret = amba_driver_register(&pl010_driver); - if (ret) - uart_unregister_driver(&amba_reg); - } - return ret; -} - -static void __exit pl010_exit(void) -{ - amba_driver_unregister(&pl010_driver); - uart_unregister_driver(&amba_reg); -} - -module_init(pl010_init); -module_exit(pl010_exit); - -MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("ARM AMBA serial port driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c deleted file mode 100644 index e76d7d000128..000000000000 --- a/drivers/serial/amba-pl011.c +++ /dev/null @@ -1,1519 +0,0 @@ -/* - * linux/drivers/char/amba.c - * - * Driver for AMBA serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright 1999 ARM Limited - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * Copyright (C) 2010 ST-Ericsson SA - * - * 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 - * - * This is a generic driver for ARM AMBA-type serial ports. They - * have a lot of 16550-like features, but are not register compatible. - * Note that although they do have CTS, DCD and DSR inputs, they do - * not have an RI input, nor do they have DTR or RTS outputs. If - * required, these have to be supplied via some other means (eg, GPIO) - * and hooked into this driver. - */ - -#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define UART_NR 14 - -#define SERIAL_AMBA_MAJOR 204 -#define SERIAL_AMBA_MINOR 64 -#define SERIAL_AMBA_NR UART_NR - -#define AMBA_ISR_PASS_LIMIT 256 - -#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) -#define UART_DUMMY_DR_RX (1 << 16) - -/* There is by now at least one vendor with differing details, so handle it */ -struct vendor_data { - unsigned int ifls; - unsigned int fifosize; - unsigned int lcrh_tx; - unsigned int lcrh_rx; - bool oversampling; - bool dma_threshold; -}; - -static struct vendor_data vendor_arm = { - .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, - .fifosize = 16, - .lcrh_tx = UART011_LCRH, - .lcrh_rx = UART011_LCRH, - .oversampling = false, - .dma_threshold = false, -}; - -static struct vendor_data vendor_st = { - .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, - .fifosize = 64, - .lcrh_tx = ST_UART011_LCRH_TX, - .lcrh_rx = ST_UART011_LCRH_RX, - .oversampling = true, - .dma_threshold = true, -}; - -/* Deals with DMA transactions */ -struct pl011_dmatx_data { - struct dma_chan *chan; - struct scatterlist sg; - char *buf; - bool queued; -}; - -/* - * We wrap our port structure around the generic uart_port. - */ -struct uart_amba_port { - struct uart_port port; - struct clk *clk; - const struct vendor_data *vendor; - unsigned int dmacr; /* dma control reg */ - unsigned int im; /* interrupt mask */ - unsigned int old_status; - unsigned int fifosize; /* vendor-specific */ - unsigned int lcrh_tx; /* vendor-specific */ - unsigned int lcrh_rx; /* vendor-specific */ - bool autorts; - char type[12]; -#ifdef CONFIG_DMA_ENGINE - /* DMA stuff */ - bool using_dma; - struct pl011_dmatx_data dmatx; -#endif -}; - -/* - * All the DMA operation mode stuff goes inside this ifdef. - * This assumes that you have a generic DMA device interface, - * no custom DMA interfaces are supported. - */ -#ifdef CONFIG_DMA_ENGINE - -#define PL011_DMA_BUFFER_SIZE PAGE_SIZE - -static void pl011_dma_probe_initcall(struct uart_amba_port *uap) -{ - /* DMA is the sole user of the platform data right now */ - struct amba_pl011_data *plat = uap->port.dev->platform_data; - struct dma_slave_config tx_conf = { - .dst_addr = uap->port.mapbase + UART01x_DR, - .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, - .direction = DMA_TO_DEVICE, - .dst_maxburst = uap->fifosize >> 1, - }; - struct dma_chan *chan; - dma_cap_mask_t mask; - - /* We need platform data */ - if (!plat || !plat->dma_filter) { - dev_info(uap->port.dev, "no DMA platform data\n"); - return; - } - - /* Try to acquire a generic DMA engine slave channel */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); - if (!chan) { - dev_err(uap->port.dev, "no TX DMA channel!\n"); - return; - } - - dmaengine_slave_config(chan, &tx_conf); - uap->dmatx.chan = chan; - - dev_info(uap->port.dev, "DMA channel TX %s\n", - dma_chan_name(uap->dmatx.chan)); -} - -#ifndef MODULE -/* - * Stack up the UARTs and let the above initcall be done at device - * initcall time, because the serial driver is called as an arch - * initcall, and at this time the DMA subsystem is not yet registered. - * At this point the driver will switch over to using DMA where desired. - */ -struct dma_uap { - struct list_head node; - struct uart_amba_port *uap; -}; - -static LIST_HEAD(pl011_dma_uarts); - -static int __init pl011_dma_initcall(void) -{ - struct list_head *node, *tmp; - - list_for_each_safe(node, tmp, &pl011_dma_uarts) { - struct dma_uap *dmau = list_entry(node, struct dma_uap, node); - pl011_dma_probe_initcall(dmau->uap); - list_del(node); - kfree(dmau); - } - return 0; -} - -device_initcall(pl011_dma_initcall); - -static void pl011_dma_probe(struct uart_amba_port *uap) -{ - struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL); - if (dmau) { - dmau->uap = uap; - list_add_tail(&dmau->node, &pl011_dma_uarts); - } -} -#else -static void pl011_dma_probe(struct uart_amba_port *uap) -{ - pl011_dma_probe_initcall(uap); -} -#endif - -static void pl011_dma_remove(struct uart_amba_port *uap) -{ - /* TODO: remove the initcall if it has not yet executed */ - if (uap->dmatx.chan) - dma_release_channel(uap->dmatx.chan); -} - - -/* Forward declare this for the refill routine */ -static int pl011_dma_tx_refill(struct uart_amba_port *uap); - -/* - * The current DMA TX buffer has been sent. - * Try to queue up another DMA buffer. - */ -static void pl011_dma_tx_callback(void *data) -{ - struct uart_amba_port *uap = data; - struct pl011_dmatx_data *dmatx = &uap->dmatx; - unsigned long flags; - u16 dmacr; - - spin_lock_irqsave(&uap->port.lock, flags); - if (uap->dmatx.queued) - dma_unmap_sg(dmatx->chan->device->dev, &dmatx->sg, 1, - DMA_TO_DEVICE); - - dmacr = uap->dmacr; - uap->dmacr = dmacr & ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - - /* - * If TX DMA was disabled, it means that we've stopped the DMA for - * some reason (eg, XOFF received, or we want to send an X-char.) - * - * Note: we need to be careful here of a potential race between DMA - * and the rest of the driver - if the driver disables TX DMA while - * a TX buffer completing, we must update the tx queued status to - * get further refills (hence we check dmacr). - */ - if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || - uart_circ_empty(&uap->port.state->xmit)) { - uap->dmatx.queued = false; - spin_unlock_irqrestore(&uap->port.lock, flags); - return; - } - - if (pl011_dma_tx_refill(uap) <= 0) { - /* - * We didn't queue a DMA buffer for some reason, but we - * have data pending to be sent. Re-enable the TX IRQ. - */ - uap->im |= UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - } - spin_unlock_irqrestore(&uap->port.lock, flags); -} - -/* - * Try to refill the TX DMA buffer. - * Locking: called with port lock held and IRQs disabled. - * Returns: - * 1 if we queued up a TX DMA buffer. - * 0 if we didn't want to handle this by DMA - * <0 on error - */ -static int pl011_dma_tx_refill(struct uart_amba_port *uap) -{ - struct pl011_dmatx_data *dmatx = &uap->dmatx; - struct dma_chan *chan = dmatx->chan; - struct dma_device *dma_dev = chan->device; - struct dma_async_tx_descriptor *desc; - struct circ_buf *xmit = &uap->port.state->xmit; - unsigned int count; - - /* - * Try to avoid the overhead involved in using DMA if the - * transaction fits in the first half of the FIFO, by using - * the standard interrupt handling. This ensures that we - * issue a uart_write_wakeup() at the appropriate time. - */ - count = uart_circ_chars_pending(xmit); - if (count < (uap->fifosize >> 1)) { - uap->dmatx.queued = false; - return 0; - } - - /* - * Bodge: don't send the last character by DMA, as this - * will prevent XON from notifying us to restart DMA. - */ - count -= 1; - - /* Else proceed to copy the TX chars to the DMA buffer and fire DMA */ - if (count > PL011_DMA_BUFFER_SIZE) - count = PL011_DMA_BUFFER_SIZE; - - if (xmit->tail < xmit->head) - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); - else { - size_t first = UART_XMIT_SIZE - xmit->tail; - size_t second = xmit->head; - - memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first); - if (second) - memcpy(&dmatx->buf[first], &xmit->buf[0], second); - } - - dmatx->sg.length = count; - - if (dma_map_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE) != 1) { - uap->dmatx.queued = false; - dev_dbg(uap->port.dev, "unable to map TX DMA\n"); - return -EBUSY; - } - - desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_TO_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE); - uap->dmatx.queued = false; - /* - * If DMA cannot be used right now, we complete this - * transaction via IRQ and let the TTY layer retry. - */ - dev_dbg(uap->port.dev, "TX DMA busy\n"); - return -EBUSY; - } - - /* Some data to go along to the callback */ - desc->callback = pl011_dma_tx_callback; - desc->callback_param = uap; - - /* All errors should happen at prepare time */ - dmaengine_submit(desc); - - /* Fire the DMA transaction */ - dma_dev->device_issue_pending(chan); - - uap->dmacr |= UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - uap->dmatx.queued = true; - - /* - * Now we know that DMA will fire, so advance the ring buffer - * with the stuff we just dispatched. - */ - xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); - uap->port.icount.tx += count; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uap->port); - - return 1; -} - -/* - * We received a transmit interrupt without a pending X-char but with - * pending characters. - * Locking: called with port lock held and IRQs disabled. - * Returns: - * false if we want to use PIO to transmit - * true if we queued a DMA buffer - */ -static bool pl011_dma_tx_irq(struct uart_amba_port *uap) -{ - if (!uap->using_dma) - return false; - - /* - * If we already have a TX buffer queued, but received a - * TX interrupt, it will be because we've just sent an X-char. - * Ensure the TX DMA is enabled and the TX IRQ is disabled. - */ - if (uap->dmatx.queued) { - uap->dmacr |= UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - return true; - } - - /* - * We don't have a TX buffer queued, so try to queue one. - * If we succesfully queued a buffer, mask the TX IRQ. - */ - if (pl011_dma_tx_refill(uap) > 0) { - uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - return true; - } - return false; -} - -/* - * Stop the DMA transmit (eg, due to received XOFF). - * Locking: called with port lock held and IRQs disabled. - */ -static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) -{ - if (uap->dmatx.queued) { - uap->dmacr &= ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - } -} - -/* - * Try to start a DMA transmit, or in the case of an XON/OFF - * character queued for send, try to get that character out ASAP. - * Locking: called with port lock held and IRQs disabled. - * Returns: - * false if we want the TX IRQ to be enabled - * true if we have a buffer queued - */ -static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) -{ - u16 dmacr; - - if (!uap->using_dma) - return false; - - if (!uap->port.x_char) { - /* no X-char, try to push chars out in DMA mode */ - bool ret = true; - - if (!uap->dmatx.queued) { - if (pl011_dma_tx_refill(uap) > 0) { - uap->im &= ~UART011_TXIM; - ret = true; - } else { - uap->im |= UART011_TXIM; - ret = false; - } - writew(uap->im, uap->port.membase + UART011_IMSC); - } else if (!(uap->dmacr & UART011_TXDMAE)) { - uap->dmacr |= UART011_TXDMAE; - writew(uap->dmacr, - uap->port.membase + UART011_DMACR); - } - return ret; - } - - /* - * We have an X-char to send. Disable DMA to prevent it loading - * the TX fifo, and then see if we can stuff it into the FIFO. - */ - dmacr = uap->dmacr; - uap->dmacr &= ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - - if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) { - /* - * No space in the FIFO, so enable the transmit interrupt - * so we know when there is space. Note that once we've - * loaded the character, we should just re-enable DMA. - */ - return false; - } - - writew(uap->port.x_char, uap->port.membase + UART01x_DR); - uap->port.icount.tx++; - uap->port.x_char = 0; - - /* Success - restore the DMA state */ - uap->dmacr = dmacr; - writew(dmacr, uap->port.membase + UART011_DMACR); - - return true; -} - -/* - * Flush the transmit buffer. - * Locking: called with port lock held and IRQs disabled. - */ -static void pl011_dma_flush_buffer(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - if (!uap->using_dma) - return; - - /* Avoid deadlock with the DMA engine callback */ - spin_unlock(&uap->port.lock); - dmaengine_terminate_all(uap->dmatx.chan); - spin_lock(&uap->port.lock); - if (uap->dmatx.queued) { - dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1, - DMA_TO_DEVICE); - uap->dmatx.queued = false; - uap->dmacr &= ~UART011_TXDMAE; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - } -} - - -static void pl011_dma_startup(struct uart_amba_port *uap) -{ - if (!uap->dmatx.chan) - return; - - uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); - if (!uap->dmatx.buf) { - dev_err(uap->port.dev, "no memory for DMA TX buffer\n"); - uap->port.fifosize = uap->fifosize; - return; - } - - sg_init_one(&uap->dmatx.sg, uap->dmatx.buf, PL011_DMA_BUFFER_SIZE); - - /* The DMA buffer is now the FIFO the TTY subsystem can use */ - uap->port.fifosize = PL011_DMA_BUFFER_SIZE; - uap->using_dma = true; - - /* Turn on DMA error (RX/TX will be enabled on demand) */ - uap->dmacr |= UART011_DMAONERR; - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - - /* - * ST Micro variants has some specific dma burst threshold - * compensation. Set this to 16 bytes, so burst will only - * be issued above/below 16 bytes. - */ - if (uap->vendor->dma_threshold) - writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16, - uap->port.membase + ST_UART011_DMAWM); -} - -static void pl011_dma_shutdown(struct uart_amba_port *uap) -{ - if (!uap->using_dma) - return; - - /* Disable RX and TX DMA */ - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) - barrier(); - - spin_lock_irq(&uap->port.lock); - uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE); - writew(uap->dmacr, uap->port.membase + UART011_DMACR); - spin_unlock_irq(&uap->port.lock); - - /* In theory, this should already be done by pl011_dma_flush_buffer */ - dmaengine_terminate_all(uap->dmatx.chan); - if (uap->dmatx.queued) { - dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1, - DMA_TO_DEVICE); - uap->dmatx.queued = false; - } - - kfree(uap->dmatx.buf); - - uap->using_dma = false; -} - -#else -/* Blank functions if the DMA engine is not available */ -static inline void pl011_dma_probe(struct uart_amba_port *uap) -{ -} - -static inline void pl011_dma_remove(struct uart_amba_port *uap) -{ -} - -static inline void pl011_dma_startup(struct uart_amba_port *uap) -{ -} - -static inline void pl011_dma_shutdown(struct uart_amba_port *uap) -{ -} - -static inline bool pl011_dma_tx_irq(struct uart_amba_port *uap) -{ - return false; -} - -static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) -{ -} - -static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) -{ - return false; -} - -#define pl011_dma_flush_buffer NULL -#endif - - -static void pl011_stop_tx(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - uap->im &= ~UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - pl011_dma_tx_stop(uap); -} - -static void pl011_start_tx(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - if (!pl011_dma_tx_start(uap)) { - uap->im |= UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - } -} - -static void pl011_stop_rx(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM| - UART011_PEIM|UART011_BEIM|UART011_OEIM); - writew(uap->im, uap->port.membase + UART011_IMSC); -} - -static void pl011_enable_ms(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM; - writew(uap->im, uap->port.membase + UART011_IMSC); -} - -static void pl011_rx_chars(struct uart_amba_port *uap) -{ - struct tty_struct *tty = uap->port.state->port.tty; - unsigned int status, ch, flag, max_count = 256; - - status = readw(uap->port.membase + UART01x_FR); - while ((status & UART01x_FR_RXFE) == 0 && max_count--) { - ch = readw(uap->port.membase + UART01x_DR) | UART_DUMMY_DR_RX; - flag = TTY_NORMAL; - uap->port.icount.rx++; - - /* - * Note that the error handling code is - * out of the main execution path - */ - if (unlikely(ch & UART_DR_ERROR)) { - if (ch & UART011_DR_BE) { - ch &= ~(UART011_DR_FE | UART011_DR_PE); - uap->port.icount.brk++; - if (uart_handle_break(&uap->port)) - goto ignore_char; - } else if (ch & UART011_DR_PE) - uap->port.icount.parity++; - else if (ch & UART011_DR_FE) - uap->port.icount.frame++; - if (ch & UART011_DR_OE) - uap->port.icount.overrun++; - - ch &= uap->port.read_status_mask; - - if (ch & UART011_DR_BE) - flag = TTY_BREAK; - else if (ch & UART011_DR_PE) - flag = TTY_PARITY; - else if (ch & UART011_DR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&uap->port, ch & 255)) - goto ignore_char; - - uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag); - - ignore_char: - status = readw(uap->port.membase + UART01x_FR); - } - spin_unlock(&uap->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&uap->port.lock); -} - -static void pl011_tx_chars(struct uart_amba_port *uap) -{ - struct circ_buf *xmit = &uap->port.state->xmit; - int count; - - if (uap->port.x_char) { - writew(uap->port.x_char, uap->port.membase + UART01x_DR); - uap->port.icount.tx++; - uap->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { - pl011_stop_tx(&uap->port); - return; - } - - /* If we are using DMA mode, try to send some characters. */ - if (pl011_dma_tx_irq(uap)) - return; - - count = uap->fifosize >> 1; - do { - writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uap->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uap->port); - - if (uart_circ_empty(xmit)) - pl011_stop_tx(&uap->port); -} - -static void pl011_modem_status(struct uart_amba_port *uap) -{ - unsigned int status, delta; - - status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; - - delta = status ^ uap->old_status; - uap->old_status = status; - - if (!delta) - return; - - if (delta & UART01x_FR_DCD) - uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); - - if (delta & UART01x_FR_DSR) - uap->port.icount.dsr++; - - if (delta & UART01x_FR_CTS) - uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); - - wake_up_interruptible(&uap->port.state->port.delta_msr_wait); -} - -static irqreturn_t pl011_int(int irq, void *dev_id) -{ - struct uart_amba_port *uap = dev_id; - unsigned long flags; - unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; - int handled = 0; - - spin_lock_irqsave(&uap->port.lock, flags); - - status = readw(uap->port.membase + UART011_MIS); - if (status) { - do { - writew(status & ~(UART011_TXIS|UART011_RTIS| - UART011_RXIS), - uap->port.membase + UART011_ICR); - - if (status & (UART011_RTIS|UART011_RXIS)) - pl011_rx_chars(uap); - if (status & (UART011_DSRMIS|UART011_DCDMIS| - UART011_CTSMIS|UART011_RIMIS)) - pl011_modem_status(uap); - if (status & UART011_TXIS) - pl011_tx_chars(uap); - - if (pass_counter-- == 0) - break; - - status = readw(uap->port.membase + UART011_MIS); - } while (status != 0); - handled = 1; - } - - spin_unlock_irqrestore(&uap->port.lock, flags); - - return IRQ_RETVAL(handled); -} - -static unsigned int pl01x_tx_empty(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int status = readw(uap->port.membase + UART01x_FR); - return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; -} - -static unsigned int pl01x_get_mctrl(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int result = 0; - unsigned int status = readw(uap->port.membase + UART01x_FR); - -#define TIOCMBIT(uartbit, tiocmbit) \ - if (status & uartbit) \ - result |= tiocmbit - - TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR); - TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR); - TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS); - TIOCMBIT(UART011_FR_RI, TIOCM_RNG); -#undef TIOCMBIT - return result; -} - -static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; - - cr = readw(uap->port.membase + UART011_CR); - -#define TIOCMBIT(tiocmbit, uartbit) \ - if (mctrl & tiocmbit) \ - cr |= uartbit; \ - else \ - cr &= ~uartbit - - TIOCMBIT(TIOCM_RTS, UART011_CR_RTS); - TIOCMBIT(TIOCM_DTR, UART011_CR_DTR); - TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1); - TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2); - TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE); - - if (uap->autorts) { - /* We need to disable auto-RTS if we want to turn RTS off */ - TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN); - } -#undef TIOCMBIT - - writew(cr, uap->port.membase + UART011_CR); -} - -static void pl011_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned long flags; - unsigned int lcr_h; - - spin_lock_irqsave(&uap->port.lock, flags); - lcr_h = readw(uap->port.membase + uap->lcrh_tx); - if (break_state == -1) - lcr_h |= UART01x_LCRH_BRK; - else - lcr_h &= ~UART01x_LCRH_BRK; - writew(lcr_h, uap->port.membase + uap->lcrh_tx); - spin_unlock_irqrestore(&uap->port.lock, flags); -} - -#ifdef CONFIG_CONSOLE_POLL -static int pl010_get_poll_char(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int status; - - status = readw(uap->port.membase + UART01x_FR); - if (status & UART01x_FR_RXFE) - return NO_POLL_CHAR; - - return readw(uap->port.membase + UART01x_DR); -} - -static void pl010_put_poll_char(struct uart_port *port, - unsigned char ch) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) - barrier(); - - writew(ch, uap->port.membase + UART01x_DR); -} - -#endif /* CONFIG_CONSOLE_POLL */ - -static int pl011_startup(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int cr; - int retval; - - /* - * Try to enable the clock producer. - */ - retval = clk_enable(uap->clk); - if (retval) - goto out; - - uap->port.uartclk = clk_get_rate(uap->clk); - - /* - * Allocate the IRQ - */ - retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); - if (retval) - goto clk_dis; - - writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); - - /* - * Provoke TX FIFO interrupt into asserting. - */ - cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE; - writew(cr, uap->port.membase + UART011_CR); - writew(0, uap->port.membase + UART011_FBRD); - writew(1, uap->port.membase + UART011_IBRD); - writew(0, uap->port.membase + uap->lcrh_rx); - if (uap->lcrh_tx != uap->lcrh_rx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(0, uap->port.membase + uap->lcrh_tx); - } - writew(0, uap->port.membase + UART01x_DR); - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) - barrier(); - - cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; - writew(cr, uap->port.membase + UART011_CR); - - /* Clear pending error interrupts */ - writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS, - uap->port.membase + UART011_ICR); - - /* - * initialise the old status of the modem signals - */ - uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; - - /* Startup DMA */ - pl011_dma_startup(uap); - - /* - * Finally, enable interrupts - */ - spin_lock_irq(&uap->port.lock); - uap->im = UART011_RXIM | UART011_RTIM; - writew(uap->im, uap->port.membase + UART011_IMSC); - spin_unlock_irq(&uap->port.lock); - - return 0; - - clk_dis: - clk_disable(uap->clk); - out: - return retval; -} - -static void pl011_shutdown_channel(struct uart_amba_port *uap, - unsigned int lcrh) -{ - unsigned long val; - - val = readw(uap->port.membase + lcrh); - val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); - writew(val, uap->port.membase + lcrh); -} - -static void pl011_shutdown(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - /* - * disable all interrupts - */ - spin_lock_irq(&uap->port.lock); - uap->im = 0; - writew(uap->im, uap->port.membase + UART011_IMSC); - writew(0xffff, uap->port.membase + UART011_ICR); - spin_unlock_irq(&uap->port.lock); - - pl011_dma_shutdown(uap); - - /* - * Free the interrupt - */ - free_irq(uap->port.irq, uap); - - /* - * disable the port - */ - uap->autorts = false; - writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR); - - /* - * disable break condition and fifos - */ - pl011_shutdown_channel(uap, uap->lcrh_rx); - if (uap->lcrh_rx != uap->lcrh_tx) - pl011_shutdown_channel(uap, uap->lcrh_tx); - - /* - * Shut down the clock producer - */ - clk_disable(uap->clk); -} - -static void -pl011_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned int lcr_h, old_cr; - unsigned long flags; - unsigned int baud, quot, clkdiv; - - if (uap->vendor->oversampling) - clkdiv = 8; - else - clkdiv = 16; - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, - port->uartclk / clkdiv); - - if (baud > port->uartclk/16) - quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); - else - quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); - - switch (termios->c_cflag & CSIZE) { - case CS5: - lcr_h = UART01x_LCRH_WLEN_5; - break; - case CS6: - lcr_h = UART01x_LCRH_WLEN_6; - break; - case CS7: - lcr_h = UART01x_LCRH_WLEN_7; - break; - default: // CS8 - lcr_h = UART01x_LCRH_WLEN_8; - break; - } - if (termios->c_cflag & CSTOPB) - lcr_h |= UART01x_LCRH_STP2; - if (termios->c_cflag & PARENB) { - lcr_h |= UART01x_LCRH_PEN; - if (!(termios->c_cflag & PARODD)) - lcr_h |= UART01x_LCRH_EPS; - } - if (uap->fifosize > 1) - lcr_h |= UART01x_LCRH_FEN; - - spin_lock_irqsave(&port->lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - port->read_status_mask = UART011_DR_OE | 255; - if (termios->c_iflag & INPCK) - port->read_status_mask |= UART011_DR_FE | UART011_DR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= UART011_DR_BE; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= UART011_DR_BE; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UART011_DR_OE; - } - - /* - * Ignore all characters if CREAD is not set. - */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= UART_DUMMY_DR_RX; - - if (UART_ENABLE_MS(port, termios->c_cflag)) - pl011_enable_ms(port); - - /* first, disable everything */ - old_cr = readw(port->membase + UART011_CR); - writew(0, port->membase + UART011_CR); - - if (termios->c_cflag & CRTSCTS) { - if (old_cr & UART011_CR_RTS) - old_cr |= UART011_CR_RTSEN; - - old_cr |= UART011_CR_CTSEN; - uap->autorts = true; - } else { - old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN); - uap->autorts = false; - } - - if (uap->vendor->oversampling) { - if (baud > port->uartclk / 16) - old_cr |= ST_UART011_CR_OVSFACT; - else - old_cr &= ~ST_UART011_CR_OVSFACT; - } - - /* Set baud rate */ - writew(quot & 0x3f, port->membase + UART011_FBRD); - writew(quot >> 6, port->membase + UART011_IBRD); - - /* - * ----------v----------v----------v----------v----- - * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L - * ----------^----------^----------^----------^----- - */ - writew(lcr_h, port->membase + uap->lcrh_rx); - if (uap->lcrh_rx != uap->lcrh_tx) { - int i; - /* - * Wait 10 PCLKs before writing LCRH_TX register, - * to get this delay write read only register 10 times - */ - for (i = 0; i < 10; ++i) - writew(0xff, uap->port.membase + UART011_MIS); - writew(lcr_h, port->membase + uap->lcrh_tx); - } - writew(old_cr, port->membase + UART011_CR); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *pl011_type(struct uart_port *port) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - return uap->port.type == PORT_AMBA ? uap->type : NULL; -} - -/* - * Release the memory region(s) being used by 'port' - */ -static void pl010_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, SZ_4K); -} - -/* - * Request the memory region(s) being used by 'port' - */ -static int pl010_request_port(struct uart_port *port) -{ - return request_mem_region(port->mapbase, SZ_4K, "uart-pl011") - != NULL ? 0 : -EBUSY; -} - -/* - * Configure/autoconfigure the port. - */ -static void pl010_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_AMBA; - pl010_request_port(port); - } -} - -/* - * verify the new serial_struct (for TIOCSSERIAL). - */ -static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - int ret = 0; - if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) - ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) - ret = -EINVAL; - if (ser->baud_base < 9600) - ret = -EINVAL; - return ret; -} - -static struct uart_ops amba_pl011_pops = { - .tx_empty = pl01x_tx_empty, - .set_mctrl = pl011_set_mctrl, - .get_mctrl = pl01x_get_mctrl, - .stop_tx = pl011_stop_tx, - .start_tx = pl011_start_tx, - .stop_rx = pl011_stop_rx, - .enable_ms = pl011_enable_ms, - .break_ctl = pl011_break_ctl, - .startup = pl011_startup, - .shutdown = pl011_shutdown, - .flush_buffer = pl011_dma_flush_buffer, - .set_termios = pl011_set_termios, - .type = pl011_type, - .release_port = pl010_release_port, - .request_port = pl010_request_port, - .config_port = pl010_config_port, - .verify_port = pl010_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = pl010_get_poll_char, - .poll_put_char = pl010_put_poll_char, -#endif -}; - -static struct uart_amba_port *amba_ports[UART_NR]; - -#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE - -static void pl011_console_putchar(struct uart_port *port, int ch) -{ - struct uart_amba_port *uap = (struct uart_amba_port *)port; - - while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) - barrier(); - writew(ch, uap->port.membase + UART01x_DR); -} - -static void -pl011_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_amba_port *uap = amba_ports[co->index]; - unsigned int status, old_cr, new_cr; - - clk_enable(uap->clk); - - /* - * First save the CR then disable the interrupts - */ - old_cr = readw(uap->port.membase + UART011_CR); - new_cr = old_cr & ~UART011_CR_CTSEN; - new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; - writew(new_cr, uap->port.membase + UART011_CR); - - uart_console_write(&uap->port, s, count, pl011_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the TCR - */ - do { - status = readw(uap->port.membase + UART01x_FR); - } while (status & UART01x_FR_BUSY); - writew(old_cr, uap->port.membase + UART011_CR); - - clk_disable(uap->clk); -} - -static void __init -pl011_console_get_options(struct uart_amba_port *uap, int *baud, - int *parity, int *bits) -{ - if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) { - unsigned int lcr_h, ibrd, fbrd; - - lcr_h = readw(uap->port.membase + uap->lcrh_tx); - - *parity = 'n'; - if (lcr_h & UART01x_LCRH_PEN) { - if (lcr_h & UART01x_LCRH_EPS) - *parity = 'e'; - else - *parity = 'o'; - } - - if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) - *bits = 7; - else - *bits = 8; - - ibrd = readw(uap->port.membase + UART011_IBRD); - fbrd = readw(uap->port.membase + UART011_FBRD); - - *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); - - if (uap->vendor->oversampling) { - if (readw(uap->port.membase + UART011_CR) - & ST_UART011_CR_OVSFACT) - *baud *= 2; - } - } -} - -static int __init pl011_console_setup(struct console *co, char *options) -{ - struct uart_amba_port *uap; - 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. - */ - if (co->index >= UART_NR) - co->index = 0; - uap = amba_ports[co->index]; - if (!uap) - return -ENODEV; - - uap->port.uartclk = clk_get_rate(uap->clk); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - pl011_console_get_options(uap, &baud, &parity, &bits); - - return uart_set_options(&uap->port, co, baud, parity, bits, flow); -} - -static struct uart_driver amba_reg; -static struct console amba_console = { - .name = "ttyAMA", - .write = pl011_console_write, - .device = uart_console_device, - .setup = pl011_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &amba_reg, -}; - -#define AMBA_CONSOLE (&amba_console) -#else -#define AMBA_CONSOLE NULL -#endif - -static struct uart_driver amba_reg = { - .owner = THIS_MODULE, - .driver_name = "ttyAMA", - .dev_name = "ttyAMA", - .major = SERIAL_AMBA_MAJOR, - .minor = SERIAL_AMBA_MINOR, - .nr = UART_NR, - .cons = AMBA_CONSOLE, -}; - -static int pl011_probe(struct amba_device *dev, struct amba_id *id) -{ - struct uart_amba_port *uap; - struct vendor_data *vendor = id->data; - void __iomem *base; - int i, ret; - - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == NULL) - break; - - if (i == ARRAY_SIZE(amba_ports)) { - ret = -EBUSY; - goto out; - } - - uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); - if (uap == NULL) { - ret = -ENOMEM; - goto out; - } - - base = ioremap(dev->res.start, resource_size(&dev->res)); - if (!base) { - ret = -ENOMEM; - goto free; - } - - uap->clk = clk_get(&dev->dev, NULL); - if (IS_ERR(uap->clk)) { - ret = PTR_ERR(uap->clk); - goto unmap; - } - - uap->vendor = vendor; - uap->lcrh_rx = vendor->lcrh_rx; - uap->lcrh_tx = vendor->lcrh_tx; - uap->fifosize = vendor->fifosize; - uap->port.dev = &dev->dev; - uap->port.mapbase = dev->res.start; - uap->port.membase = base; - uap->port.iotype = UPIO_MEM; - uap->port.irq = dev->irq[0]; - uap->port.fifosize = uap->fifosize; - uap->port.ops = &amba_pl011_pops; - uap->port.flags = UPF_BOOT_AUTOCONF; - uap->port.line = i; - pl011_dma_probe(uap); - - snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); - - amba_ports[i] = uap; - - amba_set_drvdata(dev, uap); - ret = uart_add_one_port(&amba_reg, &uap->port); - if (ret) { - 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; -} - -static int pl011_remove(struct amba_device *dev) -{ - struct uart_amba_port *uap = amba_get_drvdata(dev); - int i; - - amba_set_drvdata(dev, NULL); - - uart_remove_one_port(&amba_reg, &uap->port); - - for (i = 0; i < ARRAY_SIZE(amba_ports); i++) - if (amba_ports[i] == uap) - amba_ports[i] = NULL; - - pl011_dma_remove(uap); - iounmap(uap->port.membase); - clk_put(uap->clk); - kfree(uap); - return 0; -} - -#ifdef CONFIG_PM -static int pl011_suspend(struct amba_device *dev, pm_message_t state) -{ - struct uart_amba_port *uap = amba_get_drvdata(dev); - - if (!uap) - return -EINVAL; - - return uart_suspend_port(&amba_reg, &uap->port); -} - -static int pl011_resume(struct amba_device *dev) -{ - struct uart_amba_port *uap = amba_get_drvdata(dev); - - if (!uap) - return -EINVAL; - - return uart_resume_port(&amba_reg, &uap->port); -} -#endif - -static struct amba_id pl011_ids[] = { - { - .id = 0x00041011, - .mask = 0x000fffff, - .data = &vendor_arm, - }, - { - .id = 0x00380802, - .mask = 0x00ffffff, - .data = &vendor_st, - }, - { 0, 0 }, -}; - -static struct amba_driver pl011_driver = { - .drv = { - .name = "uart-pl011", - }, - .id_table = pl011_ids, - .probe = pl011_probe, - .remove = pl011_remove, -#ifdef CONFIG_PM - .suspend = pl011_suspend, - .resume = pl011_resume, -#endif -}; - -static int __init pl011_init(void) -{ - int ret; - printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); - - ret = uart_register_driver(&amba_reg); - if (ret == 0) { - ret = amba_driver_register(&pl011_driver); - if (ret) - uart_unregister_driver(&amba_reg); - } - return ret; -} - -static void __exit pl011_exit(void) -{ - amba_driver_unregister(&pl011_driver); - uart_unregister_driver(&amba_reg); -} - -/* - * While this can be a module, if builtin it's most likely the console - * So let's leave module_exit but move module_init to an earlier place - */ -arch_initcall(pl011_init); -module_exit(pl011_exit); - -MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("ARM AMBA serial port driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c deleted file mode 100644 index 095a5d562618..000000000000 --- a/drivers/serial/apbuart.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * Driver for GRLIB serial ports (APBUART) - * - * Based on linux/drivers/serial/amba.c - * - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * Copyright (C) 2003 Konrad Eisele - * Copyright (C) 2006 Daniel Hellstrom , Aeroflex Gaisler AB - * Copyright (C) 2008 Gilead Kutnick - * Copyright (C) 2009 Kristoffer Glembo , Aeroflex Gaisler AB - */ - -#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "apbuart.h" - -#define SERIAL_APBUART_MAJOR TTY_MAJOR -#define SERIAL_APBUART_MINOR 64 -#define UART_DUMMY_RSR_RX 0x8000 /* for ignore all read */ - -static void apbuart_tx_chars(struct uart_port *port); - -static void apbuart_stop_tx(struct uart_port *port) -{ - unsigned int cr; - - cr = UART_GET_CTRL(port); - cr &= ~UART_CTRL_TI; - UART_PUT_CTRL(port, cr); -} - -static void apbuart_start_tx(struct uart_port *port) -{ - unsigned int cr; - - cr = UART_GET_CTRL(port); - cr |= UART_CTRL_TI; - UART_PUT_CTRL(port, cr); - - if (UART_GET_STATUS(port) & UART_STATUS_THE) - apbuart_tx_chars(port); -} - -static void apbuart_stop_rx(struct uart_port *port) -{ - unsigned int cr; - - cr = UART_GET_CTRL(port); - cr &= ~(UART_CTRL_RI); - UART_PUT_CTRL(port, cr); -} - -static void apbuart_enable_ms(struct uart_port *port) -{ - /* No modem status change interrupts for APBUART */ -} - -static void apbuart_rx_chars(struct uart_port *port) -{ - struct tty_struct *tty = port->state->port.tty; - unsigned int status, ch, rsr, flag; - unsigned int max_chars = port->fifosize; - - status = UART_GET_STATUS(port); - - while (UART_RX_DATA(status) && (max_chars--)) { - - ch = UART_GET_CHAR(port); - flag = TTY_NORMAL; - - port->icount.rx++; - - rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX; - UART_PUT_STATUS(port, 0); - if (rsr & UART_STATUS_ERR) { - - if (rsr & UART_STATUS_BR) { - rsr &= ~(UART_STATUS_FE | UART_STATUS_PE); - port->icount.brk++; - if (uart_handle_break(port)) - goto ignore_char; - } else if (rsr & UART_STATUS_PE) { - port->icount.parity++; - } else if (rsr & UART_STATUS_FE) { - port->icount.frame++; - } - if (rsr & UART_STATUS_OE) - port->icount.overrun++; - - rsr &= port->read_status_mask; - - if (rsr & UART_STATUS_PE) - flag = TTY_PARITY; - else if (rsr & UART_STATUS_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; - - uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag); - - - ignore_char: - status = UART_GET_STATUS(port); - } - - tty_flip_buffer_push(tty); -} - -static void apbuart_tx_chars(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - int count; - - if (port->x_char) { - UART_PUT_CHAR(port, port->x_char); - port->icount.tx++; - port->x_char = 0; - return; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - apbuart_stop_tx(port); - return; - } - - /* amba: fill FIFO */ - count = port->fifosize >> 1; - do { - UART_PUT_CHAR(port, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - apbuart_stop_tx(port); -} - -static irqreturn_t apbuart_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - unsigned int status; - - spin_lock(&port->lock); - - status = UART_GET_STATUS(port); - if (status & UART_STATUS_DR) - apbuart_rx_chars(port); - if (status & UART_STATUS_THE) - apbuart_tx_chars(port); - - spin_unlock(&port->lock); - - return IRQ_HANDLED; -} - -static unsigned int apbuart_tx_empty(struct uart_port *port) -{ - unsigned int status = UART_GET_STATUS(port); - return status & UART_STATUS_THE ? TIOCSER_TEMT : 0; -} - -static unsigned int apbuart_get_mctrl(struct uart_port *port) -{ - /* The GRLIB APBUART handles flow control in hardware */ - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; -} - -static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* The GRLIB APBUART handles flow control in hardware */ -} - -static void apbuart_break_ctl(struct uart_port *port, int break_state) -{ - /* We don't support sending break */ -} - -static int apbuart_startup(struct uart_port *port) -{ - int retval; - unsigned int cr; - - /* Allocate the IRQ */ - retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port); - if (retval) - return retval; - - /* Finally, enable interrupts */ - cr = UART_GET_CTRL(port); - UART_PUT_CTRL(port, - cr | UART_CTRL_RE | UART_CTRL_TE | - UART_CTRL_RI | UART_CTRL_TI); - - return 0; -} - -static void apbuart_shutdown(struct uart_port *port) -{ - unsigned int cr; - - /* disable all interrupts, disable the port */ - cr = UART_GET_CTRL(port); - UART_PUT_CTRL(port, - cr & ~(UART_CTRL_RE | UART_CTRL_TE | - UART_CTRL_RI | UART_CTRL_TI)); - - /* Free the interrupt */ - free_irq(port->irq, port); -} - -static void apbuart_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - unsigned int cr; - unsigned long flags; - unsigned int baud, quot; - - /* Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - if (baud == 0) - panic("invalid baudrate %i\n", port->uartclk / 16); - - /* uart_get_divisor calc a *16 uart freq, apbuart is *8 */ - quot = (uart_get_divisor(port, baud)) * 2; - cr = UART_GET_CTRL(port); - cr &= ~(UART_CTRL_PE | UART_CTRL_PS); - - if (termios->c_cflag & PARENB) { - cr |= UART_CTRL_PE; - if ((termios->c_cflag & PARODD)) - cr |= UART_CTRL_PS; - } - - /* Enable flow control. */ - if (termios->c_cflag & CRTSCTS) - cr |= UART_CTRL_FL; - - spin_lock_irqsave(&port->lock, flags); - - /* Update the per-port timeout. */ - uart_update_timeout(port, termios->c_cflag, baud); - - port->read_status_mask = UART_STATUS_OE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE; - - /* Characters to ignore */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE; - - /* Ignore all characters if CREAD is not set. */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= UART_DUMMY_RSR_RX; - - /* Set baud rate */ - quot -= 1; - UART_PUT_SCAL(port, quot); - UART_PUT_CTRL(port, cr); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *apbuart_type(struct uart_port *port) -{ - return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL; -} - -static void apbuart_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, 0x100); -} - -static int apbuart_request_port(struct uart_port *port) -{ - return request_mem_region(port->mapbase, 0x100, "grlib-apbuart") - != NULL ? 0 : -EBUSY; - return 0; -} - -/* Configure/autoconfigure the port */ -static void apbuart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_APBUART; - apbuart_request_port(port); - } -} - -/* Verify the new serial_struct (for TIOCSSERIAL) */ -static int apbuart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - int ret = 0; - if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART) - ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= NR_IRQS) - ret = -EINVAL; - if (ser->baud_base < 9600) - ret = -EINVAL; - return ret; -} - -static struct uart_ops grlib_apbuart_ops = { - .tx_empty = apbuart_tx_empty, - .set_mctrl = apbuart_set_mctrl, - .get_mctrl = apbuart_get_mctrl, - .stop_tx = apbuart_stop_tx, - .start_tx = apbuart_start_tx, - .stop_rx = apbuart_stop_rx, - .enable_ms = apbuart_enable_ms, - .break_ctl = apbuart_break_ctl, - .startup = apbuart_startup, - .shutdown = apbuart_shutdown, - .set_termios = apbuart_set_termios, - .type = apbuart_type, - .release_port = apbuart_release_port, - .request_port = apbuart_request_port, - .config_port = apbuart_config_port, - .verify_port = apbuart_verify_port, -}; - -static struct uart_port grlib_apbuart_ports[UART_NR]; -static struct device_node *grlib_apbuart_nodes[UART_NR]; - -static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber) -{ - int ctrl, loop = 0; - int status; - int fifosize; - unsigned long flags; - - ctrl = UART_GET_CTRL(port); - - /* - * Enable the transceiver and wait for it to be ready to send data. - * Clear interrupts so that this process will not be externally - * interrupted in the middle (which can cause the transceiver to - * drain prematurely). - */ - - local_irq_save(flags); - - UART_PUT_CTRL(port, ctrl | UART_CTRL_TE); - - while (!UART_TX_READY(UART_GET_STATUS(port))) - loop++; - - /* - * Disable the transceiver so data isn't actually sent during the - * actual test. - */ - - UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE)); - - fifosize = 1; - UART_PUT_CHAR(port, 0); - - /* - * So long as transmitting a character increments the tranceivier FIFO - * length the FIFO must be at least that big. These bytes will - * automatically drain off of the FIFO. - */ - - status = UART_GET_STATUS(port); - while (((status >> 20) & 0x3F) == fifosize) { - fifosize++; - UART_PUT_CHAR(port, 0); - status = UART_GET_STATUS(port); - } - - fifosize--; - - UART_PUT_CTRL(port, ctrl); - local_irq_restore(flags); - - if (fifosize == 0) - fifosize = 1; - - return fifosize; -} - -static void apbuart_flush_fifo(struct uart_port *port) -{ - int i; - - for (i = 0; i < port->fifosize; i++) - UART_GET_CHAR(port); -} - - -/* ======================================================================== */ -/* Console driver, if enabled */ -/* ======================================================================== */ - -#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE - -static void apbuart_console_putchar(struct uart_port *port, int ch) -{ - unsigned int status; - do { - status = UART_GET_STATUS(port); - } while (!UART_TX_READY(status)); - UART_PUT_CHAR(port, ch); -} - -static void -apbuart_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_port *port = &grlib_apbuart_ports[co->index]; - unsigned int status, old_cr, new_cr; - - /* First save the CR then disable the interrupts */ - old_cr = UART_GET_CTRL(port); - new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI); - UART_PUT_CTRL(port, new_cr); - - uart_console_write(port, s, count, apbuart_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the TCR - */ - do { - status = UART_GET_STATUS(port); - } while (!UART_TX_READY(status)); - UART_PUT_CTRL(port, old_cr); -} - -static void __init -apbuart_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) -{ - if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) { - - unsigned int quot, status; - status = UART_GET_STATUS(port); - - *parity = 'n'; - if (status & UART_CTRL_PE) { - if ((status & UART_CTRL_PS) == 0) - *parity = 'e'; - else - *parity = 'o'; - } - - *bits = 8; - quot = UART_GET_SCAL(port) / 8; - *baud = port->uartclk / (16 * (quot + 1)); - } -} - -static int __init apbuart_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n", - co, co->index, options); - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= grlib_apbuart_port_nr) - co->index = 0; - - port = &grlib_apbuart_ports[co->index]; - - spin_lock_init(&port->lock); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - apbuart_console_get_options(port, &baud, &parity, &bits); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver grlib_apbuart_driver; - -static struct console grlib_apbuart_console = { - .name = "ttyS", - .write = apbuart_console_write, - .device = uart_console_device, - .setup = apbuart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &grlib_apbuart_driver, -}; - - -static int grlib_apbuart_configure(void); - -static int __init apbuart_console_init(void) -{ - if (grlib_apbuart_configure()) - return -ENODEV; - register_console(&grlib_apbuart_console); - return 0; -} - -console_initcall(apbuart_console_init); - -#define APBUART_CONSOLE (&grlib_apbuart_console) -#else -#define APBUART_CONSOLE NULL -#endif - -static struct uart_driver grlib_apbuart_driver = { - .owner = THIS_MODULE, - .driver_name = "serial", - .dev_name = "ttyS", - .major = SERIAL_APBUART_MAJOR, - .minor = SERIAL_APBUART_MINOR, - .nr = UART_NR, - .cons = APBUART_CONSOLE, -}; - - -/* ======================================================================== */ -/* OF Platform Driver */ -/* ======================================================================== */ - -static int __devinit apbuart_probe(struct platform_device *op, - const struct of_device_id *match) -{ - int i = -1; - struct uart_port *port = NULL; - - i = 0; - for (i = 0; i < grlib_apbuart_port_nr; i++) { - if (op->dev.of_node == grlib_apbuart_nodes[i]) - break; - } - - port = &grlib_apbuart_ports[i]; - port->dev = &op->dev; - - uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port); - - apbuart_flush_fifo((struct uart_port *) port); - - printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n", - (unsigned long long) port->mapbase, port->irq); - return 0; -} - -static struct of_device_id __initdata apbuart_match[] = { - { - .name = "GAISLER_APBUART", - }, - { - .name = "01_00c", - }, - {}, -}; - -static struct of_platform_driver grlib_apbuart_of_driver = { - .probe = apbuart_probe, - .driver = { - .owner = THIS_MODULE, - .name = "grlib-apbuart", - .of_match_table = apbuart_match, - }, -}; - - -static int grlib_apbuart_configure(void) -{ - struct device_node *np, *rp; - const u32 *prop; - int freq_khz, line = 0; - - /* Get bus frequency */ - rp = of_find_node_by_path("/"); - if (!rp) - return -ENODEV; - rp = of_get_next_child(rp, NULL); - if (!rp) - return -ENODEV; - prop = of_get_property(rp, "clock-frequency", NULL); - if (!prop) - return -ENODEV; - freq_khz = *prop; - - for_each_matching_node(np, apbuart_match) { - const int *irqs, *ampopts; - const struct amba_prom_registers *regs; - struct uart_port *port; - unsigned long addr; - - ampopts = of_get_property(np, "ampopts", NULL); - if (ampopts && (*ampopts == 0)) - continue; /* Ignore if used by another OS instance */ - - irqs = of_get_property(np, "interrupts", NULL); - regs = of_get_property(np, "reg", NULL); - - if (!irqs || !regs) - continue; - - grlib_apbuart_nodes[line] = np; - - addr = regs->phys_addr; - - port = &grlib_apbuart_ports[line]; - - port->mapbase = addr; - port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map)); - port->irq = *irqs; - port->iotype = UPIO_MEM; - port->ops = &grlib_apbuart_ops; - port->flags = UPF_BOOT_AUTOCONF; - port->line = line; - port->uartclk = freq_khz * 1000; - port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line); - line++; - - /* We support maximum UART_NR uarts ... */ - if (line == UART_NR) - break; - } - - grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line; - return line ? 0 : -ENODEV; -} - -static int __init grlib_apbuart_init(void) -{ - int ret; - - /* Find all APBUARTS in device the tree and initialize their ports */ - ret = grlib_apbuart_configure(); - if (ret) - return ret; - - printk(KERN_INFO "Serial: GRLIB APBUART driver\n"); - - ret = uart_register_driver(&grlib_apbuart_driver); - - if (ret) { - printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", - __FILE__, ret); - return ret; - } - - ret = of_register_platform_driver(&grlib_apbuart_of_driver); - if (ret) { - printk(KERN_ERR - "%s: of_register_platform_driver failed (%i)\n", - __FILE__, ret); - uart_unregister_driver(&grlib_apbuart_driver); - return ret; - } - - return ret; -} - -static void __exit grlib_apbuart_exit(void) -{ - int i; - - for (i = 0; i < grlib_apbuart_port_nr; i++) - uart_remove_one_port(&grlib_apbuart_driver, - &grlib_apbuart_ports[i]); - - uart_unregister_driver(&grlib_apbuart_driver); - of_unregister_platform_driver(&grlib_apbuart_of_driver); -} - -module_init(grlib_apbuart_init); -module_exit(grlib_apbuart_exit); - -MODULE_AUTHOR("Aeroflex Gaisler AB"); -MODULE_DESCRIPTION("GRLIB APBUART serial driver"); -MODULE_VERSION("2.1"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/apbuart.h b/drivers/serial/apbuart.h deleted file mode 100644 index 5faf87c8d2bc..000000000000 --- a/drivers/serial/apbuart.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __GRLIB_APBUART_H__ -#define __GRLIB_APBUART_H__ - -#include - -#define UART_NR 8 -static int grlib_apbuart_port_nr; - -struct grlib_apbuart_regs_map { - u32 data; - u32 status; - u32 ctrl; - u32 scaler; -}; - -struct amba_prom_registers { - unsigned int phys_addr; - unsigned int reg_size; -}; - -/* - * The following defines the bits in the APBUART Status Registers. - */ -#define UART_STATUS_DR 0x00000001 /* Data Ready */ -#define UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */ -#define UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */ -#define UART_STATUS_BR 0x00000008 /* Break Error */ -#define UART_STATUS_OE 0x00000010 /* RX Overrun Error */ -#define UART_STATUS_PE 0x00000020 /* RX Parity Error */ -#define UART_STATUS_FE 0x00000040 /* RX Framing Error */ -#define UART_STATUS_ERR 0x00000078 /* Error Mask */ - -/* - * The following defines the bits in the APBUART Ctrl Registers. - */ -#define UART_CTRL_RE 0x00000001 /* Receiver enable */ -#define UART_CTRL_TE 0x00000002 /* Transmitter enable */ -#define UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */ -#define UART_CTRL_TI 0x00000008 /* Transmitter irq */ -#define UART_CTRL_PS 0x00000010 /* Parity select */ -#define UART_CTRL_PE 0x00000020 /* Parity enable */ -#define UART_CTRL_FL 0x00000040 /* Flow control enable */ -#define UART_CTRL_LB 0x00000080 /* Loopback enable */ - -#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase)) - -#define APBBASE_DATA_P(port) (&(APBBASE(port)->data)) -#define APBBASE_STATUS_P(port) (&(APBBASE(port)->status)) -#define APBBASE_CTRL_P(port) (&(APBBASE(port)->ctrl)) -#define APBBASE_SCALAR_P(port) (&(APBBASE(port)->scaler)) - -#define UART_GET_CHAR(port) (__raw_readl(APBBASE_DATA_P(port))) -#define UART_PUT_CHAR(port, v) (__raw_writel(v, APBBASE_DATA_P(port))) -#define UART_GET_STATUS(port) (__raw_readl(APBBASE_STATUS_P(port))) -#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port))) -#define UART_GET_CTRL(port) (__raw_readl(APBBASE_CTRL_P(port))) -#define UART_PUT_CTRL(port, v) (__raw_writel(v, APBBASE_CTRL_P(port))) -#define UART_GET_SCAL(port) (__raw_readl(APBBASE_SCALAR_P(port))) -#define UART_PUT_SCAL(port, v) (__raw_writel(v, APBBASE_SCALAR_P(port))) - -#define UART_RX_DATA(s) (((s) & UART_STATUS_DR) != 0) -#define UART_TX_READY(s) (((s) & UART_STATUS_THE) != 0) - -#endif /* __GRLIB_APBUART_H__ */ diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c deleted file mode 100644 index 3892666b5fbd..000000000000 --- a/drivers/serial/atmel_serial.c +++ /dev/null @@ -1,1808 +0,0 @@ -/* - * linux/drivers/char/atmel_serial.c - * - * Driver for Atmel AT91 / AT32 Serial ports - * Copyright (C) 2003 Rick Bronson - * - * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * DMA support added by Chip Coldwell. - * - * 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 -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#ifdef CONFIG_ARM -#include -#include -#endif - -#define PDC_BUFFER_SIZE 512 -/* Revisit: We should calculate this based on the actual port settings */ -#define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ - -#if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -static void atmel_start_rx(struct uart_port *port); -static void atmel_stop_rx(struct uart_port *port); - -#ifdef CONFIG_SERIAL_ATMEL_TTYAT - -/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we - * should coexist with the 8250 driver, such as if we have an external 16C550 - * UART. */ -#define SERIAL_ATMEL_MAJOR 204 -#define MINOR_START 154 -#define ATMEL_DEVICENAME "ttyAT" - -#else - -/* Use device name ttyS, major 4, minor 64-68. This is the usual serial port - * name, but it is legally reserved for the 8250 driver. */ -#define SERIAL_ATMEL_MAJOR TTY_MAJOR -#define MINOR_START 64 -#define ATMEL_DEVICENAME "ttyS" - -#endif - -#define ATMEL_ISR_PASS_LIMIT 256 - -/* UART registers. CR is write-only, hence no GET macro */ -#define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR) -#define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR) -#define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR) -#define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER) -#define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR) -#define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR) -#define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR) -#define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR) -#define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR) -#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) -#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) -#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) -#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) - - /* PDC registers */ -#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) -#define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR) - -#define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR) -#define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR) -#define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR) -#define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR) -#define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR) - -#define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR) -#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR) -#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR) - -static int (*atmel_open_hook)(struct uart_port *); -static void (*atmel_close_hook)(struct uart_port *); - -struct atmel_dma_buffer { - unsigned char *buf; - dma_addr_t dma_addr; - unsigned int dma_size; - unsigned int ofs; -}; - -struct atmel_uart_char { - u16 status; - u16 ch; -}; - -#define ATMEL_SERIAL_RINGSIZE 1024 - -/* - * We wrap our port structure around the generic uart_port. - */ -struct atmel_uart_port { - struct uart_port uart; /* uart */ - struct clk *clk; /* uart clock */ - int may_wakeup; /* cached value of device_may_wakeup for times we need to disable it */ - u32 backup_imr; /* IMR saved during suspend */ - int break_active; /* break being received */ - - short use_dma_rx; /* enable PDC receiver */ - short pdc_rx_idx; /* current PDC RX buffer */ - struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ - - short use_dma_tx; /* enable PDC transmitter */ - struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ - - struct tasklet_struct tasklet; - unsigned int irq_status; - unsigned int irq_status_prev; - - struct circ_buf rx_ring; - - struct serial_rs485 rs485; /* rs485 settings */ - unsigned int tx_done_mask; -}; - -static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; - -#ifdef SUPPORT_SYSRQ -static struct console atmel_console; -#endif - -static inline struct atmel_uart_port * -to_atmel_uart_port(struct uart_port *uart) -{ - return container_of(uart, struct atmel_uart_port, uart); -} - -#ifdef CONFIG_SERIAL_ATMEL_PDC -static bool atmel_use_dma_rx(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - return atmel_port->use_dma_rx; -} - -static bool atmel_use_dma_tx(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - return atmel_port->use_dma_tx; -} -#else -static bool atmel_use_dma_rx(struct uart_port *port) -{ - return false; -} - -static bool atmel_use_dma_tx(struct uart_port *port) -{ - return false; -} -#endif - -/* Enable or disable the rs485 support */ -void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - unsigned int mode; - - spin_lock(&port->lock); - - /* Disable interrupts */ - UART_PUT_IDR(port, atmel_port->tx_done_mask); - - mode = UART_GET_MR(port); - - /* Resetting serial mode to RS232 (0x0) */ - mode &= ~ATMEL_US_USMODE; - - atmel_port->rs485 = *rs485conf; - - if (rs485conf->flags & SER_RS485_ENABLED) { - dev_dbg(port->dev, "Setting UART to RS485\n"); - atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; - if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) - UART_PUT_TTGR(port, rs485conf->delay_rts_after_send); - mode |= ATMEL_US_USMODE_RS485; - } else { - dev_dbg(port->dev, "Setting UART to RS232\n"); - if (atmel_use_dma_tx(port)) - atmel_port->tx_done_mask = ATMEL_US_ENDTX | - ATMEL_US_TXBUFE; - else - atmel_port->tx_done_mask = ATMEL_US_TXRDY; - } - UART_PUT_MR(port, mode); - - /* Enable interrupts */ - UART_PUT_IER(port, atmel_port->tx_done_mask); - - spin_unlock(&port->lock); - -} - -/* - * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. - */ -static u_int atmel_tx_empty(struct uart_port *port) -{ - return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0; -} - -/* - * Set state of the modem control output lines - */ -static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) -{ - unsigned int control = 0; - unsigned int mode; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - -#ifdef CONFIG_ARCH_AT91RM9200 - if (cpu_is_at91rm9200()) { - /* - * AT91RM9200 Errata #39: RTS0 is not internally connected - * to PA21. We need to drive the pin manually. - */ - if (port->mapbase == AT91RM9200_BASE_US0) { - if (mctrl & TIOCM_RTS) - at91_set_gpio_value(AT91_PIN_PA21, 0); - else - at91_set_gpio_value(AT91_PIN_PA21, 1); - } - } -#endif - - if (mctrl & TIOCM_RTS) - control |= ATMEL_US_RTSEN; - else - control |= ATMEL_US_RTSDIS; - - if (mctrl & TIOCM_DTR) - control |= ATMEL_US_DTREN; - else - control |= ATMEL_US_DTRDIS; - - UART_PUT_CR(port, control); - - /* Local loopback mode? */ - mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE; - if (mctrl & TIOCM_LOOP) - mode |= ATMEL_US_CHMODE_LOC_LOOP; - else - mode |= ATMEL_US_CHMODE_NORMAL; - - /* Resetting serial mode to RS232 (0x0) */ - mode &= ~ATMEL_US_USMODE; - - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { - dev_dbg(port->dev, "Setting UART to RS485\n"); - if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - UART_PUT_TTGR(port, - atmel_port->rs485.delay_rts_after_send); - mode |= ATMEL_US_USMODE_RS485; - } else { - dev_dbg(port->dev, "Setting UART to RS232\n"); - } - UART_PUT_MR(port, mode); -} - -/* - * Get state of the modem control input lines - */ -static u_int atmel_get_mctrl(struct uart_port *port) -{ - unsigned int status, ret = 0; - - status = UART_GET_CSR(port); - - /* - * The control signals are active low. - */ - if (!(status & ATMEL_US_DCD)) - ret |= TIOCM_CD; - if (!(status & ATMEL_US_CTS)) - ret |= TIOCM_CTS; - if (!(status & ATMEL_US_DSR)) - ret |= TIOCM_DSR; - if (!(status & ATMEL_US_RI)) - ret |= TIOCM_RI; - - return ret; -} - -/* - * Stop transmitting. - */ -static void atmel_stop_tx(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_use_dma_tx(port)) { - /* disable PDC transmit */ - UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - } - /* Disable interrupts */ - UART_PUT_IDR(port, atmel_port->tx_done_mask); - - if (atmel_port->rs485.flags & SER_RS485_ENABLED) - atmel_start_rx(port); -} - -/* - * Start transmitting. - */ -static void atmel_start_tx(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_use_dma_tx(port)) { - if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) - /* The transmitter is already running. Yes, we - really need this.*/ - return; - - if (atmel_port->rs485.flags & SER_RS485_ENABLED) - atmel_stop_rx(port); - - /* re-enable PDC transmit */ - UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - } - /* Enable interrupts */ - UART_PUT_IER(port, atmel_port->tx_done_mask); -} - -/* - * start receiving - port is in process of being opened. - */ -static void atmel_start_rx(struct uart_port *port) -{ - UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ - - if (atmel_use_dma_rx(port)) { - /* enable PDC controller */ - UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | - port->read_status_mask); - UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); - } else { - UART_PUT_IER(port, ATMEL_US_RXRDY); - } -} - -/* - * Stop receiving - port is in process of being closed. - */ -static void atmel_stop_rx(struct uart_port *port) -{ - if (atmel_use_dma_rx(port)) { - /* disable PDC receive */ - UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | - port->read_status_mask); - } else { - UART_PUT_IDR(port, ATMEL_US_RXRDY); - } -} - -/* - * Enable modem status interrupts - */ -static void atmel_enable_ms(struct uart_port *port) -{ - UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC - | ATMEL_US_DCDIC | ATMEL_US_CTSIC); -} - -/* - * Control the transmission of a break signal - */ -static void atmel_break_ctl(struct uart_port *port, int break_state) -{ - if (break_state != 0) - UART_PUT_CR(port, ATMEL_US_STTBRK); /* start break */ - else - UART_PUT_CR(port, ATMEL_US_STPBRK); /* stop break */ -} - -/* - * Stores the incoming character in the ring buffer - */ -static void -atmel_buffer_rx_char(struct uart_port *port, unsigned int status, - unsigned int ch) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct circ_buf *ring = &atmel_port->rx_ring; - struct atmel_uart_char *c; - - if (!CIRC_SPACE(ring->head, ring->tail, ATMEL_SERIAL_RINGSIZE)) - /* Buffer overflow, ignore char */ - return; - - c = &((struct atmel_uart_char *)ring->buf)[ring->head]; - c->status = status; - c->ch = ch; - - /* Make sure the character is stored before we update head. */ - smp_wmb(); - - ring->head = (ring->head + 1) & (ATMEL_SERIAL_RINGSIZE - 1); -} - -/* - * Deal with parity, framing and overrun errors. - */ -static void atmel_pdc_rxerr(struct uart_port *port, unsigned int status) -{ - /* clear error */ - UART_PUT_CR(port, ATMEL_US_RSTSTA); - - if (status & ATMEL_US_RXBRK) { - /* ignore side-effect */ - status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); - port->icount.brk++; - } - if (status & ATMEL_US_PARE) - port->icount.parity++; - if (status & ATMEL_US_FRAME) - port->icount.frame++; - if (status & ATMEL_US_OVRE) - port->icount.overrun++; -} - -/* - * Characters received (called from interrupt handler) - */ -static void atmel_rx_chars(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - unsigned int status, ch; - - status = UART_GET_CSR(port); - while (status & ATMEL_US_RXRDY) { - ch = UART_GET_CHAR(port); - - /* - * note that the error handling code is - * out of the main execution path - */ - if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME - | ATMEL_US_OVRE | ATMEL_US_RXBRK) - || atmel_port->break_active)) { - - /* clear error */ - UART_PUT_CR(port, ATMEL_US_RSTSTA); - - if (status & ATMEL_US_RXBRK - && !atmel_port->break_active) { - atmel_port->break_active = 1; - UART_PUT_IER(port, ATMEL_US_RXBRK); - } else { - /* - * This is either the end-of-break - * condition or we've received at - * least one character without RXBRK - * being set. In both cases, the next - * RXBRK will indicate start-of-break. - */ - UART_PUT_IDR(port, ATMEL_US_RXBRK); - status &= ~ATMEL_US_RXBRK; - atmel_port->break_active = 0; - } - } - - atmel_buffer_rx_char(port, status, ch); - status = UART_GET_CSR(port); - } - - tasklet_schedule(&atmel_port->tasklet); -} - -/* - * Transmit characters (called from tasklet with TXRDY interrupt - * disabled) - */ -static void atmel_tx_chars(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { - UART_PUT_CHAR(port, port->x_char); - port->icount.tx++; - port->x_char = 0; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - return; - - while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { - UART_PUT_CHAR(port, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (!uart_circ_empty(xmit)) - /* Enable interrupts */ - UART_PUT_IER(port, atmel_port->tx_done_mask); -} - -/* - * receive interrupt handler. - */ -static void -atmel_handle_receive(struct uart_port *port, unsigned int pending) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_use_dma_rx(port)) { - /* - * PDC receive. Just schedule the tasklet and let it - * figure out the details. - * - * TODO: We're not handling error flags correctly at - * the moment. - */ - if (pending & (ATMEL_US_ENDRX | ATMEL_US_TIMEOUT)) { - UART_PUT_IDR(port, (ATMEL_US_ENDRX - | ATMEL_US_TIMEOUT)); - tasklet_schedule(&atmel_port->tasklet); - } - - if (pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | - ATMEL_US_FRAME | ATMEL_US_PARE)) - atmel_pdc_rxerr(port, pending); - } - - /* Interrupt receive */ - if (pending & ATMEL_US_RXRDY) - atmel_rx_chars(port); - else if (pending & ATMEL_US_RXBRK) { - /* - * End of break detected. If it came along with a - * character, atmel_rx_chars will handle it. - */ - UART_PUT_CR(port, ATMEL_US_RSTSTA); - UART_PUT_IDR(port, ATMEL_US_RXBRK); - atmel_port->break_active = 0; - } -} - -/* - * transmit interrupt handler. (Transmit is IRQF_NODELAY safe) - */ -static void -atmel_handle_transmit(struct uart_port *port, unsigned int pending) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (pending & atmel_port->tx_done_mask) { - /* Either PDC or interrupt transmission */ - UART_PUT_IDR(port, atmel_port->tx_done_mask); - tasklet_schedule(&atmel_port->tasklet); - } -} - -/* - * status flags interrupt handler. - */ -static void -atmel_handle_status(struct uart_port *port, unsigned int pending, - unsigned int status) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC - | ATMEL_US_CTSIC)) { - atmel_port->irq_status = status; - tasklet_schedule(&atmel_port->tasklet); - } -} - -/* - * Interrupt handler - */ -static irqreturn_t atmel_interrupt(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - unsigned int status, pending, pass_counter = 0; - - do { - status = UART_GET_CSR(port); - pending = status & UART_GET_IMR(port); - if (!pending) - break; - - atmel_handle_receive(port, pending); - atmel_handle_status(port, pending, status); - atmel_handle_transmit(port, pending); - } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); - - return pass_counter ? IRQ_HANDLED : IRQ_NONE; -} - -/* - * Called from tasklet with ENDTX and TXBUFE interrupts disabled. - */ -static void atmel_tx_dma(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct circ_buf *xmit = &port->state->xmit; - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - int count; - - /* nothing left to transmit? */ - if (UART_GET_TCR(port)) - return; - - xmit->tail += pdc->ofs; - xmit->tail &= UART_XMIT_SIZE - 1; - - port->icount.tx += pdc->ofs; - pdc->ofs = 0; - - /* more to transmit - setup next transfer */ - - /* disable PDC transmit */ - UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - - if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { - dma_sync_single_for_device(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_TO_DEVICE); - - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - pdc->ofs = count; - - UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); - UART_PUT_TCR(port, count); - /* re-enable PDC transmit */ - UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - /* Enable interrupts */ - UART_PUT_IER(port, atmel_port->tx_done_mask); - } else { - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { - /* DMA done, stop TX, start RX for RS485 */ - atmel_start_rx(port); - } - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static void atmel_rx_from_ring(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct circ_buf *ring = &atmel_port->rx_ring; - unsigned int flg; - unsigned int status; - - while (ring->head != ring->tail) { - struct atmel_uart_char c; - - /* Make sure c is loaded after head. */ - smp_rmb(); - - c = ((struct atmel_uart_char *)ring->buf)[ring->tail]; - - ring->tail = (ring->tail + 1) & (ATMEL_SERIAL_RINGSIZE - 1); - - port->icount.rx++; - status = c.status; - flg = TTY_NORMAL; - - /* - * note that the error handling code is - * out of the main execution path - */ - if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME - | ATMEL_US_OVRE | ATMEL_US_RXBRK))) { - if (status & ATMEL_US_RXBRK) { - /* ignore side-effect */ - status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); - - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } - if (status & ATMEL_US_PARE) - port->icount.parity++; - if (status & ATMEL_US_FRAME) - port->icount.frame++; - if (status & ATMEL_US_OVRE) - port->icount.overrun++; - - status &= port->read_status_mask; - - if (status & ATMEL_US_RXBRK) - flg = TTY_BREAK; - else if (status & ATMEL_US_PARE) - flg = TTY_PARITY; - else if (status & ATMEL_US_FRAME) - flg = TTY_FRAME; - } - - - if (uart_handle_sysrq_char(port, c.ch)) - continue; - - uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg); - } - - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); - tty_flip_buffer_push(port->state->port.tty); - spin_lock(&port->lock); -} - -static void atmel_rx_from_dma(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_struct *tty = port->state->port.tty; - struct atmel_dma_buffer *pdc; - int rx_idx = atmel_port->pdc_rx_idx; - unsigned int head; - unsigned int tail; - unsigned int count; - - do { - /* Reset the UART timeout early so that we don't miss one */ - UART_PUT_CR(port, ATMEL_US_STTTO); - - pdc = &atmel_port->pdc_rx[rx_idx]; - head = UART_GET_RPR(port) - pdc->dma_addr; - tail = pdc->ofs; - - /* If the PDC has switched buffers, RPR won't contain - * any address within the current buffer. Since head - * is unsigned, we just need a one-way comparison to - * find out. - * - * In this case, we just need to consume the entire - * buffer and resubmit it for DMA. This will clear the - * ENDRX bit as well, so that we can safely re-enable - * all interrupts below. - */ - head = min(head, pdc->dma_size); - - if (likely(head != tail)) { - dma_sync_single_for_cpu(port->dev, pdc->dma_addr, - pdc->dma_size, DMA_FROM_DEVICE); - - /* - * head will only wrap around when we recycle - * the DMA buffer, and when that happens, we - * explicitly set tail to 0. So head will - * always be greater than tail. - */ - count = head - tail; - - tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); - - dma_sync_single_for_device(port->dev, pdc->dma_addr, - pdc->dma_size, DMA_FROM_DEVICE); - - port->icount.rx += count; - pdc->ofs = head; - } - - /* - * If the current buffer is full, we need to check if - * the next one contains any additional data. - */ - if (head >= pdc->dma_size) { - pdc->ofs = 0; - UART_PUT_RNPR(port, pdc->dma_addr); - UART_PUT_RNCR(port, pdc->dma_size); - - rx_idx = !rx_idx; - atmel_port->pdc_rx_idx = rx_idx; - } - } while (head >= pdc->dma_size); - - /* - * Drop the lock here since it might end up calling - * uart_start(), which takes the lock. - */ - spin_unlock(&port->lock); - tty_flip_buffer_push(tty); - spin_lock(&port->lock); - - UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); -} - -/* - * tasklet handling tty stuff outside the interrupt handler. - */ -static void atmel_tasklet_func(unsigned long data) -{ - struct uart_port *port = (struct uart_port *)data; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - unsigned int status; - unsigned int status_change; - - /* The interrupt handler does not take the lock */ - spin_lock(&port->lock); - - if (atmel_use_dma_tx(port)) - atmel_tx_dma(port); - else - atmel_tx_chars(port); - - status = atmel_port->irq_status; - status_change = status ^ atmel_port->irq_status_prev; - - if (status_change & (ATMEL_US_RI | ATMEL_US_DSR - | ATMEL_US_DCD | ATMEL_US_CTS)) { - /* TODO: All reads to CSR will clear these interrupts! */ - if (status_change & ATMEL_US_RI) - port->icount.rng++; - if (status_change & ATMEL_US_DSR) - port->icount.dsr++; - if (status_change & ATMEL_US_DCD) - uart_handle_dcd_change(port, !(status & ATMEL_US_DCD)); - if (status_change & ATMEL_US_CTS) - uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); - - wake_up_interruptible(&port->state->port.delta_msr_wait); - - atmel_port->irq_status_prev = status; - } - - if (atmel_use_dma_rx(port)) - atmel_rx_from_dma(port); - else - atmel_rx_from_ring(port); - - spin_unlock(&port->lock); -} - -/* - * Perform initialization and enable port for reception - */ -static int atmel_startup(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_struct *tty = port->state->port.tty; - int retval; - - /* - * Ensure that no interrupts are enabled otherwise when - * request_irq() is called we could get stuck trying to - * handle an unexpected interrupt - */ - UART_PUT_IDR(port, -1); - - /* - * Allocate the IRQ - */ - retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, - tty ? tty->name : "atmel_serial", port); - if (retval) { - printk("atmel_serial: atmel_startup - Can't get irq\n"); - return retval; - } - - /* - * Initialize DMA (if necessary) - */ - if (atmel_use_dma_rx(port)) { - int i; - - for (i = 0; i < 2; i++) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; - - pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); - if (pdc->buf == NULL) { - if (i != 0) { - dma_unmap_single(port->dev, - atmel_port->pdc_rx[0].dma_addr, - PDC_BUFFER_SIZE, - DMA_FROM_DEVICE); - kfree(atmel_port->pdc_rx[0].buf); - } - free_irq(port->irq, port); - return -ENOMEM; - } - pdc->dma_addr = dma_map_single(port->dev, - pdc->buf, - PDC_BUFFER_SIZE, - DMA_FROM_DEVICE); - pdc->dma_size = PDC_BUFFER_SIZE; - pdc->ofs = 0; - } - - atmel_port->pdc_rx_idx = 0; - - UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); - UART_PUT_RCR(port, PDC_BUFFER_SIZE); - - UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); - UART_PUT_RNCR(port, PDC_BUFFER_SIZE); - } - if (atmel_use_dma_tx(port)) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - struct circ_buf *xmit = &port->state->xmit; - - pdc->buf = xmit->buf; - pdc->dma_addr = dma_map_single(port->dev, - pdc->buf, - UART_XMIT_SIZE, - DMA_TO_DEVICE); - pdc->dma_size = UART_XMIT_SIZE; - pdc->ofs = 0; - } - - /* - * If there is a specific "open" function (to register - * control line interrupts) - */ - if (atmel_open_hook) { - retval = atmel_open_hook(port); - if (retval) { - free_irq(port->irq, port); - return retval; - } - } - - /* Save current CSR for comparison in atmel_tasklet_func() */ - atmel_port->irq_status_prev = UART_GET_CSR(port); - atmel_port->irq_status = atmel_port->irq_status_prev; - - /* - * Finally, enable the serial port - */ - UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); - /* enable xmit & rcvr */ - UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); - - if (atmel_use_dma_rx(port)) { - /* set UART timeout */ - UART_PUT_RTOR(port, PDC_RX_TIMEOUT); - UART_PUT_CR(port, ATMEL_US_STTTO); - - UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); - /* enable PDC controller */ - UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); - } else { - /* enable receive only */ - UART_PUT_IER(port, ATMEL_US_RXRDY); - } - - return 0; -} - -/* - * Disable the port - */ -static void atmel_shutdown(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - /* - * Ensure everything is stopped. - */ - atmel_stop_rx(port); - atmel_stop_tx(port); - - /* - * Shut-down the DMA. - */ - if (atmel_use_dma_rx(port)) { - int i; - - for (i = 0; i < 2; i++) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; - - dma_unmap_single(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_FROM_DEVICE); - kfree(pdc->buf); - } - } - if (atmel_use_dma_tx(port)) { - struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - - dma_unmap_single(port->dev, - pdc->dma_addr, - pdc->dma_size, - DMA_TO_DEVICE); - } - - /* - * Disable all interrupts, port and break condition. - */ - UART_PUT_CR(port, ATMEL_US_RSTSTA); - UART_PUT_IDR(port, -1); - - /* - * Free the interrupt - */ - free_irq(port->irq, port); - - /* - * If there is a specific "close" function (to unregister - * control line interrupts) - */ - if (atmel_close_hook) - atmel_close_hook(port); -} - -/* - * Flush any TX data submitted for DMA. Called when the TX circular - * buffer is reset. - */ -static void atmel_flush_buffer(struct uart_port *port) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_use_dma_tx(port)) { - UART_PUT_TCR(port, 0); - atmel_port->pdc_tx.ofs = 0; - } -} - -/* - * Power / Clock management. - */ -static void atmel_serial_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - switch (state) { - case 0: - /* - * Enable the peripheral clock for this serial port. - * This is called on uart_open() or a resume event. - */ - clk_enable(atmel_port->clk); - - /* re-enable interrupts if we disabled some on suspend */ - UART_PUT_IER(port, atmel_port->backup_imr); - break; - case 3: - /* Back up the interrupt mask and disable all interrupts */ - atmel_port->backup_imr = UART_GET_IMR(port); - UART_PUT_IDR(port, -1); - - /* - * Disable the peripheral clock for this serial port. - * This is called on uart_close() or a suspend event. - */ - clk_disable(atmel_port->clk); - break; - default: - printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); - } -} - -/* - * Change the port parameters - */ -static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - unsigned int mode, imr, quot, baud; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - /* Get current mode register */ - mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL - | ATMEL_US_NBSTOP | ATMEL_US_PAR - | ATMEL_US_USMODE); - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - quot = uart_get_divisor(port, baud); - - if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */ - quot /= 8; - mode |= ATMEL_US_USCLKS_MCK_DIV8; - } - - /* byte size */ - switch (termios->c_cflag & CSIZE) { - case CS5: - mode |= ATMEL_US_CHRL_5; - break; - case CS6: - mode |= ATMEL_US_CHRL_6; - break; - case CS7: - mode |= ATMEL_US_CHRL_7; - break; - default: - mode |= ATMEL_US_CHRL_8; - break; - } - - /* stop bits */ - if (termios->c_cflag & CSTOPB) - mode |= ATMEL_US_NBSTOP_2; - - /* parity */ - if (termios->c_cflag & PARENB) { - /* Mark or Space parity */ - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - mode |= ATMEL_US_PAR_MARK; - else - mode |= ATMEL_US_PAR_SPACE; - } else if (termios->c_cflag & PARODD) - mode |= ATMEL_US_PAR_ODD; - else - mode |= ATMEL_US_PAR_EVEN; - } else - mode |= ATMEL_US_PAR_NONE; - - /* hardware handshake (RTS/CTS) */ - if (termios->c_cflag & CRTSCTS) - mode |= ATMEL_US_USMODE_HWHS; - else - mode |= ATMEL_US_USMODE_NORMAL; - - spin_lock_irqsave(&port->lock, flags); - - port->read_status_mask = ATMEL_US_OVRE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= ATMEL_US_RXBRK; - - if (atmel_use_dma_rx(port)) - /* need to enable error interrupts */ - UART_PUT_IER(port, port->read_status_mask); - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= ATMEL_US_RXBRK; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= ATMEL_US_OVRE; - } - /* TODO: Ignore all characters if CREAD is set.*/ - - /* update the per-port timeout */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * save/disable interrupts. The tty layer will ensure that the - * transmitter is empty if requested by the caller, so there's - * no need to wait for it here. - */ - imr = UART_GET_IMR(port); - UART_PUT_IDR(port, -1); - - /* disable receiver and transmitter */ - UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); - - /* Resetting serial mode to RS232 (0x0) */ - mode &= ~ATMEL_US_USMODE; - - if (atmel_port->rs485.flags & SER_RS485_ENABLED) { - dev_dbg(port->dev, "Setting UART to RS485\n"); - if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) - UART_PUT_TTGR(port, - atmel_port->rs485.delay_rts_after_send); - mode |= ATMEL_US_USMODE_RS485; - } else { - dev_dbg(port->dev, "Setting UART to RS232\n"); - } - - /* set the parity, stop bits and data size */ - UART_PUT_MR(port, mode); - - /* set the baud rate */ - UART_PUT_BRGR(port, quot); - UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); - UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); - - /* restore interrupts */ - UART_PUT_IER(port, imr); - - /* CTS flow-control and modem-status interrupts */ - if (UART_ENABLE_MS(port, termios->c_cflag)) - port->ops->enable_ms(port); - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* - * Return string describing the specified port - */ -static const char *atmel_type(struct uart_port *port) -{ - return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL; -} - -/* - * Release the memory region(s) being used by 'port'. - */ -static void atmel_release_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - int size = pdev->resource[0].end - pdev->resource[0].start + 1; - - release_mem_region(port->mapbase, size); - - if (port->flags & UPF_IOREMAP) { - iounmap(port->membase); - port->membase = NULL; - } -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int atmel_request_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - int size = pdev->resource[0].end - pdev->resource[0].start + 1; - - if (!request_mem_region(port->mapbase, size, "atmel_serial")) - return -EBUSY; - - if (port->flags & UPF_IOREMAP) { - port->membase = ioremap(port->mapbase, size); - if (port->membase == NULL) { - release_mem_region(port->mapbase, size); - return -ENOMEM; - } - } - - return 0; -} - -/* - * Configure/autoconfigure the port. - */ -static void atmel_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_ATMEL; - atmel_request_port(port); - } -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - */ -static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - int ret = 0; - if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL) - ret = -EINVAL; - if (port->irq != ser->irq) - ret = -EINVAL; - if (ser->io_type != SERIAL_IO_MEM) - ret = -EINVAL; - if (port->uartclk / 16 != ser->baud_base) - ret = -EINVAL; - if ((void *)port->mapbase != ser->iomem_base) - ret = -EINVAL; - if (port->iobase != ser->port) - ret = -EINVAL; - if (ser->hub6 != 0) - ret = -EINVAL; - return ret; -} - -#ifdef CONFIG_CONSOLE_POLL -static int atmel_poll_get_char(struct uart_port *port) -{ - while (!(UART_GET_CSR(port) & ATMEL_US_RXRDY)) - cpu_relax(); - - return UART_GET_CHAR(port); -} - -static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) -{ - while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) - cpu_relax(); - - UART_PUT_CHAR(port, ch); -} -#endif - -static int -atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) -{ - struct serial_rs485 rs485conf; - - switch (cmd) { - case TIOCSRS485: - if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, - sizeof(rs485conf))) - return -EFAULT; - - atmel_config_rs485(port, &rs485conf); - break; - - case TIOCGRS485: - if (copy_to_user((struct serial_rs485 *) arg, - &(to_atmel_uart_port(port)->rs485), - sizeof(rs485conf))) - return -EFAULT; - break; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - - - -static struct uart_ops atmel_pops = { - .tx_empty = atmel_tx_empty, - .set_mctrl = atmel_set_mctrl, - .get_mctrl = atmel_get_mctrl, - .stop_tx = atmel_stop_tx, - .start_tx = atmel_start_tx, - .stop_rx = atmel_stop_rx, - .enable_ms = atmel_enable_ms, - .break_ctl = atmel_break_ctl, - .startup = atmel_startup, - .shutdown = atmel_shutdown, - .flush_buffer = atmel_flush_buffer, - .set_termios = atmel_set_termios, - .type = atmel_type, - .release_port = atmel_release_port, - .request_port = atmel_request_port, - .config_port = atmel_config_port, - .verify_port = atmel_verify_port, - .pm = atmel_serial_pm, - .ioctl = atmel_ioctl, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = atmel_poll_get_char, - .poll_put_char = atmel_poll_put_char, -#endif -}; - -/* - * Configure the port from the platform device resource info. - */ -static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, - struct platform_device *pdev) -{ - struct uart_port *port = &atmel_port->uart; - struct atmel_uart_data *data = pdev->dev.platform_data; - - port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &atmel_pops; - port->fifosize = 1; - port->line = pdev->id; - port->dev = &pdev->dev; - port->mapbase = pdev->resource[0].start; - port->irq = pdev->resource[1].start; - - tasklet_init(&atmel_port->tasklet, atmel_tasklet_func, - (unsigned long)port); - - memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); - - if (data->regs) - /* Already mapped by setup code */ - port->membase = data->regs; - else { - port->flags |= UPF_IOREMAP; - port->membase = NULL; - } - - /* for console, the clock could already be configured */ - if (!atmel_port->clk) { - atmel_port->clk = clk_get(&pdev->dev, "usart"); - clk_enable(atmel_port->clk); - port->uartclk = clk_get_rate(atmel_port->clk); - clk_disable(atmel_port->clk); - /* only enable clock when USART is in use */ - } - - atmel_port->use_dma_rx = data->use_dma_rx; - atmel_port->use_dma_tx = data->use_dma_tx; - atmel_port->rs485 = data->rs485; - /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ - if (atmel_port->rs485.flags & SER_RS485_ENABLED) - atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; - else if (atmel_use_dma_tx(port)) { - port->fifosize = PDC_BUFFER_SIZE; - atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; - } else { - atmel_port->tx_done_mask = ATMEL_US_TXRDY; - } -} - -/* - * Register board-specific modem-control line handlers. - */ -void __init atmel_register_uart_fns(struct atmel_port_fns *fns) -{ - if (fns->enable_ms) - atmel_pops.enable_ms = fns->enable_ms; - if (fns->get_mctrl) - atmel_pops.get_mctrl = fns->get_mctrl; - if (fns->set_mctrl) - atmel_pops.set_mctrl = fns->set_mctrl; - atmel_open_hook = fns->open; - atmel_close_hook = fns->close; - atmel_pops.pm = fns->pm; - atmel_pops.set_wake = fns->set_wake; -} - -#ifdef CONFIG_SERIAL_ATMEL_CONSOLE -static void atmel_console_putchar(struct uart_port *port, int ch) -{ - while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) - cpu_relax(); - UART_PUT_CHAR(port, ch); -} - -/* - * Interrupts are disabled on entering - */ -static void atmel_console_write(struct console *co, const char *s, u_int count) -{ - struct uart_port *port = &atmel_ports[co->index].uart; - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - unsigned int status, imr; - unsigned int pdc_tx; - - /* - * First, save IMR and then disable interrupts - */ - imr = UART_GET_IMR(port); - UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); - - /* Store PDC transmit status and disable it */ - pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; - UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - - uart_console_write(port, s, count, atmel_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore IMR - */ - do { - status = UART_GET_CSR(port); - } while (!(status & ATMEL_US_TXRDY)); - - /* Restore PDC transmit status */ - if (pdc_tx) - UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - - /* set interrupts back the way they were */ - UART_PUT_IER(port, imr); -} - -/* - * If the port was already initialised (eg, by a boot loader), - * try to determine the current setup. - */ -static void __init atmel_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) -{ - unsigned int mr, quot; - - /* - * If the baud rate generator isn't running, the port wasn't - * initialized by the boot loader. - */ - quot = UART_GET_BRGR(port) & ATMEL_US_CD; - if (!quot) - return; - - mr = UART_GET_MR(port) & ATMEL_US_CHRL; - if (mr == ATMEL_US_CHRL_8) - *bits = 8; - else - *bits = 7; - - mr = UART_GET_MR(port) & ATMEL_US_PAR; - if (mr == ATMEL_US_PAR_EVEN) - *parity = 'e'; - else if (mr == ATMEL_US_PAR_ODD) - *parity = 'o'; - - /* - * The serial core only rounds down when matching this to a - * supported baud rate. Make sure we don't end up slightly - * lower than one of those, as it would make us fall through - * to a much lower baud rate than we really want. - */ - *baud = port->uartclk / (16 * (quot - 1)); -} - -static int __init atmel_console_setup(struct console *co, char *options) -{ - struct uart_port *port = &atmel_ports[co->index].uart; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (port->membase == NULL) { - /* Port not initialized yet - delay setup */ - return -ENODEV; - } - - clk_enable(atmel_ports[co->index].clk); - - UART_PUT_IDR(port, -1); - UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); - UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - atmel_console_get_options(port, &baud, &parity, &bits); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver atmel_uart; - -static struct console atmel_console = { - .name = ATMEL_DEVICENAME, - .write = atmel_console_write, - .device = uart_console_device, - .setup = atmel_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &atmel_uart, -}; - -#define ATMEL_CONSOLE_DEVICE (&atmel_console) - -/* - * Early console initialization (before VM subsystem initialized). - */ -static int __init atmel_console_init(void) -{ - if (atmel_default_console_device) { - add_preferred_console(ATMEL_DEVICENAME, - atmel_default_console_device->id, NULL); - atmel_init_port(&atmel_ports[atmel_default_console_device->id], - atmel_default_console_device); - register_console(&atmel_console); - } - - return 0; -} - -console_initcall(atmel_console_init); - -/* - * Late console initialization. - */ -static int __init atmel_late_console_init(void) -{ - if (atmel_default_console_device - && !(atmel_console.flags & CON_ENABLED)) - register_console(&atmel_console); - - return 0; -} - -core_initcall(atmel_late_console_init); - -static inline bool atmel_is_console_port(struct uart_port *port) -{ - return port->cons && port->cons->index == port->line; -} - -#else -#define ATMEL_CONSOLE_DEVICE NULL - -static inline bool atmel_is_console_port(struct uart_port *port) -{ - return false; -} -#endif - -static struct uart_driver atmel_uart = { - .owner = THIS_MODULE, - .driver_name = "atmel_serial", - .dev_name = ATMEL_DEVICENAME, - .major = SERIAL_ATMEL_MAJOR, - .minor = MINOR_START, - .nr = ATMEL_MAX_UART, - .cons = ATMEL_CONSOLE_DEVICE, -}; - -#ifdef CONFIG_PM -static bool atmel_serial_clk_will_stop(void) -{ -#ifdef CONFIG_ARCH_AT91 - return at91_suspend_entering_slow_clock(); -#else - return false; -#endif -} - -static int atmel_serial_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct uart_port *port = platform_get_drvdata(pdev); - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - if (atmel_is_console_port(port) && console_suspend_enabled) { - /* Drain the TX shifter */ - while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) - cpu_relax(); - } - - /* we can not wake up if we're running on slow clock */ - atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); - if (atmel_serial_clk_will_stop()) - device_set_wakeup_enable(&pdev->dev, 0); - - uart_suspend_port(&atmel_uart, port); - - return 0; -} - -static int atmel_serial_resume(struct platform_device *pdev) -{ - struct uart_port *port = platform_get_drvdata(pdev); - struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - - uart_resume_port(&atmel_uart, port); - device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup); - - return 0; -} -#else -#define atmel_serial_suspend NULL -#define atmel_serial_resume NULL -#endif - -static int __devinit atmel_serial_probe(struct platform_device *pdev) -{ - struct atmel_uart_port *port; - void *data; - int ret; - - BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); - - port = &atmel_ports[pdev->id]; - port->backup_imr = 0; - - atmel_init_port(port, pdev); - - if (!atmel_use_dma_rx(&port->uart)) { - ret = -ENOMEM; - data = kmalloc(sizeof(struct atmel_uart_char) - * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); - if (!data) - goto err_alloc_ring; - port->rx_ring.buf = data; - } - - ret = uart_add_one_port(&atmel_uart, &port->uart); - if (ret) - goto err_add_port; - -#ifdef CONFIG_SERIAL_ATMEL_CONSOLE - if (atmel_is_console_port(&port->uart) - && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { - /* - * The serial core enabled the clock for us, so undo - * the clk_enable() in atmel_console_setup() - */ - clk_disable(port->clk); - } -#endif - - device_init_wakeup(&pdev->dev, 1); - platform_set_drvdata(pdev, port); - - return 0; - -err_add_port: - kfree(port->rx_ring.buf); - port->rx_ring.buf = NULL; -err_alloc_ring: - if (!atmel_is_console_port(&port->uart)) { - clk_put(port->clk); - port->clk = NULL; - } - - return ret; -} - -static int __devexit 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); - int ret = 0; - - device_init_wakeup(&pdev->dev, 0); - platform_set_drvdata(pdev, NULL); - - ret = uart_remove_one_port(&atmel_uart, port); - - tasklet_kill(&atmel_port->tasklet); - kfree(atmel_port->rx_ring.buf); - - /* "port" is allocated statically, so we shouldn't free it */ - - clk_put(atmel_port->clk); - - return ret; -} - -static struct platform_driver atmel_serial_driver = { - .probe = atmel_serial_probe, - .remove = __devexit_p(atmel_serial_remove), - .suspend = atmel_serial_suspend, - .resume = atmel_serial_resume, - .driver = { - .name = "atmel_usart", - .owner = THIS_MODULE, - }, -}; - -static int __init atmel_serial_init(void) -{ - int ret; - - ret = uart_register_driver(&atmel_uart); - if (ret) - return ret; - - ret = platform_driver_register(&atmel_serial_driver); - if (ret) - uart_unregister_driver(&atmel_uart); - - return ret; -} - -static void __exit atmel_serial_exit(void) -{ - platform_driver_unregister(&atmel_serial_driver); - uart_unregister_driver(&atmel_uart); -} - -module_init(atmel_serial_init); -module_exit(atmel_serial_exit); - -MODULE_AUTHOR("Rick Bronson"); -MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:atmel_usart"); diff --git a/drivers/serial/bcm63xx_uart.c b/drivers/serial/bcm63xx_uart.c deleted file mode 100644 index a1a0e55d0807..000000000000 --- a/drivers/serial/bcm63xx_uart.c +++ /dev/null @@ -1,891 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Derived from many drivers using generic_serial interface. - * - * Copyright (C) 2008 Maxime Bizon - * - * Serial driver for BCM63xx integrated UART. - * - * Hardware flow control was _not_ tested since I only have RX/TX on - * my board. - */ - -#if defined(CONFIG_SERIAL_BCM63XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define BCM63XX_NR_UARTS 2 - -static struct uart_port ports[BCM63XX_NR_UARTS]; - -/* - * rx interrupt mask / stat - * - * mask: - * - rx fifo full - * - rx fifo above threshold - * - rx fifo not empty for too long - */ -#define UART_RX_INT_MASK (UART_IR_MASK(UART_IR_RXOVER) | \ - UART_IR_MASK(UART_IR_RXTHRESH) | \ - UART_IR_MASK(UART_IR_RXTIMEOUT)) - -#define UART_RX_INT_STAT (UART_IR_STAT(UART_IR_RXOVER) | \ - UART_IR_STAT(UART_IR_RXTHRESH) | \ - UART_IR_STAT(UART_IR_RXTIMEOUT)) - -/* - * tx interrupt mask / stat - * - * mask: - * - tx fifo empty - * - tx fifo below threshold - */ -#define UART_TX_INT_MASK (UART_IR_MASK(UART_IR_TXEMPTY) | \ - UART_IR_MASK(UART_IR_TXTRESH)) - -#define UART_TX_INT_STAT (UART_IR_STAT(UART_IR_TXEMPTY) | \ - UART_IR_STAT(UART_IR_TXTRESH)) - -/* - * external input interrupt - * - * mask: any edge on CTS, DCD - */ -#define UART_EXTINP_INT_MASK (UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \ - UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) - -/* - * handy uart register accessor - */ -static inline unsigned int bcm_uart_readl(struct uart_port *port, - unsigned int offset) -{ - return bcm_readl(port->membase + offset); -} - -static inline void bcm_uart_writel(struct uart_port *port, - unsigned int value, unsigned int offset) -{ - bcm_writel(value, port->membase + offset); -} - -/* - * serial core request to check if uart tx fifo is empty - */ -static unsigned int bcm_uart_tx_empty(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_IR_REG); - return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; -} - -/* - * serial core request to set RTS and DTR pin state and loopback mode - */ -static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_MCTL_REG); - val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); - /* invert of written value is reflected on the pin */ - if (!(mctrl & TIOCM_DTR)) - val |= UART_MCTL_DTR_MASK; - if (!(mctrl & TIOCM_RTS)) - val |= UART_MCTL_RTS_MASK; - bcm_uart_writel(port, val, UART_MCTL_REG); - - val = bcm_uart_readl(port, UART_CTL_REG); - if (mctrl & TIOCM_LOOP) - val |= UART_CTL_LOOPBACK_MASK; - else - val &= ~UART_CTL_LOOPBACK_MASK; - bcm_uart_writel(port, val, UART_CTL_REG); -} - -/* - * serial core request to return RI, CTS, DCD and DSR pin state - */ -static unsigned int bcm_uart_get_mctrl(struct uart_port *port) -{ - unsigned int val, mctrl; - - mctrl = 0; - val = bcm_uart_readl(port, UART_EXTINP_REG); - if (val & UART_EXTINP_RI_MASK) - mctrl |= TIOCM_RI; - if (val & UART_EXTINP_CTS_MASK) - mctrl |= TIOCM_CTS; - if (val & UART_EXTINP_DCD_MASK) - mctrl |= TIOCM_CD; - if (val & UART_EXTINP_DSR_MASK) - mctrl |= TIOCM_DSR; - return mctrl; -} - -/* - * serial core request to disable tx ASAP (used for flow control) - */ -static void bcm_uart_stop_tx(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_CTL_REG); - val &= ~(UART_CTL_TXEN_MASK); - bcm_uart_writel(port, val, UART_CTL_REG); - - val = bcm_uart_readl(port, UART_IR_REG); - val &= ~UART_TX_INT_MASK; - bcm_uart_writel(port, val, UART_IR_REG); -} - -/* - * serial core request to (re)enable tx - */ -static void bcm_uart_start_tx(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_IR_REG); - val |= UART_TX_INT_MASK; - bcm_uart_writel(port, val, UART_IR_REG); - - val = bcm_uart_readl(port, UART_CTL_REG); - val |= UART_CTL_TXEN_MASK; - bcm_uart_writel(port, val, UART_CTL_REG); -} - -/* - * serial core request to stop rx, called before port shutdown - */ -static void bcm_uart_stop_rx(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_IR_REG); - val &= ~UART_RX_INT_MASK; - bcm_uart_writel(port, val, UART_IR_REG); -} - -/* - * serial core request to enable modem status interrupt reporting - */ -static void bcm_uart_enable_ms(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_IR_REG); - val |= UART_IR_MASK(UART_IR_EXTIP); - bcm_uart_writel(port, val, UART_IR_REG); -} - -/* - * serial core request to start/stop emitting break char - */ -static void bcm_uart_break_ctl(struct uart_port *port, int ctl) -{ - unsigned long flags; - unsigned int val; - - spin_lock_irqsave(&port->lock, flags); - - val = bcm_uart_readl(port, UART_CTL_REG); - if (ctl) - val |= UART_CTL_XMITBRK_MASK; - else - val &= ~UART_CTL_XMITBRK_MASK; - bcm_uart_writel(port, val, UART_CTL_REG); - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* - * return port type in string format - */ -static const char *bcm_uart_type(struct uart_port *port) -{ - return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL; -} - -/* - * read all chars in rx fifo and send them to core - */ -static void bcm_uart_do_rx(struct uart_port *port) -{ - struct tty_struct *tty; - unsigned int max_count; - - /* limit number of char read in interrupt, should not be - * higher than fifo size anyway since we're much faster than - * serial port */ - max_count = 32; - tty = port->state->port.tty; - do { - unsigned int iestat, c, cstat; - char flag; - - /* get overrun/fifo empty information from ier - * register */ - iestat = bcm_uart_readl(port, UART_IR_REG); - if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) - break; - - cstat = c = bcm_uart_readl(port, UART_FIFO_REG); - port->icount.rx++; - flag = TTY_NORMAL; - c &= 0xff; - - if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { - /* do stats first */ - if (cstat & UART_FIFO_BRKDET_MASK) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } - - if (cstat & UART_FIFO_PARERR_MASK) - port->icount.parity++; - if (cstat & UART_FIFO_FRAMEERR_MASK) - port->icount.frame++; - - /* update flag wrt read_status_mask */ - cstat &= port->read_status_mask; - if (cstat & UART_FIFO_BRKDET_MASK) - flag = TTY_BREAK; - if (cstat & UART_FIFO_FRAMEERR_MASK) - flag = TTY_FRAME; - if (cstat & UART_FIFO_PARERR_MASK) - flag = TTY_PARITY; - } - - if (uart_handle_sysrq_char(port, c)) - continue; - - if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { - port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - - if ((cstat & port->ignore_status_mask) == 0) - tty_insert_flip_char(tty, c, flag); - - } while (--max_count); - - tty_flip_buffer_push(tty); -} - -/* - * fill tx fifo with chars to send, stop when fifo is about to be full - * or when all chars have been sent. - */ -static void bcm_uart_do_tx(struct uart_port *port) -{ - struct circ_buf *xmit; - unsigned int val, max_count; - - if (port->x_char) { - bcm_uart_writel(port, port->x_char, UART_FIFO_REG); - port->icount.tx++; - port->x_char = 0; - return; - } - - if (uart_tx_stopped(port)) { - bcm_uart_stop_tx(port); - return; - } - - xmit = &port->state->xmit; - if (uart_circ_empty(xmit)) - goto txq_empty; - - val = bcm_uart_readl(port, UART_MCTL_REG); - val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; - max_count = port->fifosize - val; - - while (max_count--) { - unsigned int c; - - c = xmit->buf[xmit->tail]; - bcm_uart_writel(port, c, UART_FIFO_REG); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - goto txq_empty; - return; - -txq_empty: - /* nothing to send, disable transmit interrupt */ - val = bcm_uart_readl(port, UART_IR_REG); - val &= ~UART_TX_INT_MASK; - bcm_uart_writel(port, val, UART_IR_REG); - return; -} - -/* - * process uart interrupt - */ -static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) -{ - struct uart_port *port; - unsigned int irqstat; - - port = dev_id; - spin_lock(&port->lock); - - irqstat = bcm_uart_readl(port, UART_IR_REG); - if (irqstat & UART_RX_INT_STAT) - bcm_uart_do_rx(port); - - if (irqstat & UART_TX_INT_STAT) - bcm_uart_do_tx(port); - - if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { - unsigned int estat; - - estat = bcm_uart_readl(port, UART_EXTINP_REG); - if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) - uart_handle_cts_change(port, - estat & UART_EXTINP_CTS_MASK); - if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) - uart_handle_dcd_change(port, - estat & UART_EXTINP_DCD_MASK); - } - - spin_unlock(&port->lock); - return IRQ_HANDLED; -} - -/* - * enable rx & tx operation on uart - */ -static void bcm_uart_enable(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_CTL_REG); - val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); - bcm_uart_writel(port, val, UART_CTL_REG); -} - -/* - * disable rx & tx operation on uart - */ -static void bcm_uart_disable(struct uart_port *port) -{ - unsigned int val; - - val = bcm_uart_readl(port, UART_CTL_REG); - val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | - UART_CTL_RXEN_MASK); - bcm_uart_writel(port, val, UART_CTL_REG); -} - -/* - * clear all unread data in rx fifo and unsent data in tx fifo - */ -static void bcm_uart_flush(struct uart_port *port) -{ - unsigned int val; - - /* empty rx and tx fifo */ - val = bcm_uart_readl(port, UART_CTL_REG); - val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; - bcm_uart_writel(port, val, UART_CTL_REG); - - /* read any pending char to make sure all irq status are - * cleared */ - (void)bcm_uart_readl(port, UART_FIFO_REG); -} - -/* - * serial core request to initialize uart and start rx operation - */ -static int bcm_uart_startup(struct uart_port *port) -{ - unsigned int val; - int ret; - - /* mask all irq and flush port */ - bcm_uart_disable(port); - bcm_uart_writel(port, 0, UART_IR_REG); - bcm_uart_flush(port); - - /* clear any pending external input interrupt */ - (void)bcm_uart_readl(port, UART_EXTINP_REG); - - /* set rx/tx fifo thresh to fifo half size */ - val = bcm_uart_readl(port, UART_MCTL_REG); - val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); - val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; - val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; - bcm_uart_writel(port, val, UART_MCTL_REG); - - /* set rx fifo timeout to 1 char time */ - val = bcm_uart_readl(port, UART_CTL_REG); - val &= ~UART_CTL_RXTMOUTCNT_MASK; - val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; - bcm_uart_writel(port, val, UART_CTL_REG); - - /* report any edge on dcd and cts */ - val = UART_EXTINP_INT_MASK; - val |= UART_EXTINP_DCD_NOSENSE_MASK; - val |= UART_EXTINP_CTS_NOSENSE_MASK; - bcm_uart_writel(port, val, UART_EXTINP_REG); - - /* register irq and enable rx interrupts */ - ret = request_irq(port->irq, bcm_uart_interrupt, 0, - bcm_uart_type(port), port); - if (ret) - return ret; - bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); - bcm_uart_enable(port); - return 0; -} - -/* - * serial core request to flush & disable uart - */ -static void bcm_uart_shutdown(struct uart_port *port) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - bcm_uart_writel(port, 0, UART_IR_REG); - spin_unlock_irqrestore(&port->lock, flags); - - bcm_uart_disable(port); - bcm_uart_flush(port); - free_irq(port->irq, port); -} - -/* - * serial core request to change current uart setting - */ -static void bcm_uart_set_termios(struct uart_port *port, - struct ktermios *new, - struct ktermios *old) -{ - unsigned int ctl, baud, quot, ier; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - /* disable uart while changing speed */ - bcm_uart_disable(port); - bcm_uart_flush(port); - - /* update Control register */ - ctl = bcm_uart_readl(port, UART_CTL_REG); - ctl &= ~UART_CTL_BITSPERSYM_MASK; - - switch (new->c_cflag & CSIZE) { - case CS5: - ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); - break; - case CS6: - ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); - break; - case CS7: - ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); - break; - default: - ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); - break; - } - - ctl &= ~UART_CTL_STOPBITS_MASK; - if (new->c_cflag & CSTOPB) - ctl |= UART_CTL_STOPBITS_2; - else - ctl |= UART_CTL_STOPBITS_1; - - ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); - if (new->c_cflag & PARENB) - ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); - ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); - if (new->c_cflag & PARODD) - ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); - bcm_uart_writel(port, ctl, UART_CTL_REG); - - /* update Baudword register */ - baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); - quot = uart_get_divisor(port, baud) - 1; - bcm_uart_writel(port, quot, UART_BAUD_REG); - - /* update Interrupt register */ - ier = bcm_uart_readl(port, UART_IR_REG); - - ier &= ~UART_IR_MASK(UART_IR_EXTIP); - if (UART_ENABLE_MS(port, new->c_cflag)) - ier |= UART_IR_MASK(UART_IR_EXTIP); - - bcm_uart_writel(port, ier, UART_IR_REG); - - /* update read/ignore mask */ - port->read_status_mask = UART_FIFO_VALID_MASK; - if (new->c_iflag & INPCK) { - port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; - port->read_status_mask |= UART_FIFO_PARERR_MASK; - } - if (new->c_iflag & (BRKINT)) - port->read_status_mask |= UART_FIFO_BRKDET_MASK; - - port->ignore_status_mask = 0; - if (new->c_iflag & IGNPAR) - port->ignore_status_mask |= UART_FIFO_PARERR_MASK; - if (new->c_iflag & IGNBRK) - port->ignore_status_mask |= UART_FIFO_BRKDET_MASK; - if (!(new->c_cflag & CREAD)) - port->ignore_status_mask |= UART_FIFO_VALID_MASK; - - uart_update_timeout(port, new->c_cflag, baud); - bcm_uart_enable(port); - spin_unlock_irqrestore(&port->lock, flags); -} - -/* - * serial core request to claim uart iomem - */ -static int bcm_uart_request_port(struct uart_port *port) -{ - unsigned int size; - - size = RSET_UART_SIZE; - if (!request_mem_region(port->mapbase, size, "bcm63xx")) { - dev_err(port->dev, "Memory region busy\n"); - return -EBUSY; - } - - port->membase = ioremap(port->mapbase, size); - if (!port->membase) { - dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, size); - return -EBUSY; - } - return 0; -} - -/* - * serial core request to release uart iomem - */ -static void bcm_uart_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, RSET_UART_SIZE); - iounmap(port->membase); -} - -/* - * serial core request to do any port required autoconfiguration - */ -static void bcm_uart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - if (bcm_uart_request_port(port)) - return; - port->type = PORT_BCM63XX; - } -} - -/* - * serial core request to check that port information in serinfo are - * suitable - */ -static int bcm_uart_verify_port(struct uart_port *port, - struct serial_struct *serinfo) -{ - if (port->type != PORT_BCM63XX) - return -EINVAL; - if (port->irq != serinfo->irq) - return -EINVAL; - if (port->iotype != serinfo->io_type) - return -EINVAL; - if (port->mapbase != (unsigned long)serinfo->iomem_base) - return -EINVAL; - return 0; -} - -/* serial core callbacks */ -static struct uart_ops bcm_uart_ops = { - .tx_empty = bcm_uart_tx_empty, - .get_mctrl = bcm_uart_get_mctrl, - .set_mctrl = bcm_uart_set_mctrl, - .start_tx = bcm_uart_start_tx, - .stop_tx = bcm_uart_stop_tx, - .stop_rx = bcm_uart_stop_rx, - .enable_ms = bcm_uart_enable_ms, - .break_ctl = bcm_uart_break_ctl, - .startup = bcm_uart_startup, - .shutdown = bcm_uart_shutdown, - .set_termios = bcm_uart_set_termios, - .type = bcm_uart_type, - .release_port = bcm_uart_release_port, - .request_port = bcm_uart_request_port, - .config_port = bcm_uart_config_port, - .verify_port = bcm_uart_verify_port, -}; - - - -#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE -static inline void wait_for_xmitr(struct uart_port *port) -{ - unsigned int tmout; - - /* Wait up to 10ms for the character(s) to be sent. */ - tmout = 10000; - while (--tmout) { - unsigned int val; - - val = bcm_uart_readl(port, UART_IR_REG); - if (val & UART_IR_STAT(UART_IR_TXEMPTY)) - break; - udelay(1); - } - - /* Wait up to 1s for flow control if necessary */ - if (port->flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout) { - unsigned int val; - - val = bcm_uart_readl(port, UART_EXTINP_REG); - if (val & UART_EXTINP_CTS_MASK) - break; - udelay(1); - } - } -} - -/* - * output given char - */ -static void bcm_console_putchar(struct uart_port *port, int ch) -{ - wait_for_xmitr(port); - bcm_uart_writel(port, ch, UART_FIFO_REG); -} - -/* - * console core request to output given string - */ -static void bcm_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_port *port; - unsigned long flags; - int locked; - - port = &ports[co->index]; - - local_irq_save(flags); - if (port->sysrq) { - /* bcm_uart_interrupt() already took the lock */ - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&port->lock); - } else { - spin_lock(&port->lock); - locked = 1; - } - - /* call helper to deal with \r\n */ - uart_console_write(port, s, count, bcm_console_putchar); - - /* and wait for char to be transmitted */ - wait_for_xmitr(port); - - if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); -} - -/* - * console core request to setup given console, find matching uart - * port and setup it. - */ -static int bcm_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index < 0 || co->index >= BCM63XX_NR_UARTS) - return -EINVAL; - port = &ports[co->index]; - if (!port->membase) - return -ENODEV; - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver bcm_uart_driver; - -static struct console bcm63xx_console = { - .name = "ttyS", - .write = bcm_console_write, - .device = uart_console_device, - .setup = bcm_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &bcm_uart_driver, -}; - -static int __init bcm63xx_console_init(void) -{ - register_console(&bcm63xx_console); - return 0; -} - -console_initcall(bcm63xx_console_init); - -#define BCM63XX_CONSOLE (&bcm63xx_console) -#else -#define BCM63XX_CONSOLE NULL -#endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */ - -static struct uart_driver bcm_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "bcm63xx_uart", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = BCM63XX_NR_UARTS, - .cons = BCM63XX_CONSOLE, -}; - -/* - * platform driver probe/remove callback - */ -static int __devinit bcm_uart_probe(struct platform_device *pdev) -{ - struct resource *res_mem, *res_irq; - struct uart_port *port; - struct clk *clk; - int ret; - - if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) - return -EINVAL; - - if (ports[pdev->id].membase) - return -EBUSY; - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res_mem) - return -ENODEV; - - res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res_irq) - return -ENODEV; - - clk = clk_get(&pdev->dev, "periph"); - if (IS_ERR(clk)) - return -ENODEV; - - port = &ports[pdev->id]; - memset(port, 0, sizeof(*port)); - port->iotype = UPIO_MEM; - port->mapbase = res_mem->start; - port->irq = res_irq->start; - port->ops = &bcm_uart_ops; - port->flags = UPF_BOOT_AUTOCONF; - port->dev = &pdev->dev; - port->fifosize = 16; - port->uartclk = clk_get_rate(clk) / 2; - port->line = pdev->id; - clk_put(clk); - - ret = uart_add_one_port(&bcm_uart_driver, port); - if (ret) { - ports[pdev->id].membase = 0; - return ret; - } - platform_set_drvdata(pdev, port); - return 0; -} - -static int __devexit bcm_uart_remove(struct platform_device *pdev) -{ - struct uart_port *port; - - port = platform_get_drvdata(pdev); - uart_remove_one_port(&bcm_uart_driver, port); - platform_set_drvdata(pdev, NULL); - /* mark port as free */ - ports[pdev->id].membase = 0; - return 0; -} - -/* - * platform driver stuff - */ -static struct platform_driver bcm_uart_platform_driver = { - .probe = bcm_uart_probe, - .remove = __devexit_p(bcm_uart_remove), - .driver = { - .owner = THIS_MODULE, - .name = "bcm63xx_uart", - }, -}; - -static int __init bcm_uart_init(void) -{ - int ret; - - ret = uart_register_driver(&bcm_uart_driver); - if (ret) - return ret; - - ret = platform_driver_register(&bcm_uart_platform_driver); - if (ret) - uart_unregister_driver(&bcm_uart_driver); - - return ret; -} - -static void __exit bcm_uart_exit(void) -{ - platform_driver_unregister(&bcm_uart_platform_driver); - uart_unregister_driver(&bcm_uart_driver); -} - -module_init(bcm_uart_init); -module_exit(bcm_uart_exit); - -MODULE_AUTHOR("Maxime Bizon "); -MODULE_DESCRIPTION("Broadcom 63 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) -#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) -#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) -#include - -#ifdef CONFIG_SERIAL_BFIN_MODULE -# undef CONFIG_EARLY_PRINTK -#endif - -#ifdef CONFIG_SERIAL_BFIN_MODULE -# undef CONFIG_EARLY_PRINTK -#endif - -/* UART name and device definitions */ -#define BFIN_SERIAL_DEV_NAME "ttyBF" -#define BFIN_SERIAL_MAJOR 204 -#define BFIN_SERIAL_MINOR 64 - -static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS]; - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - -# ifndef CONFIG_SERIAL_BFIN_PIO -# error KGDB only support UART in PIO mode. -# endif - -static int kgdboc_port_line; -static int kgdboc_break_enabled; -#endif -/* - * Setup for console. Argument comes from the menuconfig - */ -#define DMA_RX_XCOUNT 512 -#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT) - -#define DMA_RX_FLUSH_JIFFIES (HZ / 50) - -#ifdef CONFIG_SERIAL_BFIN_DMA -static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart); -#else -static void bfin_serial_tx_chars(struct bfin_serial_port *uart); -#endif - -static void bfin_serial_reset_irda(struct uart_port *port); - -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) -static unsigned int bfin_serial_get_mctrl(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - if (uart->cts_pin < 0) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - - /* CTS PIN is negative assertive. */ - if (UART_GET_CTS(uart)) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - else - return TIOCM_DSR | TIOCM_CAR; -} - -static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - if (uart->rts_pin < 0) - return; - - /* RTS PIN is negative assertive. */ - if (mctrl & TIOCM_RTS) - UART_ENABLE_RTS(uart); - else - UART_DISABLE_RTS(uart); -} - -/* - * Handle any change of modem status signal. - */ -static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - unsigned int status; - - status = bfin_serial_get_mctrl(&uart->port); - uart_handle_cts_change(&uart->port, status & TIOCM_CTS); -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - uart->scts = 1; - UART_CLEAR_SCTS(uart); - UART_CLEAR_IER(uart, EDSSI); -#endif - - return IRQ_HANDLED; -} -#else -static unsigned int bfin_serial_get_mctrl(struct uart_port *port) -{ - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; -} - -static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} -#endif - -/* - * interrupts are disabled on entry - */ -static void bfin_serial_stop_tx(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; -#ifdef CONFIG_SERIAL_BFIN_DMA - struct circ_buf *xmit = &uart->port.state->xmit; -#endif - - while (!(UART_GET_LSR(uart) & TEMT)) - cpu_relax(); - -#ifdef CONFIG_SERIAL_BFIN_DMA - disable_dma(uart->tx_dma_channel); - xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx += uart->tx_count; - uart->tx_count = 0; - uart->tx_done = 1; -#else -#ifdef CONFIG_BF54x - /* Clear TFI bit */ - UART_PUT_LSR(uart, TFI); -#endif - UART_CLEAR_IER(uart, ETBEI); -#endif -} - -/* - * port is locked and interrupts are disabled - */ -static void bfin_serial_start_tx(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - struct tty_struct *tty = uart->port.state->port.tty; - -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - - /* - * To avoid losting RX interrupt, we reset IR function - * before sending data. - */ - if (tty->termios->c_line == N_IRDA) - bfin_serial_reset_irda(port); - -#ifdef CONFIG_SERIAL_BFIN_DMA - if (uart->tx_done) - bfin_serial_dma_tx_chars(uart); -#else - UART_SET_IER(uart, ETBEI); - bfin_serial_tx_chars(uart); -#endif -} - -/* - * Interrupts are enabled - */ -static void bfin_serial_stop_rx(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - UART_CLEAR_IER(uart, ERBFI); -} - -/* - * Set the modem control timer to fire immediately. - */ -static void bfin_serial_enable_ms(struct uart_port *port) -{ -} - - -#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO) -# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold) -# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v)) -#else -# define UART_GET_ANOMALY_THRESHOLD(uart) 0 -# define UART_SET_ANOMALY_THRESHOLD(uart, v) -#endif - -#ifdef CONFIG_SERIAL_BFIN_PIO -static void bfin_serial_rx_chars(struct bfin_serial_port *uart) -{ - struct tty_struct *tty = NULL; - unsigned int status, ch, flg; - static struct timeval anomaly_start = { .tv_sec = 0 }; - - status = UART_GET_LSR(uart); - UART_CLEAR_LSR(uart); - - ch = UART_GET_CHAR(uart); - uart->port.icount.rx++; - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - if (kgdb_connected && kgdboc_port_line == uart->port.line - && kgdboc_break_enabled) - if (ch == 0x3) {/* Ctrl + C */ - kgdb_breakpoint(); - return; - } - - if (!uart->port.state || !uart->port.state->port.tty) - return; -#endif - tty = uart->port.state->port.tty; - - if (ANOMALY_05000363) { - /* The BF533 (and BF561) family of processors have a nice anomaly - * where they continuously generate characters for a "single" break. - * We have to basically ignore this flood until the "next" valid - * character comes across. Due to the nature of the flood, it is - * not possible to reliably catch bytes that are sent too quickly - * after this break. So application code talking to the Blackfin - * which sends a break signal must allow at least 1.5 character - * times after the end of the break for things to stabilize. This - * timeout was picked as it must absolutely be larger than 1 - * character time +/- some percent. So 1.5 sounds good. All other - * Blackfin families operate properly. Woo. - */ - if (anomaly_start.tv_sec) { - struct timeval curr; - suseconds_t usecs; - - if ((~ch & (~ch + 1)) & 0xff) - goto known_good_char; - - do_gettimeofday(&curr); - if (curr.tv_sec - anomaly_start.tv_sec > 1) - goto known_good_char; - - usecs = 0; - if (curr.tv_sec != anomaly_start.tv_sec) - usecs += USEC_PER_SEC; - usecs += curr.tv_usec - anomaly_start.tv_usec; - - if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) - goto known_good_char; - - if (ch) - anomaly_start.tv_sec = 0; - else - anomaly_start = curr; - - return; - - known_good_char: - status &= ~BI; - anomaly_start.tv_sec = 0; - } - } - - if (status & BI) { - if (ANOMALY_05000363) - if (bfin_revid() < 5) - do_gettimeofday(&anomaly_start); - uart->port.icount.brk++; - if (uart_handle_break(&uart->port)) - goto ignore_char; - status &= ~(PE | FE); - } - if (status & PE) - uart->port.icount.parity++; - if (status & OE) - uart->port.icount.overrun++; - if (status & FE) - uart->port.icount.frame++; - - status &= uart->port.read_status_mask; - - if (status & BI) - flg = TTY_BREAK; - else if (status & PE) - flg = TTY_PARITY; - else if (status & FE) - flg = TTY_FRAME; - else - flg = TTY_NORMAL; - - if (uart_handle_sysrq_char(&uart->port, ch)) - goto ignore_char; - - uart_insert_char(&uart->port, status, OE, ch, flg); - - ignore_char: - tty_flip_buffer_push(tty); -} - -static void bfin_serial_tx_chars(struct bfin_serial_port *uart) -{ - struct circ_buf *xmit = &uart->port.state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { -#ifdef CONFIG_BF54x - /* Clear TFI bit */ - UART_PUT_LSR(uart, TFI); -#endif - /* Anomaly notes: - * 05000215 - we always clear ETBEI within last UART TX - * interrupt to end a string. It is always set - * when start a new tx. - */ - UART_CLEAR_IER(uart, ETBEI); - return; - } - - if (uart->port.x_char) { - UART_PUT_CHAR(uart, uart->port.x_char); - uart->port.icount.tx++; - uart->port.x_char = 0; - } - - while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { - UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uart->port); -} - -static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - - spin_lock(&uart->port.lock); - while (UART_GET_LSR(uart) & DR) - bfin_serial_rx_chars(uart); - spin_unlock(&uart->port.lock); - - return IRQ_HANDLED; -} - -static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - spin_lock(&uart->port.lock); - if (UART_GET_LSR(uart) & THRE) - bfin_serial_tx_chars(uart); - spin_unlock(&uart->port.lock); - - return IRQ_HANDLED; -} -#endif - -#ifdef CONFIG_SERIAL_BFIN_DMA -static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) -{ - struct circ_buf *xmit = &uart->port.state->xmit; - - uart->tx_done = 0; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { - uart->tx_count = 0; - uart->tx_done = 1; - return; - } - - if (uart->port.x_char) { - UART_PUT_CHAR(uart, uart->port.x_char); - uart->port.icount.tx++; - uart->port.x_char = 0; - } - - uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); - if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) - uart->tx_count = UART_XMIT_SIZE - xmit->tail; - blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail), - (unsigned long)(xmit->buf+xmit->tail+uart->tx_count)); - set_dma_config(uart->tx_dma_channel, - set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, - INTR_ON_BUF, - DIMENSION_LINEAR, - DATA_SIZE_8, - DMA_SYNC_RESTART)); - set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); - set_dma_x_count(uart->tx_dma_channel, uart->tx_count); - set_dma_x_modify(uart->tx_dma_channel, 1); - SSYNC(); - enable_dma(uart->tx_dma_channel); - - UART_SET_IER(uart, ETBEI); -} - -static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) -{ - struct tty_struct *tty = uart->port.state->port.tty; - int i, flg, status; - - status = UART_GET_LSR(uart); - UART_CLEAR_LSR(uart); - - uart->port.icount.rx += - CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, - UART_XMIT_SIZE); - - if (status & BI) { - uart->port.icount.brk++; - if (uart_handle_break(&uart->port)) - goto dma_ignore_char; - status &= ~(PE | FE); - } - if (status & PE) - uart->port.icount.parity++; - if (status & OE) - uart->port.icount.overrun++; - if (status & FE) - uart->port.icount.frame++; - - status &= uart->port.read_status_mask; - - if (status & BI) - flg = TTY_BREAK; - else if (status & PE) - flg = TTY_PARITY; - else if (status & FE) - flg = TTY_FRAME; - else - flg = TTY_NORMAL; - - for (i = uart->rx_dma_buf.tail; ; i++) { - if (i >= UART_XMIT_SIZE) - i = 0; - if (i == uart->rx_dma_buf.head) - break; - if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) - uart_insert_char(&uart->port, status, OE, - uart->rx_dma_buf.buf[i], flg); - } - - dma_ignore_char: - tty_flip_buffer_push(tty); -} - -void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) -{ - int x_pos, pos; - - dma_disable_irq(uart->tx_dma_channel); - dma_disable_irq(uart->rx_dma_channel); - spin_lock_bh(&uart->port.lock); - - /* 2D DMA RX buffer ring is used. Because curr_y_count and - * curr_x_count can't be read as an atomic operation, - * curr_y_count should be read before curr_x_count. When - * curr_x_count is read, curr_y_count may already indicate - * next buffer line. But, the position calculated here is - * still indicate the old line. The wrong position data may - * be smaller than current buffer tail, which cause garbages - * are received if it is not prohibit. - */ - uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); - x_pos = get_dma_curr_xcount(uart->rx_dma_channel); - uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; - if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) - uart->rx_dma_nrows = 0; - x_pos = DMA_RX_XCOUNT - x_pos; - if (x_pos == DMA_RX_XCOUNT) - x_pos = 0; - - pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos; - /* Ignore receiving data if new position is in the same line of - * current buffer tail and small. - */ - if (pos > uart->rx_dma_buf.tail || - uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { - uart->rx_dma_buf.head = pos; - bfin_serial_dma_rx_chars(uart); - uart->rx_dma_buf.tail = uart->rx_dma_buf.head; - } - - spin_unlock_bh(&uart->port.lock); - dma_enable_irq(uart->tx_dma_channel); - dma_enable_irq(uart->rx_dma_channel); - - mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); -} - -static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - struct circ_buf *xmit = &uart->port.state->xmit; - -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) { - uart->scts = 0; - uart_handle_cts_change(&uart->port, uart->scts); - } -#endif - - spin_lock(&uart->port.lock); - if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) { - disable_dma(uart->tx_dma_channel); - clear_dma_irqstat(uart->tx_dma_channel); - /* Anomaly notes: - * 05000215 - we always clear ETBEI within last UART TX - * interrupt to end a string. It is always set - * when start a new tx. - */ - UART_CLEAR_IER(uart, ETBEI); - xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); - uart->port.icount.tx += uart->tx_count; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uart->port); - - bfin_serial_dma_tx_chars(uart); - } - - spin_unlock(&uart->port.lock); - return IRQ_HANDLED; -} - -static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id) -{ - struct bfin_serial_port *uart = dev_id; - unsigned short irqstat; - int x_pos, pos; - - spin_lock(&uart->port.lock); - irqstat = get_dma_curr_irqstat(uart->rx_dma_channel); - clear_dma_irqstat(uart->rx_dma_channel); - - uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); - x_pos = get_dma_curr_xcount(uart->rx_dma_channel); - uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; - if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) - uart->rx_dma_nrows = 0; - - pos = uart->rx_dma_nrows * DMA_RX_XCOUNT; - if (pos > uart->rx_dma_buf.tail || - uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { - uart->rx_dma_buf.head = pos; - bfin_serial_dma_rx_chars(uart); - uart->rx_dma_buf.tail = uart->rx_dma_buf.head; - } - - spin_unlock(&uart->port.lock); - - return IRQ_HANDLED; -} -#endif - -/* - * Return TIOCSER_TEMT when transmitter is not busy. - */ -static unsigned int bfin_serial_tx_empty(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short lsr; - - lsr = UART_GET_LSR(uart); - if (lsr & TEMT) - return TIOCSER_TEMT; - else - return 0; -} - -static void bfin_serial_break_ctl(struct uart_port *port, int break_state) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - u16 lcr = UART_GET_LCR(uart); - if (break_state) - lcr |= SB; - else - lcr &= ~SB; - UART_PUT_LCR(uart, lcr); - SSYNC(); -} - -static int bfin_serial_startup(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - -#ifdef CONFIG_SERIAL_BFIN_DMA - dma_addr_t dma_handle; - - if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) { - printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n"); - return -EBUSY; - } - - if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) { - printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n"); - free_dma(uart->rx_dma_channel); - return -EBUSY; - } - - set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart); - set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart); - - uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA); - uart->rx_dma_buf.head = 0; - uart->rx_dma_buf.tail = 0; - uart->rx_dma_nrows = 0; - - set_dma_config(uart->rx_dma_channel, - set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, - INTR_ON_ROW, DIMENSION_2D, - DATA_SIZE_8, - DMA_SYNC_RESTART)); - set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT); - set_dma_x_modify(uart->rx_dma_channel, 1); - set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT); - set_dma_y_modify(uart->rx_dma_channel, 1); - set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf); - enable_dma(uart->rx_dma_channel); - - uart->rx_dma_timer.data = (unsigned long)(uart); - uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout; - uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES; - add_timer(&(uart->rx_dma_timer)); -#else -# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled) - kgdboc_break_enabled = 0; - else { -# endif - if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED, - "BFIN_UART_RX", uart)) { - printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n"); - return -EBUSY; - } - - if (request_irq - (uart->port.irq+1, bfin_serial_tx_int, IRQF_DISABLED, - "BFIN_UART_TX", uart)) { - printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n"); - free_irq(uart->port.irq, uart); - return -EBUSY; - } - -# ifdef CONFIG_BF54x - { - /* - * UART2 and UART3 on BF548 share interrupt PINs and DMA - * controllers with SPORT2 and SPORT3. UART rx and tx - * interrupts are generated in PIO mode only when configure - * their peripheral mapping registers properly, which means - * request corresponding DMA channels in PIO mode as well. - */ - unsigned uart_dma_ch_rx, uart_dma_ch_tx; - - switch (uart->port.irq) { - case IRQ_UART3_RX: - uart_dma_ch_rx = CH_UART3_RX; - uart_dma_ch_tx = CH_UART3_TX; - break; - case IRQ_UART2_RX: - uart_dma_ch_rx = CH_UART2_RX; - uart_dma_ch_tx = CH_UART2_TX; - break; - default: - uart_dma_ch_rx = uart_dma_ch_tx = 0; - break; - }; - - if (uart_dma_ch_rx && - request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { - printk(KERN_NOTICE"Fail to attach UART interrupt\n"); - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq + 1, uart); - return -EBUSY; - } - if (uart_dma_ch_tx && - request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) { - printk(KERN_NOTICE "Fail to attach UART interrupt\n"); - free_dma(uart_dma_ch_rx); - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq + 1, uart); - return -EBUSY; - } - } -# endif -# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - } -# endif -#endif - -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->cts_pin >= 0) { - if (request_irq(gpio_to_irq(uart->cts_pin), - bfin_serial_mctrl_cts_int, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_DISABLED, "BFIN_UART_CTS", uart)) { - uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); - } - } - if (uart->rts_pin >= 0) { - gpio_direction_output(uart->rts_pin, 0); - } -#endif -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->cts_pin >= 0 && request_irq(uart->status_irq, - bfin_serial_mctrl_cts_int, - IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) { - uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); - } - - /* CTS RTS PINs are negative assertive. */ - UART_PUT_MCR(uart, ACTS); - UART_SET_IER(uart, EDSSI); -#endif - - UART_SET_IER(uart, ERBFI); - return 0; -} - -static void bfin_serial_shutdown(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - -#ifdef CONFIG_SERIAL_BFIN_DMA - disable_dma(uart->tx_dma_channel); - free_dma(uart->tx_dma_channel); - disable_dma(uart->rx_dma_channel); - free_dma(uart->rx_dma_channel); - del_timer(&(uart->rx_dma_timer)); - dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0); -#else -#ifdef CONFIG_BF54x - switch (uart->port.irq) { - case IRQ_UART3_RX: - free_dma(CH_UART3_RX); - free_dma(CH_UART3_TX); - break; - case IRQ_UART2_RX: - free_dma(CH_UART2_RX); - free_dma(CH_UART2_TX); - break; - default: - break; - }; -#endif - free_irq(uart->port.irq, uart); - free_irq(uart->port.irq+1, uart); -#endif - -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->cts_pin >= 0) - free_irq(gpio_to_irq(uart->cts_pin), uart); -#endif -#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (uart->cts_pin >= 0) - free_irq(uart->status_irq, uart); -#endif -} - -static void -bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned long flags; - unsigned int baud, quot; - unsigned short val, ier, lcr = 0; - - switch (termios->c_cflag & CSIZE) { - case CS8: - lcr = WLS(8); - break; - case CS7: - lcr = WLS(7); - break; - case CS6: - lcr = WLS(6); - break; - case CS5: - lcr = WLS(5); - break; - default: - printk(KERN_ERR "%s: word lengh not supported\n", - __func__); - } - - /* Anomaly notes: - * 05000231 - STOP bit is always set to 1 whatever the user is set. - */ - if (termios->c_cflag & CSTOPB) { - if (ANOMALY_05000231) - printk(KERN_WARNING "STOP bits other than 1 is not " - "supported in case of anomaly 05000231.\n"); - else - lcr |= STB; - } - if (termios->c_cflag & PARENB) - lcr |= PEN; - if (!(termios->c_cflag & PARODD)) - lcr |= EPS; - if (termios->c_cflag & CMSPAR) - lcr |= STP; - - spin_lock_irqsave(&uart->port.lock, flags); - - port->read_status_mask = OE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (FE | PE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= BI; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= FE | PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= OE; - } - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - /* If discipline is not IRDA, apply ANOMALY_05000230 */ - if (termios->c_line != N_IRDA) - quot -= ANOMALY_05000230; - - UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); - - /* Disable UART */ - ier = UART_GET_IER(uart); - UART_DISABLE_INTS(uart); - - /* Set DLAB in LCR to Access DLL and DLH */ - UART_SET_DLAB(uart); - - UART_PUT_DLL(uart, quot & 0xFF); - UART_PUT_DLH(uart, (quot >> 8) & 0xFF); - SSYNC(); - - /* Clear DLAB in LCR to Access THR RBR IER */ - UART_CLEAR_DLAB(uart); - - UART_PUT_LCR(uart, lcr); - - /* Enable UART */ - UART_ENABLE_INTS(uart, ier); - - val = UART_GET_GCTL(uart); - val |= UCEN; - UART_PUT_GCTL(uart, val); - - /* Port speed changed, update the per-port timeout. */ - uart_update_timeout(port, termios->c_cflag, baud); - - spin_unlock_irqrestore(&uart->port.lock, flags); -} - -static const char *bfin_serial_type(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL; -} - -/* - * Release the memory region(s) being used by 'port'. - */ -static void bfin_serial_release_port(struct uart_port *port) -{ -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int bfin_serial_request_port(struct uart_port *port) -{ - return 0; -} - -/* - * Configure/autoconfigure the port. - */ -static void bfin_serial_config_port(struct uart_port *port, int flags) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - if (flags & UART_CONFIG_TYPE && - bfin_serial_request_port(&uart->port) == 0) - uart->port.type = PORT_BFIN; -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - * The only change we allow are to the flags and type, and - * even then only between PORT_BFIN and PORT_UNKNOWN - */ -static int -bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return 0; -} - -/* - * Enable the IrDA function if tty->ldisc.num is N_IRDA. - * In other cases, disable IrDA function. - */ -static void bfin_serial_set_ldisc(struct uart_port *port, int ld) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short val; - - switch (ld) { - case N_IRDA: - val = UART_GET_GCTL(uart); - val |= (IREN | RPOLC); - UART_PUT_GCTL(uart, val); - break; - default: - val = UART_GET_GCTL(uart); - val &= ~(IREN | RPOLC); - UART_PUT_GCTL(uart, val); - } -} - -static void bfin_serial_reset_irda(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned short val; - - val = UART_GET_GCTL(uart); - val &= ~(IREN | RPOLC); - UART_PUT_GCTL(uart, val); - SSYNC(); - val |= (IREN | RPOLC); - UART_PUT_GCTL(uart, val); - SSYNC(); -} - -#ifdef CONFIG_CONSOLE_POLL -/* Anomaly notes: - * 05000099 - Because we only use THRE in poll_put and DR in poll_get, - * losing other bits of UART_LSR is not a problem here. - */ -static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - while (!(UART_GET_LSR(uart) & THRE)) - cpu_relax(); - - UART_CLEAR_DLAB(uart); - UART_PUT_CHAR(uart, (unsigned char)chr); -} - -static int bfin_serial_poll_get_char(struct uart_port *port) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - unsigned char chr; - - while (!(UART_GET_LSR(uart) & DR)) - cpu_relax(); - - UART_CLEAR_DLAB(uart); - chr = UART_GET_CHAR(uart); - - return chr; -} -#endif - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) -static void bfin_kgdboc_port_shutdown(struct uart_port *port) -{ - if (kgdboc_break_enabled) { - kgdboc_break_enabled = 0; - bfin_serial_shutdown(port); - } -} - -static int bfin_kgdboc_port_startup(struct uart_port *port) -{ - kgdboc_port_line = port->line; - kgdboc_break_enabled = !bfin_serial_startup(port); - return 0; -} -#endif - -static struct uart_ops bfin_serial_pops = { - .tx_empty = bfin_serial_tx_empty, - .set_mctrl = bfin_serial_set_mctrl, - .get_mctrl = bfin_serial_get_mctrl, - .stop_tx = bfin_serial_stop_tx, - .start_tx = bfin_serial_start_tx, - .stop_rx = bfin_serial_stop_rx, - .enable_ms = bfin_serial_enable_ms, - .break_ctl = bfin_serial_break_ctl, - .startup = bfin_serial_startup, - .shutdown = bfin_serial_shutdown, - .set_termios = bfin_serial_set_termios, - .set_ldisc = bfin_serial_set_ldisc, - .type = bfin_serial_type, - .release_port = bfin_serial_release_port, - .request_port = bfin_serial_request_port, - .config_port = bfin_serial_config_port, - .verify_port = bfin_serial_verify_port, -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - .kgdboc_port_startup = bfin_kgdboc_port_startup, - .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, -#endif -#ifdef CONFIG_CONSOLE_POLL - .poll_put_char = bfin_serial_poll_put_char, - .poll_get_char = bfin_serial_poll_get_char, -#endif -}; - -#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) -/* - * If the port was already initialised (eg, by a boot loader), - * try to determine the current setup. - */ -static void __init -bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, - int *parity, int *bits) -{ - unsigned short status; - - status = UART_GET_IER(uart) & (ERBFI | ETBEI); - if (status == (ERBFI | ETBEI)) { - /* ok, the port was enabled */ - u16 lcr, dlh, dll; - - lcr = UART_GET_LCR(uart); - - *parity = 'n'; - if (lcr & PEN) { - if (lcr & EPS) - *parity = 'e'; - else - *parity = 'o'; - } - switch (lcr & 0x03) { - case 0: *bits = 5; break; - case 1: *bits = 6; break; - case 2: *bits = 7; break; - case 3: *bits = 8; break; - } - /* Set DLAB in LCR to Access DLL and DLH */ - UART_SET_DLAB(uart); - - dll = UART_GET_DLL(uart); - dlh = UART_GET_DLH(uart); - - /* Clear DLAB in LCR to Access THR RBR IER */ - UART_CLEAR_DLAB(uart); - - *baud = get_sclk() / (16*(dll | dlh << 8)); - } - pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits); -} - -static struct uart_driver bfin_serial_reg; - -static void bfin_serial_console_putchar(struct uart_port *port, int ch) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - while (!(UART_GET_LSR(uart) & THRE)) - barrier(); - UART_PUT_CHAR(uart, ch); -} - -#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || - defined (CONFIG_EARLY_PRINTK) */ - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE -#define CLASS_BFIN_CONSOLE "bfin-console" -/* - * Interrupts are disabled on entering - */ -static void -bfin_serial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct bfin_serial_port *uart = bfin_serial_ports[co->index]; - unsigned long flags; - - spin_lock_irqsave(&uart->port.lock, flags); - uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); - spin_unlock_irqrestore(&uart->port.lock, flags); - -} - -static int __init -bfin_serial_console_setup(struct console *co, char *options) -{ - struct bfin_serial_port *uart; - int baud = 57600; - int bits = 8; - int parity = 'n'; -# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - int flow = 'r'; -# else - int flow = 'n'; -# endif - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index < 0 || co->index >= BFIN_UART_NR_PORTS) - return -ENODEV; - - uart = bfin_serial_ports[co->index]; - if (!uart) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - bfin_serial_console_get_options(uart, &baud, &parity, &bits); - - return uart_set_options(&uart->port, co, baud, parity, bits, flow); -} - -static struct console bfin_serial_console = { - .name = BFIN_SERIAL_DEV_NAME, - .write = bfin_serial_console_write, - .device = uart_console_device, - .setup = bfin_serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &bfin_serial_reg, -}; -#define BFIN_SERIAL_CONSOLE &bfin_serial_console -#else -#define BFIN_SERIAL_CONSOLE NULL -#endif /* CONFIG_SERIAL_BFIN_CONSOLE */ - -#ifdef CONFIG_EARLY_PRINTK -static struct bfin_serial_port bfin_earlyprintk_port; -#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk" - -/* - * Interrupts are disabled on entering - */ -static void -bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count) -{ - unsigned long flags; - - if (bfin_earlyprintk_port.port.line != co->index) - return; - - spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags); - uart_console_write(&bfin_earlyprintk_port.port, s, count, - bfin_serial_console_putchar); - spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags); -} - -/* - * This should have a .setup or .early_setup in it, but then things get called - * without the command line options, and the baud rate gets messed up - so - * don't let the common infrastructure play with things. (see calls to setup - * & earlysetup in ./kernel/printk.c:register_console() - */ -static struct __initdata console bfin_early_serial_console = { - .name = "early_BFuart", - .write = bfin_earlyprintk_console_write, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &bfin_serial_reg, -}; -#endif - -static struct uart_driver bfin_serial_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = BFIN_SERIAL_DEV_NAME, - .major = BFIN_SERIAL_MAJOR, - .minor = BFIN_SERIAL_MINOR, - .nr = BFIN_UART_NR_PORTS, - .cons = BFIN_SERIAL_CONSOLE, -}; - -static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - - return uart_suspend_port(&bfin_serial_reg, &uart->port); -} - -static int bfin_serial_resume(struct platform_device *pdev) -{ - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - - return uart_resume_port(&bfin_serial_reg, &uart->port); -} - -static int bfin_serial_probe(struct platform_device *pdev) -{ - struct resource *res; - struct bfin_serial_port *uart = NULL; - int ret = 0; - - if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { - dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n"); - return -ENOENT; - } - - if (bfin_serial_ports[pdev->id] == NULL) { - - uart = kzalloc(sizeof(*uart), GFP_KERNEL); - if (!uart) { - dev_err(&pdev->dev, - "fail to malloc bfin_serial_port\n"); - return -ENOMEM; - } - bfin_serial_ports[pdev->id] = uart; - -#ifdef CONFIG_EARLY_PRINTK - if (!(bfin_earlyprintk_port.port.membase - && bfin_earlyprintk_port.port.line == pdev->id)) { - /* - * If the peripheral PINs of current port is allocated - * in earlyprintk probe stage, don't do it again. - */ -#endif - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, - "fail to request bfin serial peripherals\n"); - goto out_error_free_mem; - } -#ifdef CONFIG_EARLY_PRINTK - } -#endif - - spin_lock_init(&uart->port.lock); - uart->port.uartclk = get_sclk(); - uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE; - uart->port.ops = &bfin_serial_pops; - uart->port.line = pdev->id; - uart->port.iotype = UPIO_MEM; - uart->port.flags = UPF_BOOT_AUTOCONF; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); - ret = -ENOENT; - goto out_error_free_peripherals; - } - - uart->port.membase = ioremap(res->start, - res->end - res->start); - if (!uart->port.membase) { - dev_err(&pdev->dev, "Cannot map uart IO\n"); - ret = -ENXIO; - goto out_error_free_peripherals; - } - uart->port.mapbase = res->start; - - uart->port.irq = platform_get_irq(pdev, 0); - if (uart->port.irq < 0) { - dev_err(&pdev->dev, "No uart RX/TX IRQ specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - - uart->status_irq = platform_get_irq(pdev, 1); - if (uart->status_irq < 0) { - dev_err(&pdev->dev, "No uart status IRQ specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - -#ifdef CONFIG_SERIAL_BFIN_DMA - uart->tx_done = 1; - uart->tx_count = 0; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res == NULL) { - dev_err(&pdev->dev, "No uart TX DMA channel specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - uart->tx_dma_channel = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res == NULL) { - dev_err(&pdev->dev, "No uart RX DMA channel specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - uart->rx_dma_channel = res->start; - - init_timer(&(uart->rx_dma_timer)); -#endif - -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (res == NULL) - uart->cts_pin = -1; - else - uart->cts_pin = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IO, 1); - if (res == NULL) - uart->rts_pin = -1; - else - uart->rts_pin = res->start; -# if defined(CONFIG_SERIAL_BFIN_CTSRTS) - if (uart->rts_pin >= 0) - gpio_request(uart->rts_pin, DRIVER_NAME); -# endif -#endif - } - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE - if (!is_early_platform_device(pdev)) { -#endif - uart = bfin_serial_ports[pdev->id]; - uart->port.dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, uart); - ret = uart_add_one_port(&bfin_serial_reg, &uart->port); -#ifdef CONFIG_SERIAL_BFIN_CONSOLE - } -#endif - - if (!ret) - return 0; - - if (uart) { -out_error_unmap: - iounmap(uart->port.membase); -out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); -out_error_free_mem: - kfree(uart); - bfin_serial_ports[pdev->id] = NULL; - } - - return ret; -} - -static int __devexit bfin_serial_remove(struct platform_device *pdev) -{ - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - - dev_set_drvdata(&pdev->dev, NULL); - - if (uart) { - uart_remove_one_port(&bfin_serial_reg, &uart->port); -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); -#endif - iounmap(uart->port.membase); - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); - kfree(uart); - bfin_serial_ports[pdev->id] = NULL; - } - - return 0; -} - -static struct platform_driver bfin_serial_driver = { - .probe = bfin_serial_probe, - .remove = __devexit_p(bfin_serial_remove), - .suspend = bfin_serial_suspend, - .resume = bfin_serial_resume, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -#if defined(CONFIG_SERIAL_BFIN_CONSOLE) -static __initdata struct early_platform_driver early_bfin_serial_driver = { - .class_str = CLASS_BFIN_CONSOLE, - .pdrv = &bfin_serial_driver, - .requested_id = EARLY_PLATFORM_ID_UNSET, -}; - -static int __init bfin_serial_rs_console_init(void) -{ - early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME); - - early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0); - - register_console(&bfin_serial_console); - - return 0; -} -console_initcall(bfin_serial_rs_console_init); -#endif - -#ifdef CONFIG_EARLY_PRINTK -/* - * Memory can't be allocated dynamically during earlyprink init stage. - * So, do individual probe for earlyprink with a static uart port variable. - */ -static int bfin_earlyprintk_probe(struct platform_device *pdev) -{ - struct resource *res; - int ret; - - if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { - dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n"); - return -ENOENT; - } - - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, - "fail to request bfin serial peripherals\n"); - return ret; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); - ret = -ENOENT; - goto out_error_free_peripherals; - } - - bfin_earlyprintk_port.port.membase = ioremap(res->start, - res->end - res->start); - if (!bfin_earlyprintk_port.port.membase) { - dev_err(&pdev->dev, "Cannot map uart IO\n"); - ret = -ENXIO; - goto out_error_free_peripherals; - } - bfin_earlyprintk_port.port.mapbase = res->start; - bfin_earlyprintk_port.port.line = pdev->id; - bfin_earlyprintk_port.port.uartclk = get_sclk(); - bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE; - spin_lock_init(&bfin_earlyprintk_port.port.lock); - - return 0; - -out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); - - return ret; -} - -static struct platform_driver bfin_earlyprintk_driver = { - .probe = bfin_earlyprintk_probe, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { - .class_str = CLASS_BFIN_EARLYPRINTK, - .pdrv = &bfin_earlyprintk_driver, - .requested_id = EARLY_PLATFORM_ID_UNSET, -}; - -struct console __init *bfin_earlyserial_init(unsigned int port, - unsigned int cflag) -{ - struct ktermios t; - char port_name[20]; - - if (port < 0 || port >= BFIN_UART_NR_PORTS) - return NULL; - - /* - * Only probe resource of the given port in earlyprintk boot arg. - * The expected port id should be indicated in port name string. - */ - snprintf(port_name, 20, DRIVER_NAME ".%d", port); - early_platform_driver_register(&early_bfin_earlyprintk_driver, - port_name); - early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0); - - if (!bfin_earlyprintk_port.port.membase) - return NULL; - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE - /* - * If we are using early serial, don't let the normal console rewind - * log buffer, since that causes things to be printed multiple times - */ - bfin_serial_console.flags &= ~CON_PRINTBUFFER; -#endif - - bfin_early_serial_console.index = port; - t.c_cflag = cflag; - t.c_iflag = 0; - t.c_oflag = 0; - t.c_lflag = ICANON; - t.c_line = port; - bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t); - - return &bfin_early_serial_console; -} -#endif /* CONFIG_EARLY_PRINTK */ - -static int __init bfin_serial_init(void) -{ - int ret; - - pr_info("Blackfin serial driver\n"); - - ret = uart_register_driver(&bfin_serial_reg); - if (ret) { - pr_err("failed to register %s:%d\n", - bfin_serial_reg.driver_name, ret); - } - - ret = platform_driver_register(&bfin_serial_driver); - if (ret) { - pr_err("fail to register bfin uart\n"); - uart_unregister_driver(&bfin_serial_reg); - } - - return ret; -} - -static void __exit bfin_serial_exit(void) -{ - platform_driver_unregister(&bfin_serial_driver); - uart_unregister_driver(&bfin_serial_reg); -} - - -module_init(bfin_serial_init); -module_exit(bfin_serial_exit); - -MODULE_AUTHOR("Sonic Zhang, Aubrey Li"); -MODULE_DESCRIPTION("Blackfin generic serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR); -MODULE_ALIAS("platform:bfin-uart"); diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c deleted file mode 100644 index e95c524d9d18..000000000000 --- a/drivers/serial/bfin_sport_uart.c +++ /dev/null @@ -1,935 +0,0 @@ -/* - * Blackfin On-Chip Sport Emulated UART Driver - * - * Copyright 2006-2009 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -/* - * This driver and the hardware supported are in term of EE-191 of ADI. - * http://www.analog.com/static/imported-files/application_notes/EE191.pdf - * This application note describe how to implement a UART on a Sharc DSP, - * but this driver is implemented on Blackfin Processor. - * Transmit Frame Sync is not used by this driver to transfer data out. - */ - -/* #define DEBUG */ - -#define DRV_NAME "bfin-sport-uart" -#define DEVICE_NAME "ttySS" -#define pr_fmt(fmt) DRV_NAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "bfin_sport_uart.h" - -struct sport_uart_port { - struct uart_port port; - int err_irq; - unsigned short csize; - unsigned short rxmask; - unsigned short txmask1; - unsigned short txmask2; - unsigned char stopb; -/* unsigned char parib; */ -#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS - int cts_pin; - int rts_pin; -#endif -}; - -static int sport_uart_tx_chars(struct sport_uart_port *up); -static void sport_stop_tx(struct uart_port *port); - -static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) -{ - pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value, - up->txmask1, up->txmask2); - - /* Place Start and Stop bits */ - __asm__ __volatile__ ( - "%[val] <<= 1;" - "%[val] = %[val] & %[mask1];" - "%[val] = %[val] | %[mask2];" - : [val]"+d"(value) - : [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2) - : "ASTAT" - ); - pr_debug("%s value:%x\n", __func__, value); - - SPORT_PUT_TX(up, value); -} - -static inline unsigned char rx_one_byte(struct sport_uart_port *up) -{ - unsigned int value; - unsigned char extract; - u32 tmp_mask1, tmp_mask2, tmp_shift, tmp; - - if ((up->csize + up->stopb) > 7) - value = SPORT_GET_RX32(up); - else - value = SPORT_GET_RX(up); - - pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value, - up->csize, up->rxmask); - - /* Extract data */ - __asm__ __volatile__ ( - "%[extr] = 0;" - "%[mask1] = %[rxmask];" - "%[mask2] = 0x0200(Z);" - "%[shift] = 0;" - "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];" - ".Lloop_s:" - "%[tmp] = extract(%[val], %[mask1].L)(Z);" - "%[tmp] <<= %[shift];" - "%[extr] = %[extr] | %[tmp];" - "%[mask1] = %[mask1] - %[mask2];" - ".Lloop_e:" - "%[shift] += 1;" - : [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp), - [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2) - : [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize) - : "ASTAT", "LB0", "LC0", "LT0" - ); - - pr_debug(" extract:%x\n", extract); - return extract; -} - -static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate) -{ - int tclkdiv, rclkdiv; - unsigned int sclk = get_sclk(); - - /* Set TCR1 and TCR2, TFSR is not enabled for uart */ - SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK)); - SPORT_PUT_TCR2(up, size + 1); - pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); - - /* Set RCR1 and RCR2 */ - SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); - SPORT_PUT_RCR2(up, (size + 1) * 2 - 1); - pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); - - tclkdiv = sclk / (2 * baud_rate) - 1; - /* The actual uart baud rate of devices vary between +/-2%. The sport - * RX sample rate should be faster than the double of the worst case, - * otherwise, wrong data are received. So, set sport RX clock to be - * 3% faster. - */ - rclkdiv = sclk / (2 * baud_rate * 2 * 97 / 100) - 1; - SPORT_PUT_TCLKDIV(up, tclkdiv); - SPORT_PUT_RCLKDIV(up, rclkdiv); - SSYNC(); - pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n", - __func__, sclk, baud_rate, tclkdiv, rclkdiv); - - return 0; -} - -static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) -{ - struct sport_uart_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; - unsigned int ch; - - spin_lock(&up->port.lock); - - while (SPORT_GET_STAT(up) & RXNE) { - ch = rx_one_byte(up); - up->port.icount.rx++; - - if (!uart_handle_sysrq_char(&up->port, ch)) - tty_insert_flip_char(tty, ch, TTY_NORMAL); - } - tty_flip_buffer_push(tty); - - spin_unlock(&up->port.lock); - - return IRQ_HANDLED; -} - -static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) -{ - struct sport_uart_port *up = dev_id; - - spin_lock(&up->port.lock); - sport_uart_tx_chars(up); - spin_unlock(&up->port.lock); - - return IRQ_HANDLED; -} - -static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) -{ - struct sport_uart_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; - unsigned int stat = SPORT_GET_STAT(up); - - spin_lock(&up->port.lock); - - /* Overflow in RX FIFO */ - if (stat & ROVF) { - up->port.icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */ - } - /* These should not happen */ - if (stat & (TOVF | TUVF | RUVF)) { - pr_err("SPORT Error:%s %s %s\n", - (stat & TOVF) ? "TX overflow" : "", - (stat & TUVF) ? "TX underflow" : "", - (stat & RUVF) ? "RX underflow" : ""); - SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); - SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); - } - SSYNC(); - - spin_unlock(&up->port.lock); - return IRQ_HANDLED; -} - -#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS -static unsigned int sport_get_mctrl(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - if (up->cts_pin < 0) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - - /* CTS PIN is negative assertive. */ - if (SPORT_UART_GET_CTS(up)) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - else - return TIOCM_DSR | TIOCM_CAR; -} - -static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - if (up->rts_pin < 0) - return; - - /* RTS PIN is negative assertive. */ - if (mctrl & TIOCM_RTS) - SPORT_UART_ENABLE_RTS(up); - else - SPORT_UART_DISABLE_RTS(up); -} - -/* - * Handle any change of modem status signal. - */ -static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id) -{ - struct sport_uart_port *up = (struct sport_uart_port *)dev_id; - unsigned int status; - - status = sport_get_mctrl(&up->port); - uart_handle_cts_change(&up->port, status & TIOCM_CTS); - - return IRQ_HANDLED; -} -#else -static unsigned int sport_get_mctrl(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); - return TIOCM_CTS | TIOCM_CD | TIOCM_DSR; -} - -static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - pr_debug("%s enter\n", __func__); -} -#endif - -/* Reqeust IRQ, Setup clock */ -static int sport_startup(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - int ret; - - pr_debug("%s enter\n", __func__); - ret = request_irq(up->port.irq, sport_uart_rx_irq, 0, - "SPORT_UART_RX", up); - if (ret) { - dev_err(port->dev, "unable to request SPORT RX interrupt\n"); - return ret; - } - - ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0, - "SPORT_UART_TX", up); - if (ret) { - dev_err(port->dev, "unable to request SPORT TX interrupt\n"); - goto fail1; - } - - ret = request_irq(up->err_irq, sport_uart_err_irq, 0, - "SPORT_UART_STATUS", up); - if (ret) { - dev_err(port->dev, "unable to request SPORT status interrupt\n"); - goto fail2; - } - -#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS - if (up->cts_pin >= 0) { - if (request_irq(gpio_to_irq(up->cts_pin), - sport_mctrl_cts_int, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { - up->cts_pin = -1; - dev_info(port->dev, "Unable to attach BlackFin UART \ - over SPORT CTS interrupt. So, disable it.\n"); - } - } - if (up->rts_pin >= 0) - gpio_direction_output(up->rts_pin, 0); -#endif - - return 0; - fail2: - free_irq(up->port.irq+1, up); - fail1: - free_irq(up->port.irq, up); - - return ret; -} - -/* - * sport_uart_tx_chars - * - * ret 1 means need to enable sport. - * ret 0 means do nothing. - */ -static int sport_uart_tx_chars(struct sport_uart_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - - if (SPORT_GET_STAT(up) & TXF) - return 0; - - if (up->port.x_char) { - tx_one_byte(up, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return 1; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - /* The waiting loop to stop SPORT TX from TX interrupt is - * too long. This may block SPORT RX interrupts and cause - * RX FIFO overflow. So, do stop sport TX only after the last - * char in TX FIFO is moved into the shift register. - */ - if (SPORT_GET_STAT(up) & TXHRE) - sport_stop_tx(&up->port); - return 0; - } - - while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { - tx_one_byte(up, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); - up->port.icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - return 1; -} - -static unsigned int sport_tx_empty(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - unsigned int stat; - - stat = SPORT_GET_STAT(up); - pr_debug("%s stat:%04x\n", __func__, stat); - if (stat & TXHRE) { - return TIOCSER_TEMT; - } else - return 0; -} - -static void sport_stop_tx(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - pr_debug("%s enter\n", __func__); - - if (!(SPORT_GET_TCR1(up) & TSPEN)) - return; - - /* Although the hold register is empty, last byte is still in shift - * register and not sent out yet. So, put a dummy data into TX FIFO. - * Then, sport tx stops when last byte is shift out and the dummy - * data is moved into the shift register. - */ - SPORT_PUT_TX(up, 0xffff); - while (!(SPORT_GET_STAT(up) & TXHRE)) - cpu_relax(); - - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); - SSYNC(); - - return; -} - -static void sport_start_tx(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - pr_debug("%s enter\n", __func__); - - /* Write data into SPORT FIFO before enable SPROT to transmit */ - if (sport_uart_tx_chars(up)) { - /* Enable transmit, then an interrupt will generated */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); - } - - pr_debug("%s exit\n", __func__); -} - -static void sport_stop_rx(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - pr_debug("%s enter\n", __func__); - /* Disable sport to stop rx */ - SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); - SSYNC(); -} - -static void sport_enable_ms(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); -} - -static void sport_break_ctl(struct uart_port *port, int break_state) -{ - pr_debug("%s enter\n", __func__); -} - -static void sport_shutdown(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - dev_dbg(port->dev, "%s enter\n", __func__); - - /* Disable sport */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); - SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); - SSYNC(); - - free_irq(up->port.irq, up); - free_irq(up->port.irq+1, up); - free_irq(up->err_irq, up); -#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS - if (up->cts_pin >= 0) - free_irq(gpio_to_irq(up->cts_pin), up); -#endif -} - -static const char *sport_type(struct uart_port *port) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - pr_debug("%s enter\n", __func__); - return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL; -} - -static void sport_release_port(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); -} - -static int sport_request_port(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); - return 0; -} - -static void sport_config_port(struct uart_port *port, int flags) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - pr_debug("%s enter\n", __func__); - up->port.type = PORT_BFIN_SPORT; -} - -static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - pr_debug("%s enter\n", __func__); - return 0; -} - -static void sport_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - unsigned long flags; - int i; - - pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag); - - switch (termios->c_cflag & CSIZE) { - case CS8: - up->csize = 8; - break; - case CS7: - up->csize = 7; - break; - case CS6: - up->csize = 6; - break; - case CS5: - up->csize = 5; - break; - default: - pr_warning("requested word length not supported\n"); - } - - if (termios->c_cflag & CSTOPB) { - up->stopb = 1; - } - if (termios->c_cflag & PARENB) { - pr_warning("PAREN bits is not supported yet\n"); - /* up->parib = 1; */ - } - - spin_lock_irqsave(&up->port.lock, flags); - - port->read_status_mask = 0; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - - /* RX extract mask */ - up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); - /* TX masks, 8 bit data and 1 bit stop for example: - * mask1 = b#0111111110 - * mask2 = b#1000000000 - */ - for (i = 0, up->txmask1 = 0; i < up->csize; i++) - up->txmask1 |= (1<txmask2 = (1<stopb) { - ++i; - up->txmask2 |= (1<txmask1 <<= 1; - up->txmask2 <<= 1; - /* uart baud rate */ - port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); - - /* Disable UART */ - SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); - SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); - - sport_uart_setup(up, up->csize + up->stopb, port->uartclk); - - /* driver TX line high after config, one dummy data is - * necessary to stop sport after shift one byte - */ - SPORT_PUT_TX(up, 0xffff); - SPORT_PUT_TX(up, 0xffff); - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); - while (!(SPORT_GET_STAT(up) & TXHRE)) - cpu_relax(); - SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); - SSYNC(); - - /* Port speed changed, update the per-port timeout. */ - uart_update_timeout(port, termios->c_cflag, port->uartclk); - - /* Enable sport rx */ - SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN); - SSYNC(); - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -struct uart_ops sport_uart_ops = { - .tx_empty = sport_tx_empty, - .set_mctrl = sport_set_mctrl, - .get_mctrl = sport_get_mctrl, - .stop_tx = sport_stop_tx, - .start_tx = sport_start_tx, - .stop_rx = sport_stop_rx, - .enable_ms = sport_enable_ms, - .break_ctl = sport_break_ctl, - .startup = sport_startup, - .shutdown = sport_shutdown, - .set_termios = sport_set_termios, - .type = sport_type, - .release_port = sport_release_port, - .request_port = sport_request_port, - .config_port = sport_config_port, - .verify_port = sport_verify_port, -}; - -#define BFIN_SPORT_UART_MAX_PORTS 4 - -static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; - -#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE -#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console" - -static int __init -sport_uart_console_setup(struct console *co, char *options) -{ - struct sport_uart_port *up; - int baud = 57600; - int bits = 8; - int parity = 'n'; -# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS - int flow = 'r'; -# else - int flow = 'n'; -# endif - - /* Check whether an invalid uart number has been specified */ - if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) - return -ENODEV; - - up = bfin_sport_uart_ports[co->index]; - if (!up) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&up->port, co, baud, parity, bits, flow); -} - -static void sport_uart_console_putchar(struct uart_port *port, int ch) -{ - struct sport_uart_port *up = (struct sport_uart_port *)port; - - while (SPORT_GET_STAT(up) & TXF) - barrier(); - - tx_one_byte(up, ch); -} - -/* - * Interrupts are disabled on entering - */ -static void -sport_uart_console_write(struct console *co, const char *s, unsigned int count) -{ - struct sport_uart_port *up = bfin_sport_uart_ports[co->index]; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - if (SPORT_GET_TCR1(up) & TSPEN) - uart_console_write(&up->port, s, count, sport_uart_console_putchar); - else { - /* dummy data to start sport */ - while (SPORT_GET_STAT(up) & TXF) - barrier(); - SPORT_PUT_TX(up, 0xffff); - /* Enable transmit, then an interrupt will generated */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); - - uart_console_write(&up->port, s, count, sport_uart_console_putchar); - - /* Although the hold register is empty, last byte is still in shift - * register and not sent out yet. So, put a dummy data into TX FIFO. - * Then, sport tx stops when last byte is shift out and the dummy - * data is moved into the shift register. - */ - while (SPORT_GET_STAT(up) & TXF) - barrier(); - SPORT_PUT_TX(up, 0xffff); - while (!(SPORT_GET_STAT(up) & TXHRE)) - barrier(); - - /* Stop sport tx transfer */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); - SSYNC(); - } - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static struct uart_driver sport_uart_reg; - -static struct console sport_uart_console = { - .name = DEVICE_NAME, - .write = sport_uart_console_write, - .device = uart_console_device, - .setup = sport_uart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sport_uart_reg, -}; - -#define SPORT_UART_CONSOLE (&sport_uart_console) -#else -#define SPORT_UART_CONSOLE NULL -#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */ - - -static struct uart_driver sport_uart_reg = { - .owner = THIS_MODULE, - .driver_name = DRV_NAME, - .dev_name = DEVICE_NAME, - .major = 204, - .minor = 84, - .nr = BFIN_SPORT_UART_MAX_PORTS, - .cons = SPORT_UART_CONSOLE, -}; - -#ifdef CONFIG_PM -static int sport_uart_suspend(struct device *dev) -{ - struct sport_uart_port *sport = dev_get_drvdata(dev); - - dev_dbg(dev, "%s enter\n", __func__); - if (sport) - uart_suspend_port(&sport_uart_reg, &sport->port); - - return 0; -} - -static int sport_uart_resume(struct device *dev) -{ - struct sport_uart_port *sport = dev_get_drvdata(dev); - - dev_dbg(dev, "%s enter\n", __func__); - if (sport) - uart_resume_port(&sport_uart_reg, &sport->port); - - return 0; -} - -static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { - .suspend = sport_uart_suspend, - .resume = sport_uart_resume, -}; -#endif - -static int __devinit sport_uart_probe(struct platform_device *pdev) -{ - struct resource *res; - struct sport_uart_port *sport; - int ret = 0; - - dev_dbg(&pdev->dev, "%s enter\n", __func__); - - if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) { - dev_err(&pdev->dev, "Wrong sport uart platform device id.\n"); - return -ENOENT; - } - - if (bfin_sport_uart_ports[pdev->id] == NULL) { - bfin_sport_uart_ports[pdev->id] = - kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL); - sport = bfin_sport_uart_ports[pdev->id]; - if (!sport) { - dev_err(&pdev->dev, - "Fail to malloc sport_uart_port\n"); - return -ENOMEM; - } - - ret = peripheral_request_list( - (unsigned short *)pdev->dev.platform_data, DRV_NAME); - if (ret) { - dev_err(&pdev->dev, - "Fail to request SPORT peripherals\n"); - goto out_error_free_mem; - } - - spin_lock_init(&sport->port.lock); - sport->port.fifosize = SPORT_TX_FIFO_SIZE, - sport->port.ops = &sport_uart_ops; - sport->port.line = pdev->id; - sport->port.iotype = UPIO_MEM; - sport->port.flags = UPF_BOOT_AUTOCONF; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); - ret = -ENOENT; - goto out_error_free_peripherals; - } - - sport->port.membase = ioremap(res->start, resource_size(res)); - if (!sport->port.membase) { - dev_err(&pdev->dev, "Cannot map sport IO\n"); - ret = -ENXIO; - goto out_error_free_peripherals; - } - sport->port.mapbase = res->start; - - sport->port.irq = platform_get_irq(pdev, 0); - if (sport->port.irq < 0) { - dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } - - sport->err_irq = platform_get_irq(pdev, 1); - if (sport->err_irq < 0) { - dev_err(&pdev->dev, "No sport status IRQ specified\n"); - ret = -ENOENT; - goto out_error_unmap; - } -#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (res == NULL) - sport->cts_pin = -1; - else - sport->cts_pin = res->start; - - res = platform_get_resource(pdev, IORESOURCE_IO, 1); - if (res == NULL) - sport->rts_pin = -1; - else - sport->rts_pin = res->start; - - if (sport->rts_pin >= 0) - gpio_request(sport->rts_pin, DRV_NAME); -#endif - } - -#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE - if (!is_early_platform_device(pdev)) { -#endif - sport = bfin_sport_uart_ports[pdev->id]; - sport->port.dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, sport); - ret = uart_add_one_port(&sport_uart_reg, &sport->port); -#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE - } -#endif - if (!ret) - return 0; - - if (sport) { -out_error_unmap: - iounmap(sport->port.membase); -out_error_free_peripherals: - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); -out_error_free_mem: - kfree(sport); - bfin_sport_uart_ports[pdev->id] = NULL; - } - - return ret; -} - -static int __devexit sport_uart_remove(struct platform_device *pdev) -{ - struct sport_uart_port *sport = platform_get_drvdata(pdev); - - dev_dbg(&pdev->dev, "%s enter\n", __func__); - dev_set_drvdata(&pdev->dev, NULL); - - if (sport) { - uart_remove_one_port(&sport_uart_reg, &sport->port); -#ifdef CONFIG_SERIAL_BFIN_CTSRTS - if (sport->rts_pin >= 0) - gpio_free(sport->rts_pin); -#endif - iounmap(sport->port.membase); - peripheral_free_list( - (unsigned short *)pdev->dev.platform_data); - kfree(sport); - bfin_sport_uart_ports[pdev->id] = NULL; - } - - return 0; -} - -static struct platform_driver sport_uart_driver = { - .probe = sport_uart_probe, - .remove = __devexit_p(sport_uart_remove), - .driver = { - .name = DRV_NAME, -#ifdef CONFIG_PM - .pm = &bfin_sport_uart_dev_pm_ops, -#endif - }, -}; - -#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE -static __initdata struct early_platform_driver early_sport_uart_driver = { - .class_str = CLASS_BFIN_SPORT_CONSOLE, - .pdrv = &sport_uart_driver, - .requested_id = EARLY_PLATFORM_ID_UNSET, -}; - -static int __init sport_uart_rs_console_init(void) -{ - early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); - - early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE, - BFIN_SPORT_UART_MAX_PORTS, 0); - - register_console(&sport_uart_console); - - return 0; -} -console_initcall(sport_uart_rs_console_init); -#endif - -static int __init sport_uart_init(void) -{ - int ret; - - pr_info("Blackfin uart over sport driver\n"); - - ret = uart_register_driver(&sport_uart_reg); - if (ret) { - pr_err("failed to register %s:%d\n", - sport_uart_reg.driver_name, ret); - return ret; - } - - ret = platform_driver_register(&sport_uart_driver); - if (ret) { - pr_err("failed to register sport uart driver:%d\n", ret); - uart_unregister_driver(&sport_uart_reg); - } - - return ret; -} -module_init(sport_uart_init); - -static void __exit sport_uart_exit(void) -{ - platform_driver_unregister(&sport_uart_driver); - uart_unregister_driver(&sport_uart_reg); -} -module_exit(sport_uart_exit); - -MODULE_AUTHOR("Sonic Zhang, Roy Huang"); -MODULE_DESCRIPTION("Blackfin serial over SPORT driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h deleted file mode 100644 index 6d06ce1d5675..000000000000 --- a/drivers/serial/bfin_sport_uart.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Blackfin On-Chip Sport Emulated UART Driver - * - * Copyright 2006-2008 Analog Devices Inc. - * - * Enter bugs at http://blackfin.uclinux.org/ - * - * Licensed under the GPL-2 or later. - */ - -/* - * This driver and the hardware supported are in term of EE-191 of ADI. - * http://www.analog.com/static/imported-files/application_notes/EE191.pdf - * This application note describe how to implement a UART on a Sharc DSP, - * but this driver is implemented on Blackfin Processor. - * Transmit Frame Sync is not used by this driver to transfer data out. - */ - -#ifndef _BFIN_SPORT_UART_H -#define _BFIN_SPORT_UART_H - -#define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ -#define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ -#define OFFSET_TCLKDIV 0x08 /* Transmit Serial Clock Divider Register */ -#define OFFSET_TFSDIV 0x0C /* Transmit Frame Sync Divider Register */ -#define OFFSET_TX 0x10 /* Transmit Data Register */ -#define OFFSET_RX 0x18 /* Receive Data Register */ -#define OFFSET_RCR1 0x20 /* Receive Configuration 1 Register */ -#define OFFSET_RCR2 0x24 /* Receive Configuration 2 Register */ -#define OFFSET_RCLKDIV 0x28 /* Receive Serial Clock Divider Register */ -#define OFFSET_RFSDIV 0x2c /* Receive Frame Sync Divider Register */ -#define OFFSET_STAT 0x30 /* Status Register */ - -#define SPORT_GET_TCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR1)) -#define SPORT_GET_TCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR2)) -#define SPORT_GET_TCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TCLKDIV)) -#define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) -#define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) -#define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) -/* - * If another interrupt fires while doing a 32-bit read from RX FIFO, - * a fake RX underflow error will be generated. So disable interrupts - * to prevent interruption while reading the FIFO. - */ -#define SPORT_GET_RX32(sport) \ -({ \ - unsigned int __ret; \ - if (ANOMALY_05000473) \ - local_irq_disable(); \ - __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ - if (ANOMALY_05000473) \ - local_irq_enable(); \ - __ret; \ -}) -#define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) -#define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) -#define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) -#define SPORT_GET_RFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RFSDIV)) -#define SPORT_GET_STAT(sport) bfin_read16(((sport)->port.membase + OFFSET_STAT)) - -#define SPORT_PUT_TCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR1), v) -#define SPORT_PUT_TCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR2), v) -#define SPORT_PUT_TCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCLKDIV), v) -#define SPORT_PUT_TFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TFSDIV), v) -#define SPORT_PUT_TX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TX), v) -#define SPORT_PUT_RX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RX), v) -#define SPORT_PUT_RCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR1), v) -#define SPORT_PUT_RCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR2), v) -#define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) -#define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) -#define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) - -#define SPORT_TX_FIFO_SIZE 8 - -#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin) -#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1) -#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0) - -#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \ - || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \ - || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \ - || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS) -# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS -#endif - -#endif /* _BFIN_SPORT_UART_H */ diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c deleted file mode 100644 index b6acd19b458e..000000000000 --- a/drivers/serial/clps711x.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * linux/drivers/char/clps711x.c - * - * Driver for CLPS711x serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright 1999 ARM Limited - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * - * 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 - */ - -#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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 UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) - -#define tx_enabled(port) ((port)->unused[0]) - -static void clps711xuart_stop_tx(struct uart_port *port) -{ - if (tx_enabled(port)) { - disable_irq(TX_IRQ(port)); - tx_enabled(port) = 0; - } -} - -static void clps711xuart_start_tx(struct uart_port *port) -{ - if (!tx_enabled(port)) { - enable_irq(TX_IRQ(port)); - tx_enabled(port) = 1; - } -} - -static void clps711xuart_stop_rx(struct uart_port *port) -{ - disable_irq(RX_IRQ(port)); -} - -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; - unsigned int status, ch, flg; - - status = clps_readl(SYSFLG(port)); - while (!(status & SYSFLG_URXFE)) { - ch = clps_readl(UARTDR(port)); - - 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) - port->icount.parity++; - else if (ch & UARTDR_FRMERR) - port->icount.frame++; - if (ch & UARTDR_OVERR) - port->icount.overrun++; - - ch &= port->read_status_mask; - - if (ch & UARTDR_PARERR) - flg = TTY_PARITY; - else if (ch & UARTDR_FRMERR) - flg = TTY_FRAME; - -#ifdef SUPPORT_SYSRQ - port->sysrq = 0; -#endif - } - - if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; - - /* - * CHECK: does overrun affect the current character? - * ASSUMPTION: it does not. - */ - uart_insert_char(port, ch, UARTDR_OVERR, ch, flg); - - ignore_char: - status = clps_readl(SYSFLG(port)); - } - tty_flip_buffer_push(tty); - return IRQ_HANDLED; -} - -static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct circ_buf *xmit = &port->state->xmit; - int count; - - if (port->x_char) { - clps_writel(port->x_char, UARTDR(port)); - port->icount.tx++; - port->x_char = 0; - return IRQ_HANDLED; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - clps711xuart_stop_tx(port); - return IRQ_HANDLED; - } - - count = port->fifosize >> 1; - do { - clps_writel(xmit->buf[xmit->tail], UARTDR(port)); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - clps711xuart_stop_tx(port); - - return IRQ_HANDLED; -} - -static unsigned int clps711xuart_tx_empty(struct uart_port *port) -{ - unsigned int status = clps_readl(SYSFLG(port)); - return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; -} - -static unsigned int clps711xuart_get_mctrl(struct uart_port *port) -{ - unsigned int port_addr; - unsigned int result = 0; - unsigned int status; - - port_addr = SYSFLG(port); - if (port_addr == SYSFLG1) { - status = clps_readl(SYSFLG1); - if (status & SYSFLG1_DCD) - result |= TIOCM_CAR; - if (status & SYSFLG1_DSR) - result |= TIOCM_DSR; - if (status & SYSFLG1_CTS) - result |= TIOCM_CTS; - } - - return result; -} - -static void -clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) -{ -} - -static void clps711xuart_break_ctl(struct uart_port *port, int break_state) -{ - unsigned long flags; - unsigned int ubrlcr; - - spin_lock_irqsave(&port->lock, flags); - ubrlcr = clps_readl(UBRLCR(port)); - if (break_state == -1) - ubrlcr |= UBRLCR_BREAK; - else - ubrlcr &= ~UBRLCR_BREAK; - clps_writel(ubrlcr, UBRLCR(port)); - spin_unlock_irqrestore(&port->lock, flags); -} - -static int clps711xuart_startup(struct uart_port *port) -{ - unsigned int syscon; - int retval; - - tx_enabled(port) = 1; - - /* - * 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; - } - - /* - * enable the port - */ - syscon = clps_readl(SYSCON(port)); - syscon |= SYSCON_UARTEN; - clps_writel(syscon, SYSCON(port)); - - return 0; -} - -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 */ - - /* - * 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)); -} - -static void -clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int ubrlcr, baud, quot; - unsigned long flags; - - /* - * We don't implement CREAD. - */ - 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); - quot = uart_get_divisor(port, baud); - - switch (termios->c_cflag & CSIZE) { - case CS5: - ubrlcr = UBRLCR_WRDLEN5; - break; - case CS6: - ubrlcr = UBRLCR_WRDLEN6; - break; - case CS7: - ubrlcr = UBRLCR_WRDLEN7; - break; - default: // CS8 - 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)) - ubrlcr |= UBRLCR_EVENPRT; - } - if (port->fifosize > 1) - ubrlcr |= UBRLCR_FIFOEN; - - spin_lock_irqsave(&port->lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - port->read_status_mask = UARTDR_OVERR; - if (termios->c_iflag & INPCK) - port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; - - /* - * Characters to ignore - */ - 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; - } - - quot -= 1; - - clps_writel(ubrlcr | quot, UBRLCR(port)); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *clps711xuart_type(struct uart_port *port) -{ - return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; -} - -/* - * Configure/autoconfigure the port. - */ -static void clps711xuart_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 int clps711xuart_request_port(struct uart_port *port) -{ - return 0; -} - -static struct uart_ops clps711x_pops = { - .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 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) -{ - while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) - barrier(); - clps_writel(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) -{ - struct uart_port *port = clps711x_ports + co->index; - unsigned int status, syscon; - - /* - * 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); - - /* - * Finally, wait for transmitter to become empty - * and restore the uart state. - */ - do { - status = clps_readl(SYSFLG(port)); - } while (status & SYSFLG_UBUSY); - - clps_writel(syscon, SYSCON(port)); -} - -static void __init -clps711xuart_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) -{ - if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { - unsigned int ubrlcr, quot; - - ubrlcr = clps_readl(UBRLCR(port)); - - *parity = 'n'; - if (ubrlcr & UBRLCR_PRTEN) { - if (ubrlcr & UBRLCR_EVENPRT) - *parity = 'e'; - else - *parity = 'o'; - } - - if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) - *bits = 7; - else - *bits = 8; - - quot = ubrlcr & UBRLCR_BAUD_MASK; - *baud = port->uartclk / (16 * (quot + 1)); - } -} - -static int __init clps711xuart_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); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - clps711xuart_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 __init clps711xuart_init(void) -{ - int ret, i; - - printk(KERN_INFO "Serial: CLPS711x driver\n"); - - ret = uart_register_driver(&clps711x_reg); - if (ret) - return ret; - - for (i = 0; i < UART_NR; i++) - uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); - - return 0; -} - -static void __exit clps711xuart_exit(void) -{ - int i; - - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); - - uart_unregister_driver(&clps711x_reg); -} - -module_init(clps711xuart_init); -module_exit(clps711xuart_exit); - -MODULE_AUTHOR("Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("CLPS-711x generic serial driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR); diff --git a/drivers/serial/cpm_uart/Makefile b/drivers/serial/cpm_uart/Makefile deleted file mode 100644 index e072724ea754..000000000000 --- a/drivers/serial/cpm_uart/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Makefile for the Motorola 8xx FEC ethernet controller -# - -obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o - -# Select the correct platform objects. -cpm_uart-objs-$(CONFIG_CPM2) += cpm_uart_cpm2.o -cpm_uart-objs-$(CONFIG_8xx) += cpm_uart_cpm1.o - -cpm_uart-objs := cpm_uart_core.o $(cpm_uart-objs-y) diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h deleted file mode 100644 index b754dcf0fda5..000000000000 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * linux/drivers/serial/cpm_uart.h - * - * Driver for CPM (SCC/SMC) serial ports - * - * Copyright (C) 2004 Freescale Semiconductor, Inc. - * - * 2006 (c) MontaVista Software, Inc. - * Vitaly Bordug - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * - */ -#ifndef CPM_UART_H -#define CPM_UART_H - -#include -#include - -#if defined(CONFIG_CPM2) -#include "cpm_uart_cpm2.h" -#elif defined(CONFIG_8xx) -#include "cpm_uart_cpm1.h" -#endif - -#define SERIAL_CPM_MAJOR 204 -#define SERIAL_CPM_MINOR 46 - -#define IS_SMC(pinfo) (pinfo->flags & FLAG_SMC) -#define IS_DISCARDING(pinfo) (pinfo->flags & FLAG_DISCARDING) -#define FLAG_DISCARDING 0x00000004 /* when set, don't discard */ -#define FLAG_SMC 0x00000002 -#define FLAG_CONSOLE 0x00000001 - -#define UART_SMC1 fsid_smc1_uart -#define UART_SMC2 fsid_smc2_uart -#define UART_SCC1 fsid_scc1_uart -#define UART_SCC2 fsid_scc2_uart -#define UART_SCC3 fsid_scc3_uart -#define UART_SCC4 fsid_scc4_uart - -#define UART_NR fs_uart_nr - -#define RX_NUM_FIFO 4 -#define RX_BUF_SIZE 32 -#define TX_NUM_FIFO 4 -#define TX_BUF_SIZE 32 - -#define SCC_WAIT_CLOSING 100 - -#define GPIO_CTS 0 -#define GPIO_RTS 1 -#define GPIO_DCD 2 -#define GPIO_DSR 3 -#define GPIO_DTR 4 -#define GPIO_RI 5 - -#define NUM_GPIOS (GPIO_RI+1) - -struct uart_cpm_port { - struct uart_port port; - u16 rx_nrfifos; - u16 rx_fifosize; - u16 tx_nrfifos; - u16 tx_fifosize; - smc_t __iomem *smcp; - smc_uart_t __iomem *smcup; - scc_t __iomem *sccp; - scc_uart_t __iomem *sccup; - cbd_t __iomem *rx_bd_base; - cbd_t __iomem *rx_cur; - cbd_t __iomem *tx_bd_base; - cbd_t __iomem *tx_cur; - unsigned char *tx_buf; - unsigned char *rx_buf; - u32 flags; - struct clk *clk; - u8 brg; - uint dp_addr; - void *mem_addr; - dma_addr_t dma_addr; - u32 mem_size; - /* wait on close if needed */ - int wait_closing; - /* value to combine with opcode to form cpm command */ - u32 command; - int gpios[NUM_GPIOS]; -}; - -extern int cpm_uart_nr; -extern struct uart_cpm_port cpm_uart_ports[UART_NR]; - -/* these are located in their respective files */ -void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd); -void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, - struct device_node *np); -void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram); -int cpm_uart_init_portdesc(void); -int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con); -void cpm_uart_freebuf(struct uart_cpm_port *pinfo); - -void smc1_lineif(struct uart_cpm_port *pinfo); -void smc2_lineif(struct uart_cpm_port *pinfo); -void scc1_lineif(struct uart_cpm_port *pinfo); -void scc2_lineif(struct uart_cpm_port *pinfo); -void scc3_lineif(struct uart_cpm_port *pinfo); -void scc4_lineif(struct uart_cpm_port *pinfo); - -/* - virtual to phys transtalion -*/ -static inline unsigned long cpu2cpm_addr(void *addr, - struct uart_cpm_port *pinfo) -{ - int offset; - u32 val = (u32)addr; - u32 mem = (u32)pinfo->mem_addr; - /* sane check */ - if (likely(val >= mem && val < mem + pinfo->mem_size)) { - offset = val - mem; - return pinfo->dma_addr + offset; - } - /* something nasty happened */ - BUG(); - return 0; -} - -static inline void *cpm2cpu_addr(unsigned long addr, - struct uart_cpm_port *pinfo) -{ - int offset; - u32 val = addr; - u32 dma = (u32)pinfo->dma_addr; - /* sane check */ - if (likely(val >= dma && val < dma + pinfo->mem_size)) { - offset = val - dma; - return pinfo->mem_addr + offset; - } - /* something nasty happened */ - BUG(); - return NULL; -} - - -#endif /* CPM_UART_H */ diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c deleted file mode 100644 index 8692ff98fc07..000000000000 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ /dev/null @@ -1,1443 +0,0 @@ -/* - * linux/drivers/serial/cpm_uart.c - * - * Driver for CPM (SCC/SMC) serial ports; core driver - * - * Based on arch/ppc/cpm2_io/uart.c by Dan Malek - * Based on ppc8xx.c by Thomas Gleixner - * Based on drivers/serial/amba.c by Russell King - * - * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) - * Pantelis Antoniou (panto@intracom.gr) (CPM1) - * - * Copyright (C) 2004, 2007 Freescale Semiconductor, Inc. - * (C) 2004 Intracom, S.A. - * (C) 2005-2006 MontaVista Software, Inc. - * Vitaly Bordug - * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#if defined(CONFIG_SERIAL_CPM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include - -#include "cpm_uart.h" - - -/**************************************************************/ - -static int cpm_uart_tx_pump(struct uart_port *port); -static void cpm_uart_init_smc(struct uart_cpm_port *pinfo); -static void cpm_uart_init_scc(struct uart_cpm_port *pinfo); -static void cpm_uart_initbd(struct uart_cpm_port *pinfo); - -/**************************************************************/ - -#define HW_BUF_SPD_THRESHOLD 9600 - -/* - * Check, if transmit buffers are processed -*/ -static unsigned int cpm_uart_tx_empty(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - cbd_t __iomem *bdp = pinfo->tx_bd_base; - int ret = 0; - - while (1) { - if (in_be16(&bdp->cbd_sc) & BD_SC_READY) - break; - - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) { - ret = TIOCSER_TEMT; - break; - } - bdp++; - } - - pr_debug("CPM uart[%d]:tx_empty: %d\n", port->line, ret); - - return ret; -} - -static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - - if (pinfo->gpios[GPIO_RTS] >= 0) - gpio_set_value(pinfo->gpios[GPIO_RTS], !(mctrl & TIOCM_RTS)); - - if (pinfo->gpios[GPIO_DTR] >= 0) - gpio_set_value(pinfo->gpios[GPIO_DTR], !(mctrl & TIOCM_DTR)); -} - -static unsigned int cpm_uart_get_mctrl(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - unsigned int mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - - if (pinfo->gpios[GPIO_CTS] >= 0) { - if (gpio_get_value(pinfo->gpios[GPIO_CTS])) - mctrl &= ~TIOCM_CTS; - } - - if (pinfo->gpios[GPIO_DSR] >= 0) { - if (gpio_get_value(pinfo->gpios[GPIO_DSR])) - mctrl &= ~TIOCM_DSR; - } - - if (pinfo->gpios[GPIO_DCD] >= 0) { - if (gpio_get_value(pinfo->gpios[GPIO_DCD])) - mctrl &= ~TIOCM_CAR; - } - - if (pinfo->gpios[GPIO_RI] >= 0) { - if (!gpio_get_value(pinfo->gpios[GPIO_RI])) - mctrl |= TIOCM_RNG; - } - - return mctrl; -} - -/* - * Stop transmitter - */ -static void cpm_uart_stop_tx(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - smc_t __iomem *smcp = pinfo->smcp; - scc_t __iomem *sccp = pinfo->sccp; - - pr_debug("CPM uart[%d]:stop tx\n", port->line); - - if (IS_SMC(pinfo)) - clrbits8(&smcp->smc_smcm, SMCM_TX); - else - clrbits16(&sccp->scc_sccm, UART_SCCM_TX); -} - -/* - * Start transmitter - */ -static void cpm_uart_start_tx(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - smc_t __iomem *smcp = pinfo->smcp; - scc_t __iomem *sccp = pinfo->sccp; - - pr_debug("CPM uart[%d]:start tx\n", port->line); - - if (IS_SMC(pinfo)) { - if (in_8(&smcp->smc_smcm) & SMCM_TX) - return; - } else { - if (in_be16(&sccp->scc_sccm) & UART_SCCM_TX) - return; - } - - if (cpm_uart_tx_pump(port) != 0) { - if (IS_SMC(pinfo)) { - setbits8(&smcp->smc_smcm, SMCM_TX); - } else { - setbits16(&sccp->scc_sccm, UART_SCCM_TX); - } - } -} - -/* - * Stop receiver - */ -static void cpm_uart_stop_rx(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - smc_t __iomem *smcp = pinfo->smcp; - scc_t __iomem *sccp = pinfo->sccp; - - pr_debug("CPM uart[%d]:stop rx\n", port->line); - - if (IS_SMC(pinfo)) - clrbits8(&smcp->smc_smcm, SMCM_RX); - else - clrbits16(&sccp->scc_sccm, UART_SCCM_RX); -} - -/* - * Enable Modem status interrupts - */ -static void cpm_uart_enable_ms(struct uart_port *port) -{ - pr_debug("CPM uart[%d]:enable ms\n", port->line); -} - -/* - * Generate a break. - */ -static void cpm_uart_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - - pr_debug("CPM uart[%d]:break ctrl, break_state: %d\n", port->line, - break_state); - - if (break_state) - cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); - else - cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX); -} - -/* - * Transmit characters, refill buffer descriptor, if possible - */ -static void cpm_uart_int_tx(struct uart_port *port) -{ - pr_debug("CPM uart[%d]:TX INT\n", port->line); - - cpm_uart_tx_pump(port); -} - -#ifdef CONFIG_CONSOLE_POLL -static int serial_polled; -#endif - -/* - * Receive characters - */ -static void cpm_uart_int_rx(struct uart_port *port) -{ - int i; - unsigned char ch; - u8 *cp; - struct tty_struct *tty = port->state->port.tty; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - cbd_t __iomem *bdp; - u16 status; - unsigned int flg; - - pr_debug("CPM uart[%d]:RX INT\n", port->line); - - /* Just loop through the closed BDs and copy the characters into - * the buffer. - */ - bdp = pinfo->rx_cur; - for (;;) { -#ifdef CONFIG_CONSOLE_POLL - if (unlikely(serial_polled)) { - serial_polled = 0; - return; - } -#endif - /* get status */ - status = in_be16(&bdp->cbd_sc); - /* If this one is empty, return happy */ - if (status & BD_SC_EMPTY) - break; - - /* get number of characters, and check spce in flip-buffer */ - i = in_be16(&bdp->cbd_datlen); - - /* If we have not enough room in tty flip buffer, then we try - * later, which will be the next rx-interrupt or a timeout - */ - if(tty_buffer_request_room(tty, i) < i) { - printk(KERN_WARNING "No room in flip buffer\n"); - return; - } - - /* get pointer */ - cp = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - - /* loop through the buffer */ - while (i-- > 0) { - ch = *cp++; - port->icount.rx++; - flg = TTY_NORMAL; - - if (status & - (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV)) - goto handle_error; - if (uart_handle_sysrq_char(port, ch)) - continue; -#ifdef CONFIG_CONSOLE_POLL - if (unlikely(serial_polled)) { - serial_polled = 0; - return; - } -#endif - error_return: - tty_insert_flip_char(tty, ch, flg); - - } /* End while (i--) */ - - /* This BD is ready to be used again. Clear status. get next */ - clrbits16(&bdp->cbd_sc, BD_SC_BR | BD_SC_FR | BD_SC_PR | - BD_SC_OV | BD_SC_ID); - setbits16(&bdp->cbd_sc, BD_SC_EMPTY); - - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = pinfo->rx_bd_base; - else - bdp++; - - } /* End for (;;) */ - - /* Write back buffer pointer */ - pinfo->rx_cur = bdp; - - /* activate BH processing */ - tty_flip_buffer_push(tty); - - return; - - /* Error processing */ - - handle_error: - /* Statistics */ - if (status & BD_SC_BR) - port->icount.brk++; - if (status & BD_SC_PR) - port->icount.parity++; - if (status & BD_SC_FR) - port->icount.frame++; - if (status & BD_SC_OV) - port->icount.overrun++; - - /* Mask out ignored conditions */ - status &= port->read_status_mask; - - /* Handle the remaining ones */ - if (status & BD_SC_BR) - flg = TTY_BREAK; - else if (status & BD_SC_PR) - flg = TTY_PARITY; - else if (status & BD_SC_FR) - flg = TTY_FRAME; - - /* overrun does not affect the current character ! */ - if (status & BD_SC_OV) { - ch = 0; - flg = TTY_OVERRUN; - /* We skip this buffer */ - /* CHECK: Is really nothing senseful there */ - /* ASSUMPTION: it contains nothing valid */ - i = 0; - } -#ifdef SUPPORT_SYSRQ - port->sysrq = 0; -#endif - goto error_return; -} - -/* - * Asynchron mode interrupt handler - */ -static irqreturn_t cpm_uart_int(int irq, void *data) -{ - u8 events; - struct uart_port *port = data; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - smc_t __iomem *smcp = pinfo->smcp; - scc_t __iomem *sccp = pinfo->sccp; - - pr_debug("CPM uart[%d]:IRQ\n", port->line); - - if (IS_SMC(pinfo)) { - events = in_8(&smcp->smc_smce); - out_8(&smcp->smc_smce, events); - if (events & SMCM_BRKE) - uart_handle_break(port); - if (events & SMCM_RX) - cpm_uart_int_rx(port); - if (events & SMCM_TX) - cpm_uart_int_tx(port); - } else { - events = in_be16(&sccp->scc_scce); - out_be16(&sccp->scc_scce, events); - if (events & UART_SCCM_BRKE) - uart_handle_break(port); - if (events & UART_SCCM_RX) - cpm_uart_int_rx(port); - if (events & UART_SCCM_TX) - cpm_uart_int_tx(port); - } - return (events) ? IRQ_HANDLED : IRQ_NONE; -} - -static int cpm_uart_startup(struct uart_port *port) -{ - int retval; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - - pr_debug("CPM uart[%d]:startup\n", port->line); - - /* If the port is not the console, make sure rx is disabled. */ - if (!(pinfo->flags & FLAG_CONSOLE)) { - /* Disable UART rx */ - if (IS_SMC(pinfo)) { - clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN); - clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX); - } else { - clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR); - clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX); - } - cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); - } - /* Install interrupt handler. */ - retval = request_irq(port->irq, cpm_uart_int, 0, "cpm_uart", port); - if (retval) - return retval; - - /* Startup rx-int */ - if (IS_SMC(pinfo)) { - setbits8(&pinfo->smcp->smc_smcm, SMCM_RX); - setbits16(&pinfo->smcp->smc_smcmr, (SMCMR_REN | SMCMR_TEN)); - } else { - setbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX); - setbits32(&pinfo->sccp->scc_gsmrl, (SCC_GSMRL_ENR | SCC_GSMRL_ENT)); - } - - return 0; -} - -inline void cpm_uart_wait_until_send(struct uart_cpm_port *pinfo) -{ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(pinfo->wait_closing); -} - -/* - * Shutdown the uart - */ -static void cpm_uart_shutdown(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - - pr_debug("CPM uart[%d]:shutdown\n", port->line); - - /* free interrupt handler */ - free_irq(port->irq, port); - - /* If the port is not the console, disable Rx and Tx. */ - if (!(pinfo->flags & FLAG_CONSOLE)) { - /* Wait for all the BDs marked sent */ - while(!cpm_uart_tx_empty(port)) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(2); - } - - if (pinfo->wait_closing) - cpm_uart_wait_until_send(pinfo); - - /* Stop uarts */ - if (IS_SMC(pinfo)) { - smc_t __iomem *smcp = pinfo->smcp; - clrbits16(&smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); - clrbits8(&smcp->smc_smcm, SMCM_RX | SMCM_TX); - } else { - scc_t __iomem *sccp = pinfo->sccp; - clrbits32(&sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); - clrbits16(&sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); - } - - /* Shut them really down and reinit buffer descriptors */ - if (IS_SMC(pinfo)) { - out_be16(&pinfo->smcup->smc_brkcr, 0); - cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); - } else { - out_be16(&pinfo->sccup->scc_brkcr, 0); - cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX); - } - - cpm_uart_initbd(pinfo); - } -} - -static void cpm_uart_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - int baud; - unsigned long flags; - u16 cval, scval, prev_mode; - int bits, sbits; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - smc_t __iomem *smcp = pinfo->smcp; - scc_t __iomem *sccp = pinfo->sccp; - - pr_debug("CPM uart[%d]:set_termios\n", port->line); - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - if (baud <= HW_BUF_SPD_THRESHOLD || - (pinfo->port.state && pinfo->port.state->port.tty->low_latency)) - pinfo->rx_fifosize = 1; - else - pinfo->rx_fifosize = RX_BUF_SIZE; - - /* Character length programmed into the mode register is the - * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, - * 1 or 2 stop bits, minus 1. - * The value 'bits' counts this for us. - */ - cval = 0; - scval = 0; - - /* byte size */ - switch (termios->c_cflag & CSIZE) { - case CS5: - bits = 5; - break; - case CS6: - bits = 6; - break; - case CS7: - bits = 7; - break; - case CS8: - bits = 8; - break; - /* Never happens, but GCC is too dumb to figure it out */ - default: - bits = 8; - break; - } - sbits = bits - 5; - - if (termios->c_cflag & CSTOPB) { - cval |= SMCMR_SL; /* Two stops */ - scval |= SCU_PSMR_SL; - bits++; - } - - if (termios->c_cflag & PARENB) { - cval |= SMCMR_PEN; - scval |= SCU_PSMR_PEN; - bits++; - if (!(termios->c_cflag & PARODD)) { - cval |= SMCMR_PM_EVEN; - scval |= (SCU_PSMR_REVP | SCU_PSMR_TEVP); - } - } - - /* - * Update the timeout - */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * Set up parity check flag - */ -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - - port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); - if (termios->c_iflag & INPCK) - port->read_status_mask |= BD_SC_FR | BD_SC_PR; - if ((termios->c_iflag & BRKINT) || (termios->c_iflag & PARMRK)) - port->read_status_mask |= BD_SC_BR; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= BD_SC_PR | BD_SC_FR; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BD_SC_BR; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= BD_SC_OV; - } - /* - * !!! ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - port->read_status_mask &= ~BD_SC_EMPTY; - - spin_lock_irqsave(&port->lock, flags); - - /* Start bit has not been added (so don't, because we would just - * subtract it later), and we need to add one for the number of - * stops bits (there is always at least one). - */ - bits++; - if (IS_SMC(pinfo)) { - /* - * MRBLR can be changed while an SMC/SCC is operating only - * if it is done in a single bus cycle with one 16-bit move - * (not two 8-bit bus cycles back-to-back). This occurs when - * the cp shifts control to the next RxBD, so the change does - * not take effect immediately. To guarantee the exact RxBD - * on which the change occurs, change MRBLR only while the - * SMC/SCC receiver is disabled. - */ - out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize); - - /* Set the mode register. We want to keep a copy of the - * enables, because we want to put them back if they were - * present. - */ - prev_mode = in_be16(&smcp->smc_smcmr) & (SMCMR_REN | SMCMR_TEN); - /* Output in *one* operation, so we don't interrupt RX/TX if they - * were already enabled. */ - out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval | - SMCMR_SM_UART | prev_mode); - } else { - out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); - out_be16(&sccp->scc_psmr, (sbits << 12) | scval); - } - - if (pinfo->clk) - clk_set_rate(pinfo->clk, baud); - else - cpm_set_brg(pinfo->brg - 1, baud); - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *cpm_uart_type(struct uart_port *port) -{ - pr_debug("CPM uart[%d]:uart_type\n", port->line); - - return port->type == PORT_CPM ? "CPM UART" : NULL; -} - -/* - * verify the new serial_struct (for TIOCSSERIAL). - */ -static int cpm_uart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - int ret = 0; - - pr_debug("CPM uart[%d]:verify_port\n", port->line); - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM) - ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) - ret = -EINVAL; - if (ser->baud_base < 9600) - ret = -EINVAL; - return ret; -} - -/* - * Transmit characters, refill buffer descriptor, if possible - */ -static int cpm_uart_tx_pump(struct uart_port *port) -{ - cbd_t __iomem *bdp; - u8 *p; - int count; - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - struct circ_buf *xmit = &port->state->xmit; - - /* Handle xon/xoff */ - if (port->x_char) { - /* Pick next descriptor and fill from buffer */ - bdp = pinfo->tx_cur; - - p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - - *p++ = port->x_char; - - out_be16(&bdp->cbd_datlen, 1); - setbits16(&bdp->cbd_sc, BD_SC_READY); - /* Get next BD. */ - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = pinfo->tx_bd_base; - else - bdp++; - pinfo->tx_cur = bdp; - - port->icount.tx++; - port->x_char = 0; - return 1; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - cpm_uart_stop_tx(port); - return 0; - } - - /* Pick next descriptor and fill from buffer */ - bdp = pinfo->tx_cur; - - while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && - xmit->tail != xmit->head) { - count = 0; - p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - while (count < pinfo->tx_fifosize) { - *p++ = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - count++; - if (xmit->head == xmit->tail) - break; - } - out_be16(&bdp->cbd_datlen, count); - setbits16(&bdp->cbd_sc, BD_SC_READY); - /* Get next BD. */ - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = pinfo->tx_bd_base; - else - bdp++; - } - pinfo->tx_cur = bdp; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) { - cpm_uart_stop_tx(port); - return 0; - } - - return 1; -} - -/* - * init buffer descriptors - */ -static void cpm_uart_initbd(struct uart_cpm_port *pinfo) -{ - int i; - u8 *mem_addr; - cbd_t __iomem *bdp; - - pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line); - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - mem_addr = pinfo->mem_addr; - bdp = pinfo->rx_cur = pinfo->rx_bd_base; - for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) { - out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); - out_be16(&bdp->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT); - mem_addr += pinfo->rx_fifosize; - } - - out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); - out_be16(&bdp->cbd_sc, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT); - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize); - bdp = pinfo->tx_cur = pinfo->tx_bd_base; - for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) { - out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); - out_be16(&bdp->cbd_sc, BD_SC_INTRPT); - mem_addr += pinfo->tx_fifosize; - } - - out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); - out_be16(&bdp->cbd_sc, BD_SC_WRAP | BD_SC_INTRPT); -} - -static void cpm_uart_init_scc(struct uart_cpm_port *pinfo) -{ - scc_t __iomem *scp; - scc_uart_t __iomem *sup; - - pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line); - - scp = pinfo->sccp; - sup = pinfo->sccup; - - /* Store address */ - out_be16(&pinfo->sccup->scc_genscc.scc_rbase, - (u8 __iomem *)pinfo->rx_bd_base - DPRAM_BASE); - out_be16(&pinfo->sccup->scc_genscc.scc_tbase, - (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE); - - /* Set up the uart parameters in the - * parameter ram. - */ - - cpm_set_scc_fcr(sup); - - out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); - out_be16(&sup->scc_maxidl, pinfo->rx_fifosize); - out_be16(&sup->scc_brkcr, 1); - out_be16(&sup->scc_parec, 0); - out_be16(&sup->scc_frmec, 0); - out_be16(&sup->scc_nosec, 0); - out_be16(&sup->scc_brkec, 0); - out_be16(&sup->scc_uaddr1, 0); - out_be16(&sup->scc_uaddr2, 0); - out_be16(&sup->scc_toseq, 0); - out_be16(&sup->scc_char1, 0x8000); - out_be16(&sup->scc_char2, 0x8000); - out_be16(&sup->scc_char3, 0x8000); - out_be16(&sup->scc_char4, 0x8000); - out_be16(&sup->scc_char5, 0x8000); - out_be16(&sup->scc_char6, 0x8000); - out_be16(&sup->scc_char7, 0x8000); - out_be16(&sup->scc_char8, 0x8000); - out_be16(&sup->scc_rccm, 0xc0ff); - - /* Send the CPM an initialize command. - */ - cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - out_be32(&scp->scc_gsmrh, 0); - out_be32(&scp->scc_gsmrl, - SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); - - /* Enable rx interrupts and clear all pending events. */ - out_be16(&scp->scc_sccm, 0); - out_be16(&scp->scc_scce, 0xffff); - out_be16(&scp->scc_dsr, 0x7e7e); - out_be16(&scp->scc_psmr, 0x3000); - - setbits32(&scp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); -} - -static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) -{ - smc_t __iomem *sp; - smc_uart_t __iomem *up; - - pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line); - - sp = pinfo->smcp; - up = pinfo->smcup; - - /* Store address */ - out_be16(&pinfo->smcup->smc_rbase, - (u8 __iomem *)pinfo->rx_bd_base - DPRAM_BASE); - out_be16(&pinfo->smcup->smc_tbase, - (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE); - -/* - * In case SMC1 is being relocated... - */ -#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH) - out_be16(&up->smc_rbptr, in_be16(&pinfo->smcup->smc_rbase)); - out_be16(&up->smc_tbptr, in_be16(&pinfo->smcup->smc_tbase)); - out_be32(&up->smc_rstate, 0); - out_be32(&up->smc_tstate, 0); - out_be16(&up->smc_brkcr, 1); /* number of break chars */ - out_be16(&up->smc_brkec, 0); -#endif - - /* Set up the uart parameters in the - * parameter ram. - */ - cpm_set_smc_fcr(up); - - /* Using idle character time requires some additional tuning. */ - out_be16(&up->smc_mrblr, pinfo->rx_fifosize); - out_be16(&up->smc_maxidl, pinfo->rx_fifosize); - out_be16(&up->smc_brklen, 0); - out_be16(&up->smc_brkec, 0); - out_be16(&up->smc_brkcr, 1); - - cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); - - /* Set UART mode, 8 bit, no parity, one stop. - * Enable receive and transmit. - */ - out_be16(&sp->smc_smcmr, smcr_mk_clen(9) | SMCMR_SM_UART); - - /* Enable only rx interrupts clear all pending events. */ - out_8(&sp->smc_smcm, 0); - out_8(&sp->smc_smce, 0xff); - - setbits16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN); -} - -/* - * Initialize port. This is called from early_console stuff - * so we have to be careful here ! - */ -static int cpm_uart_request_port(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - int ret; - - pr_debug("CPM uart[%d]:request port\n", port->line); - - if (pinfo->flags & FLAG_CONSOLE) - return 0; - - if (IS_SMC(pinfo)) { - clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX); - clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); - } else { - clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); - clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); - } - - ret = cpm_uart_allocbuf(pinfo, 0); - - if (ret) - return ret; - - cpm_uart_initbd(pinfo); - if (IS_SMC(pinfo)) - cpm_uart_init_smc(pinfo); - else - cpm_uart_init_scc(pinfo); - - return 0; -} - -static void cpm_uart_release_port(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - - if (!(pinfo->flags & FLAG_CONSOLE)) - cpm_uart_freebuf(pinfo); -} - -/* - * Configure/autoconfigure the port. - */ -static void cpm_uart_config_port(struct uart_port *port, int flags) -{ - pr_debug("CPM uart[%d]:config_port\n", port->line); - - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_CPM; - cpm_uart_request_port(port); - } -} - -#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_CPM_CONSOLE) -/* - * Write a string to the serial port - * Note that this is called with interrupts already disabled - */ -static void cpm_uart_early_write(struct uart_cpm_port *pinfo, - const char *string, u_int count) -{ - unsigned int i; - cbd_t __iomem *bdp, *bdbase; - unsigned char *cpm_outp_addr; - - /* Get the address of the host memory buffer. - */ - bdp = pinfo->tx_cur; - bdbase = pinfo->tx_bd_base; - - /* - * Now, do each character. This is not as bad as it looks - * since this is a holding FIFO and not a transmitting FIFO. - * We could add the complexity of filling the entire transmit - * buffer, but we would just wait longer between accesses...... - */ - for (i = 0; i < count; i++, string++) { - /* Wait for transmitter fifo to empty. - * Ready indicates output is ready, and xmt is doing - * that, not that it is ready for us to send. - */ - while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) - ; - - /* Send the character out. - * If the buffer address is in the CPM DPRAM, don't - * convert it. - */ - cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), - pinfo); - *cpm_outp_addr = *string; - - out_be16(&bdp->cbd_datlen, 1); - setbits16(&bdp->cbd_sc, BD_SC_READY); - - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - - /* if a LF, also do CR... */ - if (*string == 10) { - while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) - ; - - cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), - pinfo); - *cpm_outp_addr = 13; - - out_be16(&bdp->cbd_datlen, 1); - setbits16(&bdp->cbd_sc, BD_SC_READY); - - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - } - } - - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) - ; - - pinfo->tx_cur = bdp; -} -#endif - -#ifdef CONFIG_CONSOLE_POLL -/* Serial polling routines for writing and reading from the uart while - * in an interrupt or debug context. - */ - -#define GDB_BUF_SIZE 512 /* power of 2, please */ - -static char poll_buf[GDB_BUF_SIZE]; -static char *pollp; -static int poll_chars; - -static int poll_wait_key(char *obuf, struct uart_cpm_port *pinfo) -{ - u_char c, *cp; - volatile cbd_t *bdp; - int i; - - /* Get the address of the host memory buffer. - */ - bdp = pinfo->rx_cur; - while (bdp->cbd_sc & BD_SC_EMPTY) - ; - - /* If the buffer address is in the CPM DPRAM, don't - * convert it. - */ - cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); - - if (obuf) { - i = c = bdp->cbd_datlen; - while (i-- > 0) - *obuf++ = *cp++; - } else - c = *cp; - bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID); - bdp->cbd_sc |= BD_SC_EMPTY; - - if (bdp->cbd_sc & BD_SC_WRAP) - bdp = pinfo->rx_bd_base; - else - bdp++; - pinfo->rx_cur = (cbd_t *)bdp; - - return (int)c; -} - -static int cpm_get_poll_char(struct uart_port *port) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - - if (!serial_polled) { - serial_polled = 1; - poll_chars = 0; - } - if (poll_chars <= 0) { - poll_chars = poll_wait_key(poll_buf, pinfo); - pollp = poll_buf; - } - poll_chars--; - return *pollp++; -} - -static void cpm_put_poll_char(struct uart_port *port, - unsigned char c) -{ - struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - static char ch[2]; - - ch[0] = (char)c; - cpm_uart_early_write(pinfo, ch, 1); -} -#endif /* CONFIG_CONSOLE_POLL */ - -static struct uart_ops cpm_uart_pops = { - .tx_empty = cpm_uart_tx_empty, - .set_mctrl = cpm_uart_set_mctrl, - .get_mctrl = cpm_uart_get_mctrl, - .stop_tx = cpm_uart_stop_tx, - .start_tx = cpm_uart_start_tx, - .stop_rx = cpm_uart_stop_rx, - .enable_ms = cpm_uart_enable_ms, - .break_ctl = cpm_uart_break_ctl, - .startup = cpm_uart_startup, - .shutdown = cpm_uart_shutdown, - .set_termios = cpm_uart_set_termios, - .type = cpm_uart_type, - .release_port = cpm_uart_release_port, - .request_port = cpm_uart_request_port, - .config_port = cpm_uart_config_port, - .verify_port = cpm_uart_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = cpm_get_poll_char, - .poll_put_char = cpm_put_poll_char, -#endif -}; - -struct uart_cpm_port cpm_uart_ports[UART_NR]; - -static int cpm_uart_init_port(struct device_node *np, - struct uart_cpm_port *pinfo) -{ - const u32 *data; - void __iomem *mem, *pram; - int len; - int ret; - int i; - - data = of_get_property(np, "clock", NULL); - if (data) { - struct clk *clk = clk_get(NULL, (const char*)data); - if (!IS_ERR(clk)) - pinfo->clk = clk; - } - if (!pinfo->clk) { - data = of_get_property(np, "fsl,cpm-brg", &len); - if (!data || len != 4) { - printk(KERN_ERR "CPM UART %s has no/invalid " - "fsl,cpm-brg property.\n", np->name); - return -EINVAL; - } - pinfo->brg = *data; - } - - data = of_get_property(np, "fsl,cpm-command", &len); - if (!data || len != 4) { - printk(KERN_ERR "CPM UART %s has no/invalid " - "fsl,cpm-command property.\n", np->name); - return -EINVAL; - } - pinfo->command = *data; - - mem = of_iomap(np, 0); - if (!mem) - return -ENOMEM; - - if (of_device_is_compatible(np, "fsl,cpm1-scc-uart") || - of_device_is_compatible(np, "fsl,cpm2-scc-uart")) { - pinfo->sccp = mem; - pinfo->sccup = pram = cpm_uart_map_pram(pinfo, np); - } else if (of_device_is_compatible(np, "fsl,cpm1-smc-uart") || - of_device_is_compatible(np, "fsl,cpm2-smc-uart")) { - pinfo->flags |= FLAG_SMC; - pinfo->smcp = mem; - pinfo->smcup = pram = cpm_uart_map_pram(pinfo, np); - } else { - ret = -ENODEV; - goto out_mem; - } - - if (!pram) { - ret = -ENOMEM; - goto out_mem; - } - - pinfo->tx_nrfifos = TX_NUM_FIFO; - pinfo->tx_fifosize = TX_BUF_SIZE; - pinfo->rx_nrfifos = RX_NUM_FIFO; - pinfo->rx_fifosize = RX_BUF_SIZE; - - pinfo->port.uartclk = ppc_proc_freq; - pinfo->port.mapbase = (unsigned long)mem; - pinfo->port.type = PORT_CPM; - pinfo->port.ops = &cpm_uart_pops, - pinfo->port.iotype = UPIO_MEM; - pinfo->port.fifosize = pinfo->tx_nrfifos * pinfo->tx_fifosize; - spin_lock_init(&pinfo->port.lock); - - pinfo->port.irq = of_irq_to_resource(np, 0, NULL); - if (pinfo->port.irq == NO_IRQ) { - ret = -EINVAL; - goto out_pram; - } - - for (i = 0; i < NUM_GPIOS; i++) - pinfo->gpios[i] = of_get_gpio(np, i); - -#ifdef CONFIG_PPC_EARLY_DEBUG_CPM - udbg_putc = NULL; -#endif - - return cpm_uart_request_port(&pinfo->port); - -out_pram: - cpm_uart_unmap_pram(pinfo, pram); -out_mem: - iounmap(mem); - return ret; -} - -#ifdef CONFIG_SERIAL_CPM_CONSOLE -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - * - * Note that this is called with interrupts already disabled - */ -static void cpm_uart_console_write(struct console *co, const char *s, - u_int count) -{ - struct uart_cpm_port *pinfo = &cpm_uart_ports[co->index]; - unsigned long flags; - int nolock = oops_in_progress; - - if (unlikely(nolock)) { - local_irq_save(flags); - } else { - spin_lock_irqsave(&pinfo->port.lock, flags); - } - - cpm_uart_early_write(pinfo, s, count); - - if (unlikely(nolock)) { - local_irq_restore(flags); - } else { - spin_unlock_irqrestore(&pinfo->port.lock, flags); - } -} - - -static int __init cpm_uart_console_setup(struct console *co, char *options) -{ - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - struct uart_cpm_port *pinfo; - struct uart_port *port; - - struct device_node *np = NULL; - int i = 0; - - if (co->index >= UART_NR) { - printk(KERN_ERR "cpm_uart: console index %d too high\n", - co->index); - return -ENODEV; - } - - do { - np = of_find_node_by_type(np, "serial"); - if (!np) - return -ENODEV; - - if (!of_device_is_compatible(np, "fsl,cpm1-smc-uart") && - !of_device_is_compatible(np, "fsl,cpm1-scc-uart") && - !of_device_is_compatible(np, "fsl,cpm2-smc-uart") && - !of_device_is_compatible(np, "fsl,cpm2-scc-uart")) - i--; - } while (i++ != co->index); - - pinfo = &cpm_uart_ports[co->index]; - - pinfo->flags |= FLAG_CONSOLE; - port = &pinfo->port; - - ret = cpm_uart_init_port(np, pinfo); - of_node_put(np); - if (ret) - return ret; - - if (options) { - uart_parse_options(options, &baud, &parity, &bits, &flow); - } else { - if ((baud = uart_baudrate()) == -1) - baud = 9600; - } - - if (IS_SMC(pinfo)) { - out_be16(&pinfo->smcup->smc_brkcr, 0); - cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); - clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX); - clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); - } else { - out_be16(&pinfo->sccup->scc_brkcr, 0); - cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX); - clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); - clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); - } - - ret = cpm_uart_allocbuf(pinfo, 1); - - if (ret) - return ret; - - cpm_uart_initbd(pinfo); - - if (IS_SMC(pinfo)) - cpm_uart_init_smc(pinfo); - else - cpm_uart_init_scc(pinfo); - - uart_set_options(port, co, baud, parity, bits, flow); - cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX); - - return 0; -} - -static struct uart_driver cpm_reg; -static struct console cpm_scc_uart_console = { - .name = "ttyCPM", - .write = cpm_uart_console_write, - .device = uart_console_device, - .setup = cpm_uart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &cpm_reg, -}; - -static int __init cpm_uart_console_init(void) -{ - register_console(&cpm_scc_uart_console); - return 0; -} - -console_initcall(cpm_uart_console_init); - -#define CPM_UART_CONSOLE &cpm_scc_uart_console -#else -#define CPM_UART_CONSOLE NULL -#endif - -static struct uart_driver cpm_reg = { - .owner = THIS_MODULE, - .driver_name = "ttyCPM", - .dev_name = "ttyCPM", - .major = SERIAL_CPM_MAJOR, - .minor = SERIAL_CPM_MINOR, - .cons = CPM_UART_CONSOLE, - .nr = UART_NR, -}; - -static int probe_index; - -static int __devinit cpm_uart_probe(struct platform_device *ofdev, - const struct of_device_id *match) -{ - int index = probe_index++; - struct uart_cpm_port *pinfo = &cpm_uart_ports[index]; - int ret; - - pinfo->port.line = index; - - if (index >= UART_NR) - return -ENODEV; - - dev_set_drvdata(&ofdev->dev, pinfo); - - /* initialize the device pointer for the port */ - pinfo->port.dev = &ofdev->dev; - - ret = cpm_uart_init_port(ofdev->dev.of_node, pinfo); - if (ret) - return ret; - - return uart_add_one_port(&cpm_reg, &pinfo->port); -} - -static int __devexit 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); -} - -static struct of_device_id cpm_uart_match[] = { - { - .compatible = "fsl,cpm1-smc-uart", - }, - { - .compatible = "fsl,cpm1-scc-uart", - }, - { - .compatible = "fsl,cpm2-smc-uart", - }, - { - .compatible = "fsl,cpm2-scc-uart", - }, - {} -}; - -static struct of_platform_driver cpm_uart_driver = { - .driver = { - .name = "cpm_uart", - .owner = THIS_MODULE, - .of_match_table = cpm_uart_match, - }, - .probe = cpm_uart_probe, - .remove = cpm_uart_remove, - }; - -static int __init cpm_uart_init(void) -{ - int ret = uart_register_driver(&cpm_reg); - if (ret) - return ret; - - ret = of_register_platform_driver(&cpm_uart_driver); - if (ret) - uart_unregister_driver(&cpm_reg); - - return ret; -} - -static void __exit cpm_uart_exit(void) -{ - of_unregister_platform_driver(&cpm_uart_driver); - uart_unregister_driver(&cpm_reg); -} - -module_init(cpm_uart_init); -module_exit(cpm_uart_exit); - -MODULE_AUTHOR("Kumar Gala/Antoniou Pantelis"); -MODULE_DESCRIPTION("CPM SCC/SMC port driver $Revision: 0.01 $"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(SERIAL_CPM_MAJOR, SERIAL_CPM_MINOR); diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c deleted file mode 100644 index 3fc1d66e32c6..000000000000 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * linux/drivers/serial/cpm_uart.c - * - * Driver for CPM (SCC/SMC) serial ports; CPM1 definitions - * - * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) - * Pantelis Antoniou (panto@intracom.gr) (CPM1) - * - * Copyright (C) 2004 Freescale Semiconductor, Inc. - * (C) 2004 Intracom, S.A. - * (C) 2006 MontaVista Software, Inc. - * Vitaly Bordug - * - * 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 -#include - -#include -#include -#include - -#include -#include - -#include - -#include "cpm_uart.h" - -/**************************************************************/ - -void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd) -{ - cpm_command(port->command, cmd); -} - -void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, - struct device_node *np) -{ - return of_iomap(np, 1); -} - -void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram) -{ - iounmap(pram); -} - -/* - * Allocate DP-Ram and memory buffers. We need to allocate a transmit and - * receive buffer descriptors from dual port ram, and a character - * buffer area from host mem. If we are allocating for the console we need - * to do it from bootmem - */ -int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) -{ - int dpmemsz, memsz; - u8 *dp_mem; - unsigned long dp_offset; - u8 *mem_addr; - dma_addr_t dma_addr = 0; - - pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line); - - dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); - dp_offset = cpm_dpalloc(dpmemsz, 8); - if (IS_ERR_VALUE(dp_offset)) { - printk(KERN_ERR - "cpm_uart_cpm1.c: could not allocate buffer descriptors\n"); - return -ENOMEM; - } - dp_mem = cpm_dpram_addr(dp_offset); - - memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + - L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); - if (is_con) { - /* was hostalloc but changed cause it blows away the */ - /* large tlb mapping when pinning the kernel area */ - mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8)); - dma_addr = (u32)cpm_dpram_phys(mem_addr); - } else - mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr, - GFP_KERNEL); - - if (mem_addr == NULL) { - cpm_dpfree(dp_offset); - printk(KERN_ERR - "cpm_uart_cpm1.c: could not allocate coherent memory\n"); - return -ENOMEM; - } - - pinfo->dp_addr = dp_offset; - pinfo->mem_addr = mem_addr; /* virtual address*/ - pinfo->dma_addr = dma_addr; /* physical address*/ - pinfo->mem_size = memsz; - - pinfo->rx_buf = mem_addr; - pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos - * pinfo->rx_fifosize); - - pinfo->rx_bd_base = (cbd_t __iomem __force *)dp_mem; - pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos; - - return 0; -} - -void cpm_uart_freebuf(struct uart_cpm_port *pinfo) -{ - dma_free_coherent(pinfo->port.dev, L1_CACHE_ALIGN(pinfo->rx_nrfifos * - pinfo->rx_fifosize) + - L1_CACHE_ALIGN(pinfo->tx_nrfifos * - pinfo->tx_fifosize), pinfo->mem_addr, - pinfo->dma_addr); - - cpm_dpfree(pinfo->dp_addr); -} diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.h b/drivers/serial/cpm_uart/cpm_uart_cpm1.h deleted file mode 100644 index 10eecd6af6d4..000000000000 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * linux/drivers/serial/cpm_uart/cpm_uart_cpm1.h - * - * Driver for CPM (SCC/SMC) serial ports - * - * definitions for cpm1 - * - */ - -#ifndef CPM_UART_CPM1_H -#define CPM_UART_CPM1_H - -#include - -static inline void cpm_set_brg(int brg, int baud) -{ - cpm_setbrg(brg, baud); -} - -static inline void cpm_set_scc_fcr(scc_uart_t __iomem * sup) -{ - out_8(&sup->scc_genscc.scc_rfcr, SMC_EB); - out_8(&sup->scc_genscc.scc_tfcr, SMC_EB); -} - -static inline void cpm_set_smc_fcr(smc_uart_t __iomem * up) -{ - out_8(&up->smc_rfcr, SMC_EB); - out_8(&up->smc_tfcr, SMC_EB); -} - -#define DPRAM_BASE ((u8 __iomem __force *)cpm_dpram_addr(0)) - -#endif diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c deleted file mode 100644 index 814ac006393f..000000000000 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * linux/drivers/serial/cpm_uart_cpm2.c - * - * Driver for CPM (SCC/SMC) serial ports; CPM2 definitions - * - * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) - * Pantelis Antoniou (panto@intracom.gr) (CPM1) - * - * Copyright (C) 2004 Freescale Semiconductor, Inc. - * (C) 2004 Intracom, S.A. - * (C) 2006 MontaVista Software, Inc. - * Vitaly Bordug - * - * 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 -#include - -#include -#include -#include -#include - -#include -#include - -#include "cpm_uart.h" - -/**************************************************************/ - -void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd) -{ - cpm_command(port->command, cmd); -} - -void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, - struct device_node *np) -{ - void __iomem *pram; - unsigned long offset; - struct resource res; - resource_size_t len; - - /* Don't remap parameter RAM if it has already been initialized - * during console setup. - */ - if (IS_SMC(port) && port->smcup) - return port->smcup; - else if (!IS_SMC(port) && port->sccup) - return port->sccup; - - if (of_address_to_resource(np, 1, &res)) - return NULL; - - len = resource_size(&res); - pram = ioremap(res.start, len); - if (!pram) - return NULL; - - if (!IS_SMC(port)) - return pram; - - if (len != 2) { - printk(KERN_WARNING "cpm_uart[%d]: device tree references " - "SMC pram, using boot loader/wrapper pram mapping. " - "Please fix your device tree to reference the pram " - "base register instead.\n", - port->port.line); - return pram; - } - - offset = cpm_dpalloc(PROFF_SMC_SIZE, 64); - out_be16(pram, offset); - iounmap(pram); - return cpm_muram_addr(offset); -} - -void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram) -{ - if (!IS_SMC(port)) - iounmap(pram); -} - -/* - * Allocate DP-Ram and memory buffers. We need to allocate a transmit and - * receive buffer descriptors from dual port ram, and a character - * buffer area from host mem. If we are allocating for the console we need - * to do it from bootmem - */ -int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) -{ - int dpmemsz, memsz; - u8 __iomem *dp_mem; - unsigned long dp_offset; - u8 *mem_addr; - dma_addr_t dma_addr = 0; - - pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line); - - dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); - dp_offset = cpm_dpalloc(dpmemsz, 8); - if (IS_ERR_VALUE(dp_offset)) { - printk(KERN_ERR - "cpm_uart_cpm.c: could not allocate buffer descriptors\n"); - return -ENOMEM; - } - - dp_mem = cpm_dpram_addr(dp_offset); - - memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + - L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); - if (is_con) { - mem_addr = kzalloc(memsz, GFP_NOWAIT); - dma_addr = virt_to_bus(mem_addr); - } - else - mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr, - GFP_KERNEL); - - if (mem_addr == NULL) { - cpm_dpfree(dp_offset); - printk(KERN_ERR - "cpm_uart_cpm.c: could not allocate coherent memory\n"); - return -ENOMEM; - } - - pinfo->dp_addr = dp_offset; - pinfo->mem_addr = mem_addr; - pinfo->dma_addr = dma_addr; - pinfo->mem_size = memsz; - - pinfo->rx_buf = mem_addr; - pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos - * pinfo->rx_fifosize); - - pinfo->rx_bd_base = (cbd_t __iomem *)dp_mem; - pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos; - - return 0; -} - -void cpm_uart_freebuf(struct uart_cpm_port *pinfo) -{ - dma_free_coherent(pinfo->port.dev, L1_CACHE_ALIGN(pinfo->rx_nrfifos * - pinfo->rx_fifosize) + - L1_CACHE_ALIGN(pinfo->tx_nrfifos * - pinfo->tx_fifosize), (void __force *)pinfo->mem_addr, - pinfo->dma_addr); - - cpm_dpfree(pinfo->dp_addr); -} diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.h b/drivers/serial/cpm_uart/cpm_uart_cpm2.h deleted file mode 100644 index 7194c63dcf5f..000000000000 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * linux/drivers/serial/cpm_uart/cpm_uart_cpm2.h - * - * Driver for CPM (SCC/SMC) serial ports - * - * definitions for cpm2 - * - */ - -#ifndef CPM_UART_CPM2_H -#define CPM_UART_CPM2_H - -#include - -static inline void cpm_set_brg(int brg, int baud) -{ - cpm_setbrg(brg, baud); -} - -static inline void cpm_set_scc_fcr(scc_uart_t __iomem *sup) -{ - out_8(&sup->scc_genscc.scc_rfcr, CPMFCR_GBL | CPMFCR_EB); - out_8(&sup->scc_genscc.scc_tfcr, CPMFCR_GBL | CPMFCR_EB); -} - -static inline void cpm_set_smc_fcr(smc_uart_t __iomem *up) -{ - out_8(&up->smc_rfcr, CPMFCR_GBL | CPMFCR_EB); - out_8(&up->smc_tfcr, CPMFCR_GBL | CPMFCR_EB); -} - -#define DPRAM_BASE ((u8 __iomem __force *)cpm_dpram_addr(0)) - -#endif diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c deleted file mode 100644 index bcc31f2140ac..000000000000 --- a/drivers/serial/crisv10.c +++ /dev/null @@ -1,4573 +0,0 @@ -/* - * Serial port driver for the ETRAX 100LX chip - * - * Copyright (C) 1998-2007 Axis Communications AB - * - * Many, many authors. Based once upon a time on serial.c for 16x50. - * - */ - -static char *serial_version = "$Revision: 1.25 $"; - -#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 - -#include - -/* non-arch dependent serial structures are in linux/serial.h */ -#include -/* while we keep our own stuff (struct e100_serial) in a local .h file */ -#include "crisv10.h" -#include -#include - -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER -#ifndef CONFIG_ETRAX_FAST_TIMER -#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER" -#endif -#endif - -#if defined(CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS) && \ - (CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS == 0) -#error "RX_TIMEOUT_TICKS == 0 not allowed, use 1" -#endif - -#if defined(CONFIG_ETRAX_RS485_ON_PA) && defined(CONFIG_ETRAX_RS485_ON_PORT_G) -#error "Disable either CONFIG_ETRAX_RS485_ON_PA or CONFIG_ETRAX_RS485_ON_PORT_G" -#endif - -/* - * All of the compatibilty code so we can compile serial.c against - * older kernels is hidden in serial_compat.h - */ -#if defined(LOCAL_HEADERS) -#include "serial_compat.h" -#endif - -struct tty_driver *serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -//#define SERIAL_DEBUG_INTR -//#define SERIAL_DEBUG_OPEN -//#define SERIAL_DEBUG_FLOW -//#define SERIAL_DEBUG_DATA -//#define SERIAL_DEBUG_THROTTLE -//#define SERIAL_DEBUG_IO /* Debug for Extra control and status pins */ -//#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */ - -/* Enable this to use serial interrupts to handle when you - expect the first received event on the serial port to - be an error, break or similar. Used to be able to flash IRMA - from eLinux */ -#define SERIAL_HANDLE_EARLY_ERRORS - -/* Currently 16 descriptors x 128 bytes = 2048 bytes */ -#define SERIAL_DESCR_BUF_SIZE 256 - -#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */ -#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE - -/* We don't want to load the system with massive fast timer interrupt - * on high baudrates so limit it to 250 us (4kHz) */ -#define MIN_FLUSH_TIME_USEC 250 - -/* Add an x here to log a lot of timer stuff */ -#define TIMERD(x) -/* Debug details of interrupt handling */ -#define DINTR1(x) /* irq on/off, errors */ -#define DINTR2(x) /* tx and rx */ -/* Debug flip buffer stuff */ -#define DFLIP(x) -/* Debug flow control and overview of data flow */ -#define DFLOW(x) -#define DBAUD(x) -#define DLOG_INT_TRIG(x) - -//#define DEBUG_LOG_INCLUDED -#ifndef DEBUG_LOG_INCLUDED -#define DEBUG_LOG(line, string, value) -#else -struct debug_log_info -{ - unsigned long time; - unsigned long timer_data; -// int line; - const char *string; - int value; -}; -#define DEBUG_LOG_SIZE 4096 - -struct debug_log_info debug_log[DEBUG_LOG_SIZE]; -int debug_log_pos = 0; - -#define DEBUG_LOG(_line, _string, _value) do { \ - if ((_line) == SERIAL_DEBUG_LINE) {\ - debug_log_func(_line, _string, _value); \ - }\ -}while(0) - -void debug_log_func(int line, const char *string, int value) -{ - if (debug_log_pos < DEBUG_LOG_SIZE) { - debug_log[debug_log_pos].time = jiffies; - debug_log[debug_log_pos].timer_data = *R_TIMER_DATA; -// debug_log[debug_log_pos].line = line; - debug_log[debug_log_pos].string = string; - debug_log[debug_log_pos].value = value; - debug_log_pos++; - } - /*printk(string, value);*/ -} -#endif - -#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS -/* Default number of timer ticks before flushing rx fifo - * When using "little data, low latency applications: use 0 - * When using "much data applications (PPP)" use ~5 - */ -#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 -#endif - -unsigned long timer_data_to_ns(unsigned long timer_data); - -static void change_speed(struct e100_serial *info); -static void rs_throttle(struct tty_struct * tty); -static void rs_wait_until_sent(struct tty_struct *tty, int timeout); -static int rs_write(struct tty_struct *tty, - const unsigned char *buf, int count); -#ifdef CONFIG_ETRAX_RS485 -static int e100_write_rs485(struct tty_struct *tty, - const unsigned char *buf, int count); -#endif -static int get_lsr_info(struct e100_serial *info, unsigned int *value); - - -#define DEF_BAUD 115200 /* 115.2 kbit/s */ -#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) -#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ -/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ -#define DEF_TX 0x80 /* or SERIAL_CTRL_B */ - -/* offsets from R_SERIALx_CTRL */ - -#define REG_DATA 0 -#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */ -#define REG_TR_DATA 0 -#define REG_STATUS 1 -#define REG_TR_CTRL 1 -#define REG_REC_CTRL 2 -#define REG_BAUD 3 -#define REG_XOFF 4 /* this is a 32 bit register */ - -/* The bitfields are the same for all serial ports */ -#define SER_RXD_MASK IO_MASK(R_SERIAL0_STATUS, rxd) -#define SER_DATA_AVAIL_MASK IO_MASK(R_SERIAL0_STATUS, data_avail) -#define SER_FRAMING_ERR_MASK IO_MASK(R_SERIAL0_STATUS, framing_err) -#define SER_PAR_ERR_MASK IO_MASK(R_SERIAL0_STATUS, par_err) -#define SER_OVERRUN_MASK IO_MASK(R_SERIAL0_STATUS, overrun) - -#define SER_ERROR_MASK (SER_OVERRUN_MASK | SER_PAR_ERR_MASK | SER_FRAMING_ERR_MASK) - -/* Values for info->errorcode */ -#define ERRCODE_SET_BREAK (TTY_BREAK) -#define ERRCODE_INSERT 0x100 -#define ERRCODE_INSERT_BREAK (ERRCODE_INSERT | TTY_BREAK) - -#define FORCE_EOP(info) *R_SET_EOP = 1U << info->iseteop; - -/* - * General note regarding the use of IO_* macros in this file: - * - * We will use the bits defined for DMA channel 6 when using various - * IO_* macros (e.g. IO_STATE, IO_MASK, IO_EXTRACT) and _assume_ they are - * the same for all channels (which of course they are). - * - * We will also use the bits defined for serial port 0 when writing commands - * to the different ports, as these bits too are the same for all ports. - */ - - -/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */ -static const unsigned long e100_ser_int_mask = 0 -#ifdef CONFIG_ETRAX_SERIAL_PORT0 -| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready) -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT1 -| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready) -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT2 -| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready) -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT3 -| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready) -#endif -; -unsigned long r_alt_ser_baudrate_shadow = 0; - -/* this is the data for the four serial ports in the etrax100 */ -/* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */ -/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */ - -static struct e100_serial rs_table[] = { - { .baud = DEF_BAUD, - .ioport = (unsigned char *)R_SERIAL0_CTRL, - .irq = 1U << 12, /* uses DMA 6 and 7 */ - .oclrintradr = R_DMA_CH6_CLR_INTR, - .ofirstadr = R_DMA_CH6_FIRST, - .ocmdadr = R_DMA_CH6_CMD, - .ostatusadr = R_DMA_CH6_STATUS, - .iclrintradr = R_DMA_CH7_CLR_INTR, - .ifirstadr = R_DMA_CH7_FIRST, - .icmdadr = R_DMA_CH7_CMD, - .idescradr = R_DMA_CH7_DESCR, - .flags = STD_FLAGS, - .rx_ctrl = DEF_RX, - .tx_ctrl = DEF_TX, - .iseteop = 2, - .dma_owner = dma_ser0, - .io_if = if_serial_0, -#ifdef CONFIG_ETRAX_SERIAL_PORT0 - .enabled = 1, -#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT - .dma_out_enabled = 1, - .dma_out_nbr = SER0_TX_DMA_NBR, - .dma_out_irq_nbr = SER0_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, - .dma_out_irq_description = "serial 0 dma tr", -#else - .dma_out_enabled = 0, - .dma_out_nbr = UINT_MAX, - .dma_out_irq_nbr = 0, - .dma_out_irq_flags = 0, - .dma_out_irq_description = NULL, -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN - .dma_in_enabled = 1, - .dma_in_nbr = SER0_RX_DMA_NBR, - .dma_in_irq_nbr = SER0_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, - .dma_in_irq_description = "serial 0 dma rec", -#else - .dma_in_enabled = 0, - .dma_in_nbr = UINT_MAX, - .dma_in_irq_nbr = 0, - .dma_in_irq_flags = 0, - .dma_in_irq_description = NULL, -#endif -#else - .enabled = 0, - .io_if_description = NULL, - .dma_out_enabled = 0, - .dma_in_enabled = 0 -#endif - -}, /* ttyS0 */ -#ifndef CONFIG_SVINTO_SIM - { .baud = DEF_BAUD, - .ioport = (unsigned char *)R_SERIAL1_CTRL, - .irq = 1U << 16, /* uses DMA 8 and 9 */ - .oclrintradr = R_DMA_CH8_CLR_INTR, - .ofirstadr = R_DMA_CH8_FIRST, - .ocmdadr = R_DMA_CH8_CMD, - .ostatusadr = R_DMA_CH8_STATUS, - .iclrintradr = R_DMA_CH9_CLR_INTR, - .ifirstadr = R_DMA_CH9_FIRST, - .icmdadr = R_DMA_CH9_CMD, - .idescradr = R_DMA_CH9_DESCR, - .flags = STD_FLAGS, - .rx_ctrl = DEF_RX, - .tx_ctrl = DEF_TX, - .iseteop = 3, - .dma_owner = dma_ser1, - .io_if = if_serial_1, -#ifdef CONFIG_ETRAX_SERIAL_PORT1 - .enabled = 1, - .io_if_description = "ser1", -#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT - .dma_out_enabled = 1, - .dma_out_nbr = SER1_TX_DMA_NBR, - .dma_out_irq_nbr = SER1_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, - .dma_out_irq_description = "serial 1 dma tr", -#else - .dma_out_enabled = 0, - .dma_out_nbr = UINT_MAX, - .dma_out_irq_nbr = 0, - .dma_out_irq_flags = 0, - .dma_out_irq_description = NULL, -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN - .dma_in_enabled = 1, - .dma_in_nbr = SER1_RX_DMA_NBR, - .dma_in_irq_nbr = SER1_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, - .dma_in_irq_description = "serial 1 dma rec", -#else - .dma_in_enabled = 0, - .dma_in_enabled = 0, - .dma_in_nbr = UINT_MAX, - .dma_in_irq_nbr = 0, - .dma_in_irq_flags = 0, - .dma_in_irq_description = NULL, -#endif -#else - .enabled = 0, - .io_if_description = NULL, - .dma_in_irq_nbr = 0, - .dma_out_enabled = 0, - .dma_in_enabled = 0 -#endif -}, /* ttyS1 */ - - { .baud = DEF_BAUD, - .ioport = (unsigned char *)R_SERIAL2_CTRL, - .irq = 1U << 4, /* uses DMA 2 and 3 */ - .oclrintradr = R_DMA_CH2_CLR_INTR, - .ofirstadr = R_DMA_CH2_FIRST, - .ocmdadr = R_DMA_CH2_CMD, - .ostatusadr = R_DMA_CH2_STATUS, - .iclrintradr = R_DMA_CH3_CLR_INTR, - .ifirstadr = R_DMA_CH3_FIRST, - .icmdadr = R_DMA_CH3_CMD, - .idescradr = R_DMA_CH3_DESCR, - .flags = STD_FLAGS, - .rx_ctrl = DEF_RX, - .tx_ctrl = DEF_TX, - .iseteop = 0, - .dma_owner = dma_ser2, - .io_if = if_serial_2, -#ifdef CONFIG_ETRAX_SERIAL_PORT2 - .enabled = 1, - .io_if_description = "ser2", -#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT - .dma_out_enabled = 1, - .dma_out_nbr = SER2_TX_DMA_NBR, - .dma_out_irq_nbr = SER2_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, - .dma_out_irq_description = "serial 2 dma tr", -#else - .dma_out_enabled = 0, - .dma_out_nbr = UINT_MAX, - .dma_out_irq_nbr = 0, - .dma_out_irq_flags = 0, - .dma_out_irq_description = NULL, -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN - .dma_in_enabled = 1, - .dma_in_nbr = SER2_RX_DMA_NBR, - .dma_in_irq_nbr = SER2_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, - .dma_in_irq_description = "serial 2 dma rec", -#else - .dma_in_enabled = 0, - .dma_in_nbr = UINT_MAX, - .dma_in_irq_nbr = 0, - .dma_in_irq_flags = 0, - .dma_in_irq_description = NULL, -#endif -#else - .enabled = 0, - .io_if_description = NULL, - .dma_out_enabled = 0, - .dma_in_enabled = 0 -#endif - }, /* ttyS2 */ - - { .baud = DEF_BAUD, - .ioport = (unsigned char *)R_SERIAL3_CTRL, - .irq = 1U << 8, /* uses DMA 4 and 5 */ - .oclrintradr = R_DMA_CH4_CLR_INTR, - .ofirstadr = R_DMA_CH4_FIRST, - .ocmdadr = R_DMA_CH4_CMD, - .ostatusadr = R_DMA_CH4_STATUS, - .iclrintradr = R_DMA_CH5_CLR_INTR, - .ifirstadr = R_DMA_CH5_FIRST, - .icmdadr = R_DMA_CH5_CMD, - .idescradr = R_DMA_CH5_DESCR, - .flags = STD_FLAGS, - .rx_ctrl = DEF_RX, - .tx_ctrl = DEF_TX, - .iseteop = 1, - .dma_owner = dma_ser3, - .io_if = if_serial_3, -#ifdef CONFIG_ETRAX_SERIAL_PORT3 - .enabled = 1, - .io_if_description = "ser3", -#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT - .dma_out_enabled = 1, - .dma_out_nbr = SER3_TX_DMA_NBR, - .dma_out_irq_nbr = SER3_DMA_TX_IRQ_NBR, - .dma_out_irq_flags = IRQF_DISABLED, - .dma_out_irq_description = "serial 3 dma tr", -#else - .dma_out_enabled = 0, - .dma_out_nbr = UINT_MAX, - .dma_out_irq_nbr = 0, - .dma_out_irq_flags = 0, - .dma_out_irq_description = NULL, -#endif -#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN - .dma_in_enabled = 1, - .dma_in_nbr = SER3_RX_DMA_NBR, - .dma_in_irq_nbr = SER3_DMA_RX_IRQ_NBR, - .dma_in_irq_flags = IRQF_DISABLED, - .dma_in_irq_description = "serial 3 dma rec", -#else - .dma_in_enabled = 0, - .dma_in_nbr = UINT_MAX, - .dma_in_irq_nbr = 0, - .dma_in_irq_flags = 0, - .dma_in_irq_description = NULL -#endif -#else - .enabled = 0, - .io_if_description = NULL, - .dma_out_enabled = 0, - .dma_in_enabled = 0 -#endif - } /* ttyS3 */ -#endif -}; - - -#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial)) - -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER -static struct fast_timer fast_timers[NR_PORTS]; -#endif - -#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY -#define PROCSTAT(x) x -struct ser_statistics_type { - int overrun_cnt; - int early_errors_cnt; - int ser_ints_ok_cnt; - int errors_cnt; - unsigned long int processing_flip; - unsigned long processing_flip_still_room; - unsigned long int timeout_flush_cnt; - int rx_dma_ints; - int tx_dma_ints; - int rx_tot; - int tx_tot; -}; - -static struct ser_statistics_type ser_stat[NR_PORTS]; - -#else - -#define PROCSTAT(x) - -#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */ - -/* RS-485 */ -#if defined(CONFIG_ETRAX_RS485) -#ifdef CONFIG_ETRAX_FAST_TIMER -static struct fast_timer fast_timers_rs485[NR_PORTS]; -#endif -#if defined(CONFIG_ETRAX_RS485_ON_PA) -static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT; -#endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) -static int rs485_port_g_bit = CONFIG_ETRAX_RS485_ON_PORT_G_BIT; -#endif -#endif - -/* Info and macros needed for each ports extra control/status signals. */ -#define E100_STRUCT_PORT(line, pinname) \ - ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ - (R_PORT_PA_DATA): ( \ - (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ - (R_PORT_PB_DATA):&dummy_ser[line])) - -#define E100_STRUCT_SHADOW(line, pinname) \ - ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ - (&port_pa_data_shadow): ( \ - (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ - (&port_pb_data_shadow):&dummy_ser[line])) -#define E100_STRUCT_MASK(line, pinname) \ - ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ - (1<= 0)? \ - (1< 3.3V to RS-232 driver -> -12V on RS-232 level - * inactive = 1 -> 0V to RS-232 driver -> +12V on RS-232 level - * - * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip - */ - -/* Output */ -#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK) -/* Input */ -#define E100_CTS_GET(info) ((info)->ioport[REG_STATUS] & E100_CTS_MASK) - -/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */ -/* Is an output */ -#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].dtr_shadow) & e100_modem_pins[(info)->line].dtr_mask) - -/* Normally inputs */ -#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].ri_port) & e100_modem_pins[(info)->line].ri_mask) -#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].cd_port) & e100_modem_pins[(info)->line].cd_mask) - -/* Input */ -#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask) - - -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the memcpy_fromfs blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static unsigned char *tmp_buf; -static DEFINE_MUTEX(tmp_buf_mutex); - -/* Calculate the chartime depending on baudrate, numbor of bits etc. */ -static void update_char_time(struct e100_serial * info) -{ - tcflag_t cflags = info->port.tty->termios->c_cflag; - int bits; - - /* calc. number of bits / data byte */ - /* databits + startbit and 1 stopbit */ - if ((cflags & CSIZE) == CS7) - bits = 9; - else - bits = 10; - - if (cflags & CSTOPB) /* 2 stopbits ? */ - bits++; - - if (cflags & PARENB) /* parity bit ? */ - bits++; - - /* calc timeout */ - info->char_time_usec = ((bits * 1000000) / info->baud) + 1; - info->flush_time_usec = 4*info->char_time_usec; - if (info->flush_time_usec < MIN_FLUSH_TIME_USEC) - info->flush_time_usec = MIN_FLUSH_TIME_USEC; - -} - -/* - * This function maps from the Bxxxx defines in asm/termbits.h into real - * baud rates. - */ - -static int -cflag_to_baud(unsigned int cflag) -{ - static int baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400 }; - - static int ext_baud_table[] = { - 0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000, - 0, 0, 0, 0, 0, 0, 0, 0 }; - - if (cflag & CBAUDEX) - return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX]; - else - return baud_table[cflag & CBAUD]; -} - -/* and this maps to an etrax100 hardware baud constant */ - -static unsigned char -cflag_to_etrax_baud(unsigned int cflag) -{ - char retval; - - static char baud_table[] = { - -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 }; - - static char ext_baud_table[] = { - -1, 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 }; - - if (cflag & CBAUDEX) - retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX]; - else - retval = baud_table[cflag & CBAUD]; - - if (retval < 0) { - printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag); - retval = 5; /* choose default 9600 instead */ - } - - return retval | (retval << 4); /* choose same for both TX and RX */ -} - - -/* Various static support functions */ - -/* Functions to set or clear DTR/RTS on the requested line */ -/* It is complicated by the fact that RTS is a serial port register, while - * DTR might not be implemented in the HW at all, and if it is, it can be on - * any general port. - */ - - -static inline void -e100_dtr(struct e100_serial *info, int set) -{ -#ifndef CONFIG_SVINTO_SIM - unsigned char mask = e100_modem_pins[info->line].dtr_mask; - -#ifdef SERIAL_DEBUG_IO - printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask); - printk("ser%i shadow before 0x%02X get: %i\n", - info->line, *e100_modem_pins[info->line].dtr_shadow, - E100_DTR_GET(info)); -#endif - /* DTR is active low */ - { - unsigned long flags; - - local_irq_save(flags); - *e100_modem_pins[info->line].dtr_shadow &= ~mask; - *e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask); - *e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow; - local_irq_restore(flags); - } - -#ifdef SERIAL_DEBUG_IO - printk("ser%i shadow after 0x%02X get: %i\n", - info->line, *e100_modem_pins[info->line].dtr_shadow, - E100_DTR_GET(info)); -#endif -#endif -} - -/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive - * 0=0V , 1=3.3V - */ -static inline void -e100_rts(struct e100_serial *info, int set) -{ -#ifndef CONFIG_SVINTO_SIM - unsigned long flags; - local_irq_save(flags); - info->rx_ctrl &= ~E100_RTS_MASK; - info->rx_ctrl |= (set ? 0 : E100_RTS_MASK); /* RTS is active low */ - info->ioport[REG_REC_CTRL] = info->rx_ctrl; - local_irq_restore(flags); -#ifdef SERIAL_DEBUG_IO - printk("ser%i rts %i\n", info->line, set); -#endif -#endif -} - - -/* If this behaves as a modem, RI and CD is an output */ -static inline void -e100_ri_out(struct e100_serial *info, int set) -{ -#ifndef CONFIG_SVINTO_SIM - /* RI is active low */ - { - unsigned char mask = e100_modem_pins[info->line].ri_mask; - unsigned long flags; - - local_irq_save(flags); - *e100_modem_pins[info->line].ri_shadow &= ~mask; - *e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask); - *e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow; - local_irq_restore(flags); - } -#endif -} -static inline void -e100_cd_out(struct e100_serial *info, int set) -{ -#ifndef CONFIG_SVINTO_SIM - /* CD is active low */ - { - unsigned char mask = e100_modem_pins[info->line].cd_mask; - unsigned long flags; - - local_irq_save(flags); - *e100_modem_pins[info->line].cd_shadow &= ~mask; - *e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask); - *e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow; - local_irq_restore(flags); - } -#endif -} - -static inline void -e100_disable_rx(struct e100_serial *info) -{ -#ifndef CONFIG_SVINTO_SIM - /* disable the receiver */ - info->ioport[REG_REC_CTRL] = - (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); -#endif -} - -static inline void -e100_enable_rx(struct e100_serial *info) -{ -#ifndef CONFIG_SVINTO_SIM - /* enable the receiver */ - info->ioport[REG_REC_CTRL] = - (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); -#endif -} - -/* the rx DMA uses both the dma_descr and the dma_eop interrupts */ - -static inline void -e100_disable_rxdma_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("rxdma_irq(%d): 0\n",info->line); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line)); - *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3); -} - -static inline void -e100_enable_rxdma_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("rxdma_irq(%d): 1\n",info->line); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line)); - *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3); -} - -/* the tx DMA uses only dma_descr interrupt */ - -static void e100_disable_txdma_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("txdma_irq(%d): 0\n",info->line); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line)); - *R_IRQ_MASK2_CLR = info->irq; -} - -static void e100_enable_txdma_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("txdma_irq(%d): 1\n",info->line); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line)); - *R_IRQ_MASK2_SET = info->irq; -} - -static void e100_disable_txdma_channel(struct e100_serial *info) -{ - unsigned long flags; - - /* Disable output DMA channel for the serial port in question - * ( set to something other than serialX) - */ - local_irq_save(flags); - DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line)); - if (info->line == 0) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) == - IO_STATE(R_GEN_CONFIG, dma6, serial0)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused); - } - } else if (info->line == 1) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) == - IO_STATE(R_GEN_CONFIG, dma8, serial1)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb); - } - } else if (info->line == 2) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) == - IO_STATE(R_GEN_CONFIG, dma2, serial2)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0); - } - } else if (info->line == 3) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) == - IO_STATE(R_GEN_CONFIG, dma4, serial3)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1); - } - } - *R_GEN_CONFIG = genconfig_shadow; - local_irq_restore(flags); -} - - -static void e100_enable_txdma_channel(struct e100_serial *info) -{ - unsigned long flags; - - local_irq_save(flags); - DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line)); - /* Enable output DMA channel for the serial port in question */ - if (info->line == 0) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0); - } else if (info->line == 1) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1); - } else if (info->line == 2) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2); - } else if (info->line == 3) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3); - } - *R_GEN_CONFIG = genconfig_shadow; - local_irq_restore(flags); -} - -static void e100_disable_rxdma_channel(struct e100_serial *info) -{ - unsigned long flags; - - /* Disable input DMA channel for the serial port in question - * ( set to something other than serialX) - */ - local_irq_save(flags); - if (info->line == 0) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) == - IO_STATE(R_GEN_CONFIG, dma7, serial0)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused); - } - } else if (info->line == 1) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) == - IO_STATE(R_GEN_CONFIG, dma9, serial1)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb); - } - } else if (info->line == 2) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) == - IO_STATE(R_GEN_CONFIG, dma3, serial2)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0); - } - } else if (info->line == 3) { - if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) == - IO_STATE(R_GEN_CONFIG, dma5, serial3)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1); - } - } - *R_GEN_CONFIG = genconfig_shadow; - local_irq_restore(flags); -} - - -static void e100_enable_rxdma_channel(struct e100_serial *info) -{ - unsigned long flags; - - local_irq_save(flags); - /* Enable input DMA channel for the serial port in question */ - if (info->line == 0) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0); - } else if (info->line == 1) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1); - } else if (info->line == 2) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2); - } else if (info->line == 3) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5); - genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3); - } - *R_GEN_CONFIG = genconfig_shadow; - local_irq_restore(flags); -} - -#ifdef SERIAL_HANDLE_EARLY_ERRORS -/* in order to detect and fix errors on the first byte - we have to use the serial interrupts as well. */ - -static inline void -e100_disable_serial_data_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("ser_irq(%d): 0\n",info->line); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line)); - *R_IRQ_MASK1_CLR = (1U << (8+2*info->line)); -} - -static inline void -e100_enable_serial_data_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("ser_irq(%d): 1\n",info->line); - printk("**** %d = %d\n", - (8+2*info->line), - (1U << (8+2*info->line))); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line)); - *R_IRQ_MASK1_SET = (1U << (8+2*info->line)); -} -#endif - -static inline void -e100_disable_serial_tx_ready_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("ser_tx_irq(%d): 0\n",info->line); -#endif - DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line)); - *R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line)); -} - -static inline void -e100_enable_serial_tx_ready_irq(struct e100_serial *info) -{ -#ifdef SERIAL_DEBUG_INTR - printk("ser_tx_irq(%d): 1\n",info->line); - printk("**** %d = %d\n", - (8+1+2*info->line), - (1U << (8+1+2*info->line))); -#endif - DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line)); - *R_IRQ_MASK1_SET = (1U << (8+1+2*info->line)); -} - -static inline void e100_enable_rx_irq(struct e100_serial *info) -{ - if (info->uses_dma_in) - e100_enable_rxdma_irq(info); - else - e100_enable_serial_data_irq(info); -} -static inline void e100_disable_rx_irq(struct e100_serial *info) -{ - if (info->uses_dma_in) - e100_disable_rxdma_irq(info); - else - e100_disable_serial_data_irq(info); -} - -#if defined(CONFIG_ETRAX_RS485) -/* Enable RS-485 mode on selected port. This is UGLY. */ -static int -e100_enable_rs485(struct tty_struct *tty, struct serial_rs485 *r) -{ - struct e100_serial * info = (struct e100_serial *)tty->driver_data; - -#if defined(CONFIG_ETRAX_RS485_ON_PA) - *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit); -#endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - rs485_port_g_bit, 1); -#endif -#if defined(CONFIG_ETRAX_RS485_LTC1387) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 1); - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 1); -#endif - - info->rs485 = *r; - - /* Maximum delay before RTS equal to 1000 */ - if (info->rs485.delay_rts_before_send >= 1000) - info->rs485.delay_rts_before_send = 1000; - -/* printk("rts: on send = %i, after = %i, enabled = %i", - info->rs485.rts_on_send, - info->rs485.rts_after_sent, - info->rs485.enabled - ); -*/ - return 0; -} - -static int -e100_write_rs485(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct e100_serial * info = (struct e100_serial *)tty->driver_data; - int old_value = (info->rs485.flags) & SER_RS485_ENABLED; - - /* rs485 is always implicitly enabled if we're using the ioctl() - * but it doesn't have to be set in the serial_rs485 - * (to be backward compatible with old apps) - * So we store, set and restore it. - */ - info->rs485.flags |= SER_RS485_ENABLED; - /* rs_write now deals with RS485 if enabled */ - count = rs_write(tty, buf, count); - if (!old_value) - info->rs485.flags &= ~(SER_RS485_ENABLED); - return count; -} - -#ifdef CONFIG_ETRAX_FAST_TIMER -/* Timer function to toggle RTS when using FAST_TIMER */ -static void rs485_toggle_rts_timer_function(unsigned long data) -{ - struct e100_serial *info = (struct e100_serial *)data; - - fast_timers_rs485[info->line].function = NULL; - e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND)); -#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) - e100_enable_rx(info); - e100_enable_rx_irq(info); -#endif -} -#endif -#endif /* CONFIG_ETRAX_RS485 */ - -/* - * ------------------------------------------------------------ - * rs_stop() and rs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter using the XOFF registers, as necessary. - * ------------------------------------------------------------ - */ - -static void -rs_stop(struct tty_struct *tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - if (info) { - unsigned long flags; - unsigned long xoff; - - local_irq_save(flags); - DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n", - CIRC_CNT(info->xmit.head, - info->xmit.tail,SERIAL_XMIT_SIZE))); - - xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, - STOP_CHAR(info->port.tty)); - xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop); - if (tty->termios->c_iflag & IXON ) { - xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); - } - - *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; - local_irq_restore(flags); - } -} - -static void -rs_start(struct tty_struct *tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - if (info) { - unsigned long flags; - unsigned long xoff; - - local_irq_save(flags); - DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n", - CIRC_CNT(info->xmit.head, - info->xmit.tail,SERIAL_XMIT_SIZE))); - xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty)); - xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); - if (tty->termios->c_iflag & IXON ) { - xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); - } - - *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; - if (!info->uses_dma_out && - info->xmit.head != info->xmit.tail && info->xmit.buf) - e100_enable_serial_tx_ready_irq(info); - - local_irq_restore(flags); - } -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * rs_interrupt(). They were separated out for readability's sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static void rs_sched_event(struct e100_serial *info, int event) -{ - if (info->event & (1 << event)) - return; - info->event |= 1 << event; - schedule_work(&info->work); -} - -/* The output DMA channel is free - use it to send as many chars as possible - * NOTES: - * We don't pay attention to info->x_char, which means if the TTY wants to - * use XON/XOFF it will set info->x_char but we won't send any X char! - * - * To implement this, we'd just start a DMA send of 1 byte pointing at a - * buffer containing the X char, and skip updating xmit. We'd also have to - * check if the last sent char was the X char when we enter this function - * the next time, to avoid updating xmit with the sent X value. - */ - -static void -transmit_chars_dma(struct e100_serial *info) -{ - unsigned int c, sentl; - struct etrax_dma_descr *descr; - -#ifdef CONFIG_SVINTO_SIM - /* This will output too little if tail is not 0 always since - * we don't reloop to send the other part. Anyway this SHOULD be a - * no-op - transmit_chars_dma would never really be called during sim - * since rs_write does not write into the xmit buffer then. - */ - if (info->xmit.tail) - printk("Error in serial.c:transmit_chars-dma(), tail!=0\n"); - if (info->xmit.head != info->xmit.tail) { - SIMCOUT(info->xmit.buf + info->xmit.tail, - CIRC_CNT(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE)); - info->xmit.head = info->xmit.tail; /* move back head */ - info->tr_running = 0; - } - return; -#endif - /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ - *info->oclrintradr = - IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | - IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - -#ifdef SERIAL_DEBUG_INTR - if (info->line == SERIAL_DEBUG_LINE) - printk("tc\n"); -#endif - if (!info->tr_running) { - /* weirdo... we shouldn't get here! */ - printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n"); - return; - } - - descr = &info->tr_descr; - - /* first get the amount of bytes sent during the last DMA transfer, - and update xmit accordingly */ - - /* if the stop bit was not set, all data has been sent */ - if (!(descr->status & d_stop)) { - sentl = descr->sw_len; - } else - /* otherwise we find the amount of data sent here */ - sentl = descr->hw_len; - - DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl)); - - /* update stats */ - info->icount.tx += sentl; - - /* update xmit buffer */ - info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1); - - /* if there is only a few chars left in the buf, wake up the blocked - write if any */ - if (CIRC_CNT(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE) < WAKEUP_CHARS) - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - - /* find out the largest amount of consecutive bytes we want to send now */ - - c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); - - /* Don't send all in one DMA transfer - divide it so we wake up - * application before all is sent - */ - - if (c >= 4*WAKEUP_CHARS) - c = c/2; - - if (c <= 0) { - /* our job here is done, don't schedule any new DMA transfer */ - info->tr_running = 0; - -#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER) - if (info->rs485.flags & SER_RS485_ENABLED) { - /* Set a short timer to toggle RTS */ - start_one_shot_timer(&fast_timers_rs485[info->line], - rs485_toggle_rts_timer_function, - (unsigned long)info, - info->char_time_usec*2, - "RS-485"); - } -#endif /* RS485 */ - return; - } - - /* ok we can schedule a dma send of c chars starting at info->xmit.tail */ - /* set up the descriptor correctly for output */ - DFLOW(DEBUG_LOG(info->line, "TX %i\n", c)); - descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */ - descr->sw_len = c; - descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail); - descr->status = 0; - - *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */ - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); - - /* DMA is now running (hopefully) */ -} /* transmit_chars_dma */ - -static void -start_transmit(struct e100_serial *info) -{ -#if 0 - if (info->line == SERIAL_DEBUG_LINE) - printk("x\n"); -#endif - - info->tr_descr.sw_len = 0; - info->tr_descr.hw_len = 0; - info->tr_descr.status = 0; - info->tr_running = 1; - if (info->uses_dma_out) - transmit_chars_dma(info); - else - e100_enable_serial_tx_ready_irq(info); -} /* start_transmit */ - -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER -static int serial_fast_timer_started = 0; -static int serial_fast_timer_expired = 0; -static void flush_timeout_function(unsigned long data); -#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\ - unsigned long timer_flags; \ - local_irq_save(timer_flags); \ - if (fast_timers[info->line].function == NULL) { \ - serial_fast_timer_started++; \ - TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \ - TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \ - start_one_shot_timer(&fast_timers[info->line], \ - flush_timeout_function, \ - (unsigned long)info, \ - (usec), \ - string); \ - } \ - else { \ - TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \ - } \ - local_irq_restore(timer_flags); \ -} -#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec) - -#else -#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) -#define START_FLUSH_FAST_TIMER(info, string) -#endif - -static struct etrax_recv_buffer * -alloc_recv_buffer(unsigned int size) -{ - struct etrax_recv_buffer *buffer; - - if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC))) - return NULL; - - buffer->next = NULL; - buffer->length = 0; - buffer->error = TTY_NORMAL; - - return buffer; -} - -static void -append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer) -{ - unsigned long flags; - - local_irq_save(flags); - - if (!info->first_recv_buffer) - info->first_recv_buffer = buffer; - else - info->last_recv_buffer->next = buffer; - - info->last_recv_buffer = buffer; - - info->recv_cnt += buffer->length; - if (info->recv_cnt > info->max_recv_cnt) - info->max_recv_cnt = info->recv_cnt; - - local_irq_restore(flags); -} - -static int -add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag) -{ - struct etrax_recv_buffer *buffer; - if (info->uses_dma_in) { - if (!(buffer = alloc_recv_buffer(4))) - return 0; - - buffer->length = 1; - buffer->error = flag; - buffer->buffer[0] = data; - - append_recv_buffer(info, buffer); - - info->icount.rx++; - } else { - struct tty_struct *tty = info->port.tty; - tty_insert_flip_char(tty, data, flag); - info->icount.rx++; - } - - return 1; -} - -static unsigned int handle_descr_data(struct e100_serial *info, - struct etrax_dma_descr *descr, - unsigned int recvl) -{ - struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer; - - if (info->recv_cnt + recvl > 65536) { - printk(KERN_CRIT - "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __func__, recvl); - return 0; - } - - buffer->length = recvl; - - if (info->errorcode == ERRCODE_SET_BREAK) - buffer->error = TTY_BREAK; - info->errorcode = 0; - - append_recv_buffer(info, buffer); - - if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) - panic("%s: Failed to allocate memory for receive buffer!\n", __func__); - - descr->buf = virt_to_phys(buffer->buffer); - - return recvl; -} - -static unsigned int handle_all_descr_data(struct e100_serial *info) -{ - struct etrax_dma_descr *descr; - unsigned int recvl; - unsigned int ret = 0; - - while (1) - { - descr = &info->rec_descr[info->cur_rec_descr]; - - if (descr == phys_to_virt(*info->idescradr)) - break; - - if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS) - info->cur_rec_descr = 0; - - /* find out how many bytes were read */ - - /* if the eop bit was not set, all data has been received */ - if (!(descr->status & d_eop)) { - recvl = descr->sw_len; - } else { - /* otherwise we find the amount of data received here */ - recvl = descr->hw_len; - } - - /* Reset the status information */ - descr->status = 0; - - DFLOW( DEBUG_LOG(info->line, "RX %lu\n", recvl); - if (info->port.tty->stopped) { - unsigned char *buf = phys_to_virt(descr->buf); - DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]); - DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]); - DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]); - } - ); - - /* update stats */ - info->icount.rx += recvl; - - ret += handle_descr_data(info, descr, recvl); - } - - return ret; -} - -static void receive_chars_dma(struct e100_serial *info) -{ - struct tty_struct *tty; - unsigned char rstat; - -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - return; -#endif - - /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ - *info->iclrintradr = - IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | - IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - - tty = info->port.tty; - if (!tty) /* Something wrong... */ - return; - -#ifdef SERIAL_HANDLE_EARLY_ERRORS - if (info->uses_dma_in) - e100_enable_serial_data_irq(info); -#endif - - if (info->errorcode == ERRCODE_INSERT_BREAK) - add_char_and_flag(info, '\0', TTY_BREAK); - - handle_all_descr_data(info); - - /* Read the status register to detect errors */ - rstat = info->ioport[REG_STATUS]; - if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) { - DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat)); - } - - if (rstat & SER_ERROR_MASK) { - /* If we got an error, we must reset it by reading the - * data_in field - */ - unsigned char data = info->ioport[REG_DATA]; - - PROCSTAT(ser_stat[info->line].errors_cnt++); - DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n", - ((rstat & SER_ERROR_MASK) << 8) | data); - - if (rstat & SER_PAR_ERR_MASK) - add_char_and_flag(info, data, TTY_PARITY); - else if (rstat & SER_OVERRUN_MASK) - add_char_and_flag(info, data, TTY_OVERRUN); - else if (rstat & SER_FRAMING_ERR_MASK) - add_char_and_flag(info, data, TTY_FRAME); - } - - START_FLUSH_FAST_TIMER(info, "receive_chars"); - - /* Restart the receiving DMA */ - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); -} - -static int start_recv_dma(struct e100_serial *info) -{ - struct etrax_dma_descr *descr = info->rec_descr; - struct etrax_recv_buffer *buffer; - int i; - - /* Set up the receiving descriptors */ - for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { - if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) - panic("%s: Failed to allocate memory for receive buffer!\n", __func__); - - descr[i].ctrl = d_int; - descr[i].buf = virt_to_phys(buffer->buffer); - descr[i].sw_len = SERIAL_DESCR_BUF_SIZE; - descr[i].hw_len = 0; - descr[i].status = 0; - descr[i].next = virt_to_phys(&descr[i+1]); - } - - /* Link the last descriptor to the first */ - descr[i-1].next = virt_to_phys(&descr[0]); - - /* Start with the first descriptor in the list */ - info->cur_rec_descr = 0; - - /* Start the DMA */ - *info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]); - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); - - /* Input DMA should be running now */ - return 1; -} - -static void -start_receive(struct e100_serial *info) -{ -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - return; -#endif - if (info->uses_dma_in) { - /* reset the input dma channel to be sure it works */ - - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - - start_recv_dma(info); - } -} - - -/* the bits in the MASK2 register are laid out like this: - DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR - where I is the input channel and O is the output channel for the port. - info->irq is the bit number for the DMAO_DESCR so to check the others we - shift info->irq to the left. -*/ - -/* dma output channel interrupt handler - this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or - DMA8(ser1) when they have finished a descriptor with the intr flag set. -*/ - -static irqreturn_t -tr_interrupt(int irq, void *dev_id) -{ - struct e100_serial *info; - unsigned long ireg; - int i; - int handled = 0; - -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - { - const char *s = "What? tr_interrupt in simulator??\n"; - SIMCOUT(s,strlen(s)); - } - return IRQ_HANDLED; -#endif - - /* find out the line that caused this irq and get it from rs_table */ - - ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ - - for (i = 0; i < NR_PORTS; i++) { - info = rs_table + i; - if (!info->enabled || !info->uses_dma_out) - continue; - /* check for dma_descr (don't need to check for dma_eop in output dma for serial */ - if (ireg & info->irq) { - handled = 1; - /* we can send a new dma bunch. make it so. */ - DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i)); - /* Read jiffies_usec first, - * we want this time to be as late as possible - */ - PROCSTAT(ser_stat[info->line].tx_dma_ints++); - info->last_tx_active_usec = GET_JIFFIES_USEC(); - info->last_tx_active = jiffies; - transmit_chars_dma(info); - } - - /* FIXME: here we should really check for a change in the - status lines and if so call status_handle(info) */ - } - return IRQ_RETVAL(handled); -} /* tr_interrupt */ - -/* dma input channel interrupt handler */ - -static irqreturn_t -rec_interrupt(int irq, void *dev_id) -{ - struct e100_serial *info; - unsigned long ireg; - int i; - int handled = 0; - -#ifdef CONFIG_SVINTO_SIM - /* No receive in the simulator. Will probably be when the rest of - * the serial interface works, and this piece will just be removed. - */ - { - const char *s = "What? rec_interrupt in simulator??\n"; - SIMCOUT(s,strlen(s)); - } - return IRQ_HANDLED; -#endif - - /* find out the line that caused this irq and get it from rs_table */ - - ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ - - for (i = 0; i < NR_PORTS; i++) { - info = rs_table + i; - if (!info->enabled || !info->uses_dma_in) - continue; - /* check for both dma_eop and dma_descr for the input dma channel */ - if (ireg & ((info->irq << 2) | (info->irq << 3))) { - handled = 1; - /* we have received something */ - receive_chars_dma(info); - } - - /* FIXME: here we should really check for a change in the - status lines and if so call status_handle(info) */ - } - return IRQ_RETVAL(handled); -} /* rec_interrupt */ - -static int force_eop_if_needed(struct e100_serial *info) -{ - /* We check data_avail bit to determine if data has - * arrived since last time - */ - unsigned char rstat = info->ioport[REG_STATUS]; - - /* error or datavail? */ - if (rstat & SER_ERROR_MASK) { - /* Some error has occurred. If there has been valid data, an - * EOP interrupt will be made automatically. If no data, the - * normal ser_interrupt should be enabled and handle it. - * So do nothing! - */ - DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n", - rstat | (info->line << 8)); - return 0; - } - - if (rstat & SER_DATA_AVAIL_MASK) { - /* Ok data, no error, count it */ - TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n", - rstat | (info->line << 8))); - /* Read data to clear status flags */ - (void)info->ioport[REG_DATA]; - - info->forced_eop = 0; - START_FLUSH_FAST_TIMER(info, "magic"); - return 0; - } - - /* hit the timeout, force an EOP for the input - * dma channel if we haven't already - */ - if (!info->forced_eop) { - info->forced_eop = 1; - PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); - TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line)); - FORCE_EOP(info); - } - - return 1; -} - -static void flush_to_flip_buffer(struct e100_serial *info) -{ - struct tty_struct *tty; - struct etrax_recv_buffer *buffer; - unsigned long flags; - - local_irq_save(flags); - tty = info->port.tty; - - if (!tty) { - local_irq_restore(flags); - return; - } - - while ((buffer = info->first_recv_buffer) != NULL) { - unsigned int count = buffer->length; - - tty_insert_flip_string(tty, buffer->buffer, count); - info->recv_cnt -= count; - - if (count == buffer->length) { - info->first_recv_buffer = buffer->next; - kfree(buffer); - } else { - buffer->length -= count; - memmove(buffer->buffer, buffer->buffer + count, buffer->length); - buffer->error = TTY_NORMAL; - } - } - - if (!info->first_recv_buffer) - info->last_recv_buffer = NULL; - - local_irq_restore(flags); - - /* This includes a check for low-latency */ - tty_flip_buffer_push(tty); -} - -static void check_flush_timeout(struct e100_serial *info) -{ - /* Flip what we've got (if we can) */ - flush_to_flip_buffer(info); - - /* We might need to flip later, but not to fast - * since the system is busy processing input... */ - if (info->first_recv_buffer) - START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000); - - /* Force eop last, since data might have come while we're processing - * and if we started the slow timer above, we won't start a fast - * below. - */ - force_eop_if_needed(info); -} - -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER -static void flush_timeout_function(unsigned long data) -{ - struct e100_serial *info = (struct e100_serial *)data; - - fast_timers[info->line].function = NULL; - serial_fast_timer_expired++; - TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line)); - TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired)); - check_flush_timeout(info); -} - -#else - -/* dma fifo/buffer timeout handler - forces an end-of-packet for the dma input channel if no chars - have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s. -*/ - -static struct timer_list flush_timer; - -static void -timed_flush_handler(unsigned long ptr) -{ - struct e100_serial *info; - int i; - -#ifdef CONFIG_SVINTO_SIM - return; -#endif - - for (i = 0; i < NR_PORTS; i++) { - info = rs_table + i; - if (info->uses_dma_in) - check_flush_timeout(info); - } - - /* restart flush timer */ - mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS); -} -#endif - -#ifdef SERIAL_HANDLE_EARLY_ERRORS - -/* If there is an error (ie break) when the DMA is running and - * there are no bytes in the fifo the DMA is stopped and we get no - * eop interrupt. Thus we have to monitor the first bytes on a DMA - * transfer, and if it is without error we can turn the serial - * interrupts off. - */ - -/* -BREAK handling on ETRAX 100: -ETRAX will generate interrupt although there is no stop bit between the -characters. - -Depending on how long the break sequence is, the end of the breaksequence -will look differently: -| indicates start/end of a character. - -B= Break character (0x00) with framing error. -E= Error byte with parity error received after B characters. -F= "Faked" valid byte received immediately after B characters. -V= Valid byte - -1. - B BL ___________________________ V -.._|__________|__________| |valid data | - -Multiple frame errors with data == 0x00 (B), -the timing matches up "perfectly" so no extra ending char is detected. -The RXD pin is 1 in the last interrupt, in that case -we set info->errorcode = ERRCODE_INSERT_BREAK, but we can't really -know if another byte will come and this really is case 2. below -(e.g F=0xFF or 0xFE) -If RXD pin is 0 we can expect another character (see 2. below). - - -2. - - B B E or F__________________..__ V -.._|__________|__________|______ | |valid data - "valid" or - parity error - -Multiple frame errors with data == 0x00 (B), -but the part of the break trigs is interpreted as a start bit (and possibly -some 0 bits followed by a number of 1 bits and a stop bit). -Depending on parity settings etc. this last character can be either -a fake "valid" char (F) or have a parity error (E). - -If the character is valid it will be put in the buffer, -we set info->errorcode = ERRCODE_SET_BREAK so the receive interrupt -will set the flags so the tty will handle it, -if it's an error byte it will not be put in the buffer -and we set info->errorcode = ERRCODE_INSERT_BREAK. - -To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp -of the last faulty char (B) and compares it with the current time: -If the time elapsed time is less then 2*char_time_usec we will assume -it's a faked F char and not a Valid char and set -info->errorcode = ERRCODE_SET_BREAK. - -Flaws in the above solution: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We use the timer to distinguish a F character from a V character, -if a V character is to close after the break we might make the wrong decision. - -TODO: The break will be delayed until an F or V character is received. - -*/ - -static -struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info) -{ - unsigned long data_read; - struct tty_struct *tty = info->port.tty; - - if (!tty) { - printk("!NO TTY!\n"); - return info; - } - - /* Read data and status at the same time */ - data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]); -more_data: - if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) { - DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0)); - } - DINTR2(DEBUG_LOG(info->line, "ser_rx %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read))); - - if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) | - IO_MASK(R_SERIAL0_READ, par_err) | - IO_MASK(R_SERIAL0_READ, overrun) )) { - /* An error */ - info->last_rx_active_usec = GET_JIFFIES_USEC(); - info->last_rx_active = jiffies; - DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read)); - DLOG_INT_TRIG( - if (!log_int_trig1_pos) { - log_int_trig1_pos = log_int_pos; - log_int(rdpc(), 0, 0); - } - ); - - - if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) && - (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) { - /* Most likely a break, but we get interrupts over and - * over again. - */ - - if (!info->break_detected_cnt) { - DEBUG_LOG(info->line, "#BRK start\n", 0); - } - if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) { - /* The RX pin is high now, so the break - * must be over, but.... - * we can't really know if we will get another - * last byte ending the break or not. - * And we don't know if the byte (if any) will - * have an error or look valid. - */ - DEBUG_LOG(info->line, "# BL BRK\n", 0); - info->errorcode = ERRCODE_INSERT_BREAK; - } - info->break_detected_cnt++; - } else { - /* The error does not look like a break, but could be - * the end of one - */ - if (info->break_detected_cnt) { - DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); - info->errorcode = ERRCODE_INSERT_BREAK; - } else { - unsigned char data = IO_EXTRACT(R_SERIAL0_READ, - data_in, data_read); - char flag = TTY_NORMAL; - if (info->errorcode == ERRCODE_INSERT_BREAK) { - struct tty_struct *tty = info->port.tty; - tty_insert_flip_char(tty, 0, flag); - info->icount.rx++; - } - - if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) { - info->icount.parity++; - flag = TTY_PARITY; - } else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) { - info->icount.overrun++; - flag = TTY_OVERRUN; - } else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) { - info->icount.frame++; - flag = TTY_FRAME; - } - tty_insert_flip_char(tty, data, flag); - info->errorcode = 0; - } - info->break_detected_cnt = 0; - } - } else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) { - /* No error */ - DLOG_INT_TRIG( - if (!log_int_trig1_pos) { - if (log_int_pos >= log_int_size) { - log_int_pos = 0; - } - log_int_trig0_pos = log_int_pos; - log_int(rdpc(), 0, 0); - } - ); - tty_insert_flip_char(tty, - IO_EXTRACT(R_SERIAL0_READ, data_in, data_read), - TTY_NORMAL); - } else { - DEBUG_LOG(info->line, "ser_rx int but no data_avail %08lX\n", data_read); - } - - - info->icount.rx++; - data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]); - if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) { - DEBUG_LOG(info->line, "ser_rx %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)); - goto more_data; - } - - tty_flip_buffer_push(info->port.tty); - return info; -} - -static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) -{ - unsigned char rstat; - -#ifdef SERIAL_DEBUG_INTR - printk("Interrupt from serport %d\n", i); -#endif -/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ - if (!info->uses_dma_in) { - return handle_ser_rx_interrupt_no_dma(info); - } - /* DMA is used */ - rstat = info->ioport[REG_STATUS]; - if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) { - DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0)); - } - - if (rstat & SER_ERROR_MASK) { - unsigned char data; - - info->last_rx_active_usec = GET_JIFFIES_USEC(); - info->last_rx_active = jiffies; - /* If we got an error, we must reset it by reading the - * data_in field - */ - data = info->ioport[REG_DATA]; - DINTR1(DEBUG_LOG(info->line, "ser_rx! %c\n", data)); - DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat)); - if (!data && (rstat & SER_FRAMING_ERR_MASK)) { - /* Most likely a break, but we get interrupts over and - * over again. - */ - - if (!info->break_detected_cnt) { - DEBUG_LOG(info->line, "#BRK start\n", 0); - } - if (rstat & SER_RXD_MASK) { - /* The RX pin is high now, so the break - * must be over, but.... - * we can't really know if we will get another - * last byte ending the break or not. - * And we don't know if the byte (if any) will - * have an error or look valid. - */ - DEBUG_LOG(info->line, "# BL BRK\n", 0); - info->errorcode = ERRCODE_INSERT_BREAK; - } - info->break_detected_cnt++; - } else { - /* The error does not look like a break, but could be - * the end of one - */ - if (info->break_detected_cnt) { - DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); - info->errorcode = ERRCODE_INSERT_BREAK; - } else { - if (info->errorcode == ERRCODE_INSERT_BREAK) { - info->icount.brk++; - add_char_and_flag(info, '\0', TTY_BREAK); - } - - if (rstat & SER_PAR_ERR_MASK) { - info->icount.parity++; - add_char_and_flag(info, data, TTY_PARITY); - } else if (rstat & SER_OVERRUN_MASK) { - info->icount.overrun++; - add_char_and_flag(info, data, TTY_OVERRUN); - } else if (rstat & SER_FRAMING_ERR_MASK) { - info->icount.frame++; - add_char_and_flag(info, data, TTY_FRAME); - } - - info->errorcode = 0; - } - info->break_detected_cnt = 0; - DEBUG_LOG(info->line, "#iERR s d %04X\n", - ((rstat & SER_ERROR_MASK) << 8) | data); - } - PROCSTAT(ser_stat[info->line].early_errors_cnt++); - } else { /* It was a valid byte, now let the DMA do the rest */ - unsigned long curr_time_u = GET_JIFFIES_USEC(); - unsigned long curr_time = jiffies; - - if (info->break_detected_cnt) { - /* Detect if this character is a new valid char or the - * last char in a break sequence: If LSBits are 0 and - * MSBits are high AND the time is close to the - * previous interrupt we should discard it. - */ - long elapsed_usec = - (curr_time - info->last_rx_active) * (1000000/HZ) + - curr_time_u - info->last_rx_active_usec; - if (elapsed_usec < 2*info->char_time_usec) { - DEBUG_LOG(info->line, "FBRK %i\n", info->line); - /* Report as BREAK (error) and let - * receive_chars_dma() handle it - */ - info->errorcode = ERRCODE_SET_BREAK; - } else { - DEBUG_LOG(info->line, "Not end of BRK (V)%i\n", info->line); - } - DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt); - } - -#ifdef SERIAL_DEBUG_INTR - printk("** OK, disabling ser_interrupts\n"); -#endif - e100_disable_serial_data_irq(info); - DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line)); - info->break_detected_cnt = 0; - - PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); - } - /* Restarting the DMA never hurts */ - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); - START_FLUSH_FAST_TIMER(info, "ser_int"); - return info; -} /* handle_ser_rx_interrupt */ - -static void handle_ser_tx_interrupt(struct e100_serial *info) -{ - unsigned long flags; - - if (info->x_char) { - unsigned char rstat; - DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char)); - local_irq_save(flags); - rstat = info->ioport[REG_STATUS]; - DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat)); - - info->ioport[REG_TR_DATA] = info->x_char; - info->icount.tx++; - info->x_char = 0; - /* We must enable since it is disabled in ser_interrupt */ - e100_enable_serial_tx_ready_irq(info); - local_irq_restore(flags); - return; - } - if (info->uses_dma_out) { - unsigned char rstat; - int i; - /* We only use normal tx interrupt when sending x_char */ - DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0)); - local_irq_save(flags); - rstat = info->ioport[REG_STATUS]; - DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat)); - e100_disable_serial_tx_ready_irq(info); - if (info->port.tty->stopped) - rs_stop(info->port.tty); - /* Enable the DMA channel and tell it to continue */ - e100_enable_txdma_channel(info); - /* Wait 12 cycles before doing the DMA command */ - for(i = 6; i > 0; i--) - nop(); - - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue); - local_irq_restore(flags); - return; - } - /* Normal char-by-char interrupt */ - if (info->xmit.head == info->xmit.tail - || info->port.tty->stopped - || info->port.tty->hw_stopped) { - DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", - info->port.tty->stopped)); - e100_disable_serial_tx_ready_irq(info); - info->tr_running = 0; - return; - } - DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail])); - /* Send a byte, rs485 timing is critical so turn of ints */ - local_irq_save(flags); - info->ioport[REG_TR_DATA] = info->xmit.buf[info->xmit.tail]; - info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); - info->icount.tx++; - if (info->xmit.head == info->xmit.tail) { -#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER) - if (info->rs485.flags & SER_RS485_ENABLED) { - /* Set a short timer to toggle RTS */ - start_one_shot_timer(&fast_timers_rs485[info->line], - rs485_toggle_rts_timer_function, - (unsigned long)info, - info->char_time_usec*2, - "RS-485"); - } -#endif /* RS485 */ - info->last_tx_active_usec = GET_JIFFIES_USEC(); - info->last_tx_active = jiffies; - e100_disable_serial_tx_ready_irq(info); - info->tr_running = 0; - DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0)); - } else { - /* We must enable since it is disabled in ser_interrupt */ - e100_enable_serial_tx_ready_irq(info); - } - local_irq_restore(flags); - - if (CIRC_CNT(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE) < WAKEUP_CHARS) - rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); - -} /* handle_ser_tx_interrupt */ - -/* result of time measurements: - * RX duration 54-60 us when doing something, otherwise 6-9 us - * ser_int duration: just sending: 8-15 us normally, up to 73 us - */ -static irqreturn_t -ser_interrupt(int irq, void *dev_id) -{ - static volatile int tx_started = 0; - struct e100_serial *info; - int i; - unsigned long flags; - unsigned long irq_mask1_rd; - unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */ - int handled = 0; - static volatile unsigned long reentered_ready_mask = 0; - - local_irq_save(flags); - irq_mask1_rd = *R_IRQ_MASK1_RD; - /* First handle all rx interrupts with ints disabled */ - info = rs_table; - irq_mask1_rd &= e100_ser_int_mask; - for (i = 0; i < NR_PORTS; i++) { - /* Which line caused the data irq? */ - if (irq_mask1_rd & data_mask) { - handled = 1; - handle_ser_rx_interrupt(info); - } - info += 1; - data_mask <<= 2; - } - /* Handle tx interrupts with interrupts enabled so we - * can take care of new data interrupts while transmitting - * We protect the tx part with the tx_started flag. - * We disable the tr_ready interrupts we are about to handle and - * unblock the serial interrupt so new serial interrupts may come. - * - * If we get a new interrupt: - * - it migth be due to synchronous serial ports. - * - serial irq will be blocked by general irq handler. - * - async data will be handled above (sync will be ignored). - * - tx_started flag will prevent us from trying to send again and - * we will exit fast - no need to unblock serial irq. - * - Next (sync) serial interrupt handler will be runned with - * disabled interrupt due to restore_flags() at end of function, - * so sync handler will not be preempted or reentered. - */ - if (!tx_started) { - unsigned long ready_mask; - unsigned long - tx_started = 1; - /* Only the tr_ready interrupts left */ - irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) | - IO_MASK(R_IRQ_MASK1_RD, ser1_ready) | - IO_MASK(R_IRQ_MASK1_RD, ser2_ready) | - IO_MASK(R_IRQ_MASK1_RD, ser3_ready)); - while (irq_mask1_rd) { - /* Disable those we are about to handle */ - *R_IRQ_MASK1_CLR = irq_mask1_rd; - /* Unblock the serial interrupt */ - *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); - - local_irq_enable(); - ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */ - info = rs_table; - for (i = 0; i < NR_PORTS; i++) { - /* Which line caused the ready irq? */ - if (irq_mask1_rd & ready_mask) { - handled = 1; - handle_ser_tx_interrupt(info); - } - info += 1; - ready_mask <<= 2; - } - /* handle_ser_tx_interrupt enables tr_ready interrupts */ - local_irq_disable(); - /* Handle reentered TX interrupt */ - irq_mask1_rd = reentered_ready_mask; - } - local_irq_disable(); - tx_started = 0; - } else { - unsigned long ready_mask; - ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) | - IO_MASK(R_IRQ_MASK1_RD, ser1_ready) | - IO_MASK(R_IRQ_MASK1_RD, ser2_ready) | - IO_MASK(R_IRQ_MASK1_RD, ser3_ready)); - if (ready_mask) { - reentered_ready_mask |= ready_mask; - /* Disable those we are about to handle */ - *R_IRQ_MASK1_CLR = ready_mask; - DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask)); - } - } - - local_irq_restore(flags); - return IRQ_RETVAL(handled); -} /* ser_interrupt */ -#endif - -/* - * ------------------------------------------------------------------- - * Here ends the serial interrupt routines. - * ------------------------------------------------------------------- - */ - -/* - * This routine is used to handle the "bottom half" processing for the - * serial driver, known also the "software interrupt" processing. - * This processing is done at the kernel interrupt level, after the - * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This - * is where time-consuming activities which can not be done in the - * interrupt driver proper are done; the interrupt driver schedules - * them using rs_sched_event(), and they get done here. - */ -static void -do_softint(struct work_struct *work) -{ - struct e100_serial *info; - struct tty_struct *tty; - - info = container_of(work, struct e100_serial, work); - - tty = info->port.tty; - if (!tty) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) - tty_wakeup(tty); -} - -static int -startup(struct e100_serial * info) -{ - unsigned long flags; - unsigned long xmit_page; - int i; - - xmit_page = get_zeroed_page(GFP_KERNEL); - if (!xmit_page) - return -ENOMEM; - - local_irq_save(flags); - - /* if it was already initialized, skip this */ - - if (info->flags & ASYNC_INITIALIZED) { - local_irq_restore(flags); - free_page(xmit_page); - return 0; - } - - if (info->xmit.buf) - free_page(xmit_page); - else - info->xmit.buf = (unsigned char *) xmit_page; - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf); -#endif - -#ifdef CONFIG_SVINTO_SIM - /* Bits and pieces collected from below. Better to have them - in one ifdef:ed clause than to mix in a lot of ifdefs, - right? */ - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->xmit.head = info->xmit.tail = 0; - info->first_recv_buffer = info->last_recv_buffer = NULL; - info->recv_cnt = info->max_recv_cnt = 0; - - for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) - info->rec_descr[i].buf = NULL; - - /* No real action in the simulator, but may set info important - to ioctl. */ - change_speed(info); -#else - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in change_speed()) - */ - - /* - * Reset the DMA channels and make sure their interrupts are cleared - */ - - if (info->dma_in_enabled) { - info->uses_dma_in = 1; - e100_enable_rxdma_channel(info); - - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - - /* Wait until reset cycle is complete */ - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - - /* Make sure the irqs are cleared */ - *info->iclrintradr = - IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | - IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - } else { - e100_disable_rxdma_channel(info); - } - - if (info->dma_out_enabled) { - info->uses_dma_out = 1; - e100_enable_txdma_channel(info); - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) == - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); - - /* Make sure the irqs are cleared */ - *info->oclrintradr = - IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | - IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); - } else { - e100_disable_txdma_channel(info); - } - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->xmit.head = info->xmit.tail = 0; - info->first_recv_buffer = info->last_recv_buffer = NULL; - info->recv_cnt = info->max_recv_cnt = 0; - - for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) - info->rec_descr[i].buf = 0; - - /* - * and set the speed and other flags of the serial port - * this will start the rx/tx as well - */ -#ifdef SERIAL_HANDLE_EARLY_ERRORS - e100_enable_serial_data_irq(info); -#endif - change_speed(info); - - /* dummy read to reset any serial errors */ - - (void)info->ioport[REG_DATA]; - - /* enable the interrupts */ - if (info->uses_dma_out) - e100_enable_txdma_irq(info); - - e100_enable_rx_irq(info); - - info->tr_running = 0; /* to be sure we don't lock up the transmitter */ - - /* setup the dma input descriptor and start dma */ - - start_receive(info); - - /* for safety, make sure the descriptors last result is 0 bytes written */ - - info->tr_descr.sw_len = 0; - info->tr_descr.hw_len = 0; - info->tr_descr.status = 0; - - /* enable RTS/DTR last */ - - e100_rts(info, 1); - e100_dtr(info, 1); - -#endif /* CONFIG_SVINTO_SIM */ - - info->flags |= ASYNC_INITIALIZED; - - local_irq_restore(flags); - return 0; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void -shutdown(struct e100_serial * info) -{ - unsigned long flags; - struct etrax_dma_descr *descr = info->rec_descr; - struct etrax_recv_buffer *buffer; - int i; - -#ifndef CONFIG_SVINTO_SIM - /* shut down the transmitter and receiver */ - DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line)); - e100_disable_rx(info); - info->ioport[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40); - - /* disable interrupts, reset dma channels */ - if (info->uses_dma_in) { - e100_disable_rxdma_irq(info); - *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - info->uses_dma_in = 0; - } else { - e100_disable_serial_data_irq(info); - } - - if (info->uses_dma_out) { - e100_disable_txdma_irq(info); - info->tr_running = 0; - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); - info->uses_dma_out = 0; - } else { - e100_disable_serial_tx_ready_irq(info); - info->tr_running = 0; - } - -#endif /* CONFIG_SVINTO_SIM */ - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....\n", info->line, - info->irq); -#endif - - local_irq_save(flags); - - if (info->xmit.buf) { - free_page((unsigned long)info->xmit.buf); - info->xmit.buf = NULL; - } - - for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) - if (descr[i].buf) { - buffer = phys_to_virt(descr[i].buf) - sizeof *buffer; - kfree(buffer); - descr[i].buf = 0; - } - - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { - /* hang up DTR and RTS if HUPCL is enabled */ - e100_dtr(info, 0); - e100_rts(info, 0); /* could check CRTSCTS before doing this */ - } - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); -} - - -/* change baud rate and other assorted parameters */ - -static void -change_speed(struct e100_serial *info) -{ - unsigned int cflag; - unsigned long xoff; - unsigned long flags; - /* first some safety checks */ - - if (!info->port.tty || !info->port.tty->termios) - return; - if (!info->ioport) - return; - - cflag = info->port.tty->termios->c_cflag; - - /* possibly, the tx/rx should be disabled first to do this safely */ - - /* change baud-rate and write it to the hardware */ - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { - /* Special baudrate */ - u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ - unsigned long alt_source = - IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) | - IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); - /* R_ALT_SER_BAUDRATE selects the source */ - DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n", - (unsigned long)info->baud_base, info->custom_divisor)); - if (info->baud_base == SERIAL_PRESCALE_BASE) { - /* 0, 2-65535 (0=65536) */ - u16 divisor = info->custom_divisor; - /* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */ - /* baudrate is 3.125MHz/custom_divisor */ - alt_source = - IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) | - IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale); - alt_source = 0x11; - DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor)); - *R_SERIAL_PRESCALE = divisor; - info->baud = SERIAL_PRESCALE_BASE/divisor; - } -#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED - else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 && - info->custom_divisor == 1) || - (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ && - info->custom_divisor == 8)) { - /* ext_clk selected */ - alt_source = - IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) | - IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern); - DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8)); - info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8; - } -#endif - else - { - /* Bad baudbase, we don't support using timer0 - * for baudrate. - */ - printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n", - (unsigned long)info->baud_base, info->custom_divisor); - } - r_alt_ser_baudrate_shadow &= ~mask; - r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); - *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; - } else { - /* Normal baudrate */ - /* Make sure we use normal baudrate */ - u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ - unsigned long alt_source = - IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) | - IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); - r_alt_ser_baudrate_shadow &= ~mask; - r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); -#ifndef CONFIG_SVINTO_SIM - *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; -#endif /* CONFIG_SVINTO_SIM */ - - info->baud = cflag_to_baud(cflag); -#ifndef CONFIG_SVINTO_SIM - info->ioport[REG_BAUD] = cflag_to_etrax_baud(cflag); -#endif /* CONFIG_SVINTO_SIM */ - } - -#ifndef CONFIG_SVINTO_SIM - /* start with default settings and then fill in changes */ - local_irq_save(flags); - /* 8 bit, no/even parity */ - info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) | - IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) | - IO_MASK(R_SERIAL0_REC_CTRL, rec_par)); - - /* 8 bit, no/even parity, 1 stop bit, no cts */ - info->tx_ctrl &= ~(IO_MASK(R_SERIAL0_TR_CTRL, tr_bitnr) | - IO_MASK(R_SERIAL0_TR_CTRL, tr_par_en) | - IO_MASK(R_SERIAL0_TR_CTRL, tr_par) | - IO_MASK(R_SERIAL0_TR_CTRL, stop_bits) | - IO_MASK(R_SERIAL0_TR_CTRL, auto_cts)); - - if ((cflag & CSIZE) == CS7) { - /* set 7 bit mode */ - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit); - info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit); - } - - if (cflag & CSTOPB) { - /* set 2 stop bit mode */ - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, two_bits); - } - - if (cflag & PARENB) { - /* enable parity */ - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable); - info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable); - } - - if (cflag & CMSPAR) { - /* enable stick parity, PARODD mean Mark which matches ETRAX */ - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick); - info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick); - } - if (cflag & PARODD) { - /* set odd parity (or Mark if CMSPAR) */ - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd); - info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd); - } - - if (cflag & CRTSCTS) { - /* enable automatic CTS handling */ - DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0)); - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active); - } - - /* make sure the tx and rx are enabled */ - - info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable); - info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); - - /* actually write the control regs to the hardware */ - - info->ioport[REG_TR_CTRL] = info->tx_ctrl; - info->ioport[REG_REC_CTRL] = info->rx_ctrl; - xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty)); - xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); - if (info->port.tty->termios->c_iflag & IXON ) { - DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", - STOP_CHAR(info->port.tty))); - xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); - } - - *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; - local_irq_restore(flags); -#endif /* !CONFIG_SVINTO_SIM */ - - update_char_time(info); - -} /* change_speed */ - -/* start transmitting chars NOW */ - -static void -rs_flush_chars(struct tty_struct *tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - - if (info->tr_running || - info->xmit.head == info->xmit.tail || - tty->stopped || - tty->hw_stopped || - !info->xmit.buf) - return; - -#ifdef SERIAL_DEBUG_FLOW - printk("rs_flush_chars\n"); -#endif - - /* this protection might not exactly be necessary here */ - - local_irq_save(flags); - start_transmit(info); - local_irq_restore(flags); -} - -static int rs_raw_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - int c, ret = 0; - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - - /* first some sanity checks */ - - if (!tty || !info->xmit.buf || !tmp_buf) - return 0; - -#ifdef SERIAL_DEBUG_DATA - if (info->line == SERIAL_DEBUG_LINE) - printk("rs_raw_write (%d), status %d\n", - count, info->ioport[REG_STATUS]); -#endif - -#ifdef CONFIG_SVINTO_SIM - /* Really simple. The output is here and now. */ - SIMCOUT(buf, count); - return count; -#endif - local_save_flags(flags); - DFLOW(DEBUG_LOG(info->line, "write count %i ", count)); - DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty))); - - - /* The local_irq_disable/restore_flags pairs below are needed - * because the DMA interrupt handler moves the info->xmit values. - * the memcpy needs to be in the critical region unfortunately, - * because we need to read xmit values, memcpy, write xmit values - * in one atomic operation... this could perhaps be avoided by - * more clever design. - */ - local_irq_disable(); - while (count) { - c = CIRC_SPACE_TO_END(info->xmit.head, - info->xmit.tail, - SERIAL_XMIT_SIZE); - - if (count < c) - c = count; - if (c <= 0) - break; - - memcpy(info->xmit.buf + info->xmit.head, buf, c); - info->xmit.head = (info->xmit.head + c) & - (SERIAL_XMIT_SIZE-1); - buf += c; - count -= c; - ret += c; - } - local_irq_restore(flags); - - /* enable transmitter if not running, unless the tty is stopped - * this does not need IRQ protection since if tr_running == 0 - * the IRQ's are not running anyway for this port. - */ - DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret)); - - if (info->xmit.head != info->xmit.tail && - !tty->stopped && - !tty->hw_stopped && - !info->tr_running) { - start_transmit(info); - } - - return ret; -} /* raw_raw_write() */ - -static int -rs_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ -#if defined(CONFIG_ETRAX_RS485) - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - - if (info->rs485.flags & SER_RS485_ENABLED) - { - /* If we are in RS-485 mode, we need to toggle RTS and disable - * the receiver before initiating a DMA transfer - */ -#ifdef CONFIG_ETRAX_FAST_TIMER - /* Abort any started timer */ - fast_timers_rs485[info->line].function = NULL; - del_fast_timer(&fast_timers_rs485[info->line]); -#endif - e100_rts(info, (info->rs485.flags & SER_RS485_RTS_ON_SEND)); -#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) - e100_disable_rx(info); - e100_enable_rx_irq(info); -#endif - if ((info->rs485.flags & SER_RS485_RTS_BEFORE_SEND) && - (info->rs485.delay_rts_before_send > 0)) - msleep(info->rs485.delay_rts_before_send); - } -#endif /* CONFIG_ETRAX_RS485 */ - - count = rs_raw_write(tty, buf, count); - -#if defined(CONFIG_ETRAX_RS485) - if (info->rs485.flags & SER_RS485_ENABLED) - { - unsigned int val; - /* If we are in RS-485 mode the following has to be done: - * wait until DMA is ready - * wait on transmit shift register - * toggle RTS - * enable the receiver - */ - - /* Sleep until all sent */ - tty_wait_until_sent(tty, 0); -#ifdef CONFIG_ETRAX_FAST_TIMER - /* Now sleep a little more so that shift register is empty */ - schedule_usleep(info->char_time_usec * 2); -#endif - /* wait on transmit shift register */ - do{ - get_lsr_info(info, &val); - }while (!(val & TIOCSER_TEMT)); - - e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND)); - -#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) - e100_enable_rx(info); - e100_enable_rxdma_irq(info); -#endif - } -#endif /* CONFIG_ETRAX_RS485 */ - - return count; -} /* rs_write */ - - -/* how much space is available in the xmit buffer? */ - -static int -rs_write_room(struct tty_struct *tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - - return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); -} - -/* How many chars are in the xmit buffer? - * This does not include any chars in the transmitter FIFO. - * Use wait_until_sent for waiting for FIFO drain. - */ - -static int -rs_chars_in_buffer(struct tty_struct *tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - - return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); -} - -/* discard everything in the xmit buffer */ - -static void -rs_flush_buffer(struct tty_struct *tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - - local_irq_save(flags); - info->xmit.head = info->xmit.tail = 0; - local_irq_restore(flags); - - tty_wakeup(tty); -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - * - * Since we use DMA we don't check for info->x_char in transmit_chars_dma(), - * but we do it in handle_ser_tx_interrupt(). - * We disable DMA channel and enable tx ready interrupt and write the - * character when possible. - */ -static void rs_send_xchar(struct tty_struct *tty, char ch) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - local_irq_save(flags); - if (info->uses_dma_out) { - /* Put the DMA on hold and disable the channel */ - *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold); - while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) != - IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold)); - e100_disable_txdma_channel(info); - } - - /* Must make sure transmitter is not stopped before we can transmit */ - if (tty->stopped) - rs_start(tty); - - /* Enable manual transmit interrupt and send from there */ - DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch)); - info->x_char = ch; - e100_enable_serial_tx_ready_irq(info); - local_irq_restore(flags); -} - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void -rs_throttle(struct tty_struct * tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %lu....\n", tty_name(tty, buf), - (unsigned long)tty->ldisc.chars_in_buffer(tty)); -#endif - DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); - - /* Do RTS before XOFF since XOFF might take some time */ - if (tty->termios->c_cflag & CRTSCTS) { - /* Turn off RTS line */ - e100_rts(info, 0); - } - if (I_IXOFF(tty)) - rs_send_xchar(tty, STOP_CHAR(tty)); - -} - -static void -rs_unthrottle(struct tty_struct * tty) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %lu....\n", tty_name(tty, buf), - (unsigned long)tty->ldisc.chars_in_buffer(tty)); -#endif - DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); - DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count)); - /* Do RTS before XOFF since XOFF might take some time */ - if (tty->termios->c_cflag & CRTSCTS) { - /* Assert RTS line */ - e100_rts(info, 1); - } - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - rs_send_xchar(tty, START_CHAR(tty)); - } - -} - -/* - * ------------------------------------------------------------ - * rs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int -get_serial_info(struct e100_serial * info, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - - /* this is all probably wrong, there are a lot of fields - * here that we don't have in e100_serial and maybe we - * should set them to something else than 0. - */ - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = (int)info->ioport; - tmp.irq = info->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int -set_serial_info(struct e100_serial *info, - struct serial_struct *new_info) -{ - struct serial_struct new_serial; - struct e100_serial old_info; - int retval = 0; - - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - goto check_and_exit; - } - - if (info->count > 1) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->custom_divisor = new_serial.custom_divisor; - info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { - change_speed(info); - } else - retval = startup(info); - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int -get_lsr_info(struct e100_serial * info, unsigned int *value) -{ - unsigned int result = TIOCSER_TEMT; -#ifndef CONFIG_SVINTO_SIM - unsigned long curr_time = jiffies; - unsigned long curr_time_usec = GET_JIFFIES_USEC(); - unsigned long elapsed_usec = - (curr_time - info->last_tx_active) * 1000000/HZ + - curr_time_usec - info->last_tx_active_usec; - - if (info->xmit.head != info->xmit.tail || - elapsed_usec < 2*info->char_time_usec) { - result = 0; - } -#endif - - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - return 0; -} - -#ifdef SERIAL_DEBUG_IO -struct state_str -{ - int state; - const char *str; -}; - -const struct state_str control_state_str[] = { - {TIOCM_DTR, "DTR" }, - {TIOCM_RTS, "RTS"}, - {TIOCM_ST, "ST?" }, - {TIOCM_SR, "SR?" }, - {TIOCM_CTS, "CTS" }, - {TIOCM_CD, "CD" }, - {TIOCM_RI, "RI" }, - {TIOCM_DSR, "DSR" }, - {0, NULL } -}; - -char *get_control_state_str(int MLines, char *s) -{ - int i = 0; - - s[0]='\0'; - while (control_state_str[i].str != NULL) { - if (MLines & control_state_str[i].state) { - if (s[0] != '\0') { - strcat(s, ", "); - } - strcat(s, control_state_str[i].str); - } - i++; - } - return s; -} -#endif - -static int -rs_break(struct tty_struct *tty, int break_state) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - - if (!info->ioport) - return -EIO; - - local_irq_save(flags); - if (break_state == -1) { - /* Go to manual mode and set the txd pin to 0 */ - /* Clear bit 7 (txd) and 6 (tr_enable) */ - info->tx_ctrl &= 0x3F; - } else { - /* Set bit 7 (txd) and 6 (tr_enable) */ - info->tx_ctrl |= (0x80 | 0x40); - } - info->ioport[REG_TR_CTRL] = info->tx_ctrl; - local_irq_restore(flags); - return 0; -} - -static int -rs_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - - local_irq_save(flags); - - if (clear & TIOCM_RTS) - e100_rts(info, 0); - if (clear & TIOCM_DTR) - e100_dtr(info, 0); - /* Handle FEMALE behaviour */ - if (clear & TIOCM_RI) - e100_ri_out(info, 0); - if (clear & TIOCM_CD) - e100_cd_out(info, 0); - - if (set & TIOCM_RTS) - e100_rts(info, 1); - if (set & TIOCM_DTR) - e100_dtr(info, 1); - /* Handle FEMALE behaviour */ - if (set & TIOCM_RI) - e100_ri_out(info, 1); - if (set & TIOCM_CD) - e100_cd_out(info, 1); - - local_irq_restore(flags); - return 0; -} - -static int -rs_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned int result; - unsigned long flags; - - local_irq_save(flags); - - result = - (!E100_RTS_GET(info) ? TIOCM_RTS : 0) - | (!E100_DTR_GET(info) ? TIOCM_DTR : 0) - | (!E100_RI_GET(info) ? TIOCM_RNG : 0) - | (!E100_DSR_GET(info) ? TIOCM_DSR : 0) - | (!E100_CD_GET(info) ? TIOCM_CAR : 0) - | (!E100_CTS_GET(info) ? TIOCM_CTS : 0); - - local_irq_restore(flags); - -#ifdef SERIAL_DEBUG_IO - printk(KERN_DEBUG "ser%i: modem state: %i 0x%08X\n", - info->line, result, result); - { - char s[100]; - - get_control_state_str(result, s); - printk(KERN_DEBUG "state: %s\n", s); - } -#endif - return result; - -} - - -static int -rs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct e100_serial * info = (struct e100_serial *)tty->driver_data; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TIOCGSERIAL: - return get_serial_info(info, - (struct serial_struct *) arg); - case TIOCSSERIAL: - return set_serial_info(info, - (struct serial_struct *) arg); - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info(info, (unsigned int *) arg); - - case TIOCSERGSTRUCT: - if (copy_to_user((struct e100_serial *) arg, - info, sizeof(struct e100_serial))) - return -EFAULT; - return 0; - -#if defined(CONFIG_ETRAX_RS485) - case TIOCSERSETRS485: - { - /* In this ioctl we still use the old structure - * rs485_control for backward compatibility - * (if we use serial_rs485, then old user-level code - * wouldn't work anymore...). - * The use of this ioctl is deprecated: use TIOCSRS485 - * instead.*/ - struct rs485_control rs485ctrl; - struct serial_rs485 rs485data; - printk(KERN_DEBUG "The use of this ioctl is deprecated. Use TIOCSRS485 instead\n"); - if (copy_from_user(&rs485ctrl, (struct rs485_control *)arg, - sizeof(rs485ctrl))) - return -EFAULT; - - rs485data.delay_rts_before_send = rs485ctrl.delay_rts_before_send; - rs485data.flags = 0; - if (rs485data.delay_rts_before_send != 0) - rs485data.flags |= SER_RS485_RTS_BEFORE_SEND; - else - rs485data.flags &= ~(SER_RS485_RTS_BEFORE_SEND); - - if (rs485ctrl.enabled) - rs485data.flags |= SER_RS485_ENABLED; - else - rs485data.flags &= ~(SER_RS485_ENABLED); - - if (rs485ctrl.rts_on_send) - rs485data.flags |= SER_RS485_RTS_ON_SEND; - else - rs485data.flags &= ~(SER_RS485_RTS_ON_SEND); - - if (rs485ctrl.rts_after_sent) - rs485data.flags |= SER_RS485_RTS_AFTER_SEND; - else - rs485data.flags &= ~(SER_RS485_RTS_AFTER_SEND); - - return e100_enable_rs485(tty, &rs485data); - } - - case TIOCSRS485: - { - /* This is the new version of TIOCSRS485, with new - * data structure serial_rs485 */ - struct serial_rs485 rs485data; - if (copy_from_user(&rs485data, (struct rs485_control *)arg, - sizeof(rs485data))) - return -EFAULT; - - return e100_enable_rs485(tty, &rs485data); - } - - case TIOCGRS485: - { - struct serial_rs485 *rs485data = - &(((struct e100_serial *)tty->driver_data)->rs485); - /* This is the ioctl to get RS485 data from user-space */ - if (copy_to_user((struct serial_rs485 *) arg, - rs485data, - sizeof(struct serial_rs485))) - return -EFAULT; - break; - } - - case TIOCSERWRRS485: - { - struct rs485_write rs485wr; - if (copy_from_user(&rs485wr, (struct rs485_write *)arg, - sizeof(rs485wr))) - return -EFAULT; - - return e100_write_rs485(tty, rs485wr.outc, rs485wr.outc_size); - } -#endif - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void -rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - - change_speed(info); - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - rs_start(tty); - } - -} - -/* - * ------------------------------------------------------------ - * rs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * S structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void -rs_close(struct tty_struct *tty, struct file * filp) -{ - struct e100_serial * info = (struct e100_serial *)tty->driver_data; - unsigned long flags; - - if (!info) - return; - - /* interrupts are disabled for this entire function */ - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - local_irq_restore(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, - info->line, info->count); -#endif - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_CRIT - "rs_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - if (--info->count < 0) { - printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; - } - if (info->count) { - local_irq_restore(flags); - return; - } - info->flags |= ASYNC_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - /* - * At this point we stop accepting input. To do this, we - * disable the serial receiver and the DMA receive interrupt. - */ -#ifdef SERIAL_HANDLE_EARLY_ERRORS - e100_disable_serial_data_irq(info); -#endif - -#ifndef CONFIG_SVINTO_SIM - e100_disable_rx(info); - e100_disable_rx_irq(info); - - if (info->flags & ASYNC_INITIALIZED) { - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important as we have a transmit FIFO! - */ - rs_wait_until_sent(tty, HZ); - } -#endif - - shutdown(info); - rs_flush_buffer(tty); - tty_ldisc_flush(tty); - tty->closing = 0; - info->event = 0; - info->port.tty = NULL; - if (info->blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); - - /* port closed */ - -#if defined(CONFIG_ETRAX_RS485) - if (info->rs485.flags & SER_RS485_ENABLED) { - info->rs485.flags &= ~(SER_RS485_ENABLED); -#if defined(CONFIG_ETRAX_RS485_ON_PA) - *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit); -#endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - rs485_port_g_bit, 0); -#endif -#if defined(CONFIG_ETRAX_RS485_LTC1387) - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 0); - REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, - CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 0); -#endif - } -#endif - - /* - * Release any allocated DMA irq's. - */ - if (info->dma_in_enabled) { - free_irq(info->dma_in_irq_nbr, info); - cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description); - info->uses_dma_in = 0; -#ifdef SERIAL_DEBUG_OPEN - printk(KERN_DEBUG "DMA irq '%s' freed\n", - info->dma_in_irq_description); -#endif - } - if (info->dma_out_enabled) { - free_irq(info->dma_out_irq_nbr, info); - cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description); - info->uses_dma_out = 0; -#ifdef SERIAL_DEBUG_OPEN - printk(KERN_DEBUG "DMA irq '%s' freed\n", - info->dma_out_irq_description); -#endif - } -} - -/* - * rs_wait_until_sent() --- wait until the transmitter is empty - */ -static void rs_wait_until_sent(struct tty_struct *tty, int timeout) -{ - unsigned long orig_jiffies; - struct e100_serial *info = (struct e100_serial *)tty->driver_data; - unsigned long curr_time = jiffies; - unsigned long curr_time_usec = GET_JIFFIES_USEC(); - long elapsed_usec = - (curr_time - info->last_tx_active) * (1000000/HZ) + - curr_time_usec - info->last_tx_active_usec; - - /* - * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO - * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) - */ - orig_jiffies = jiffies; - while (info->xmit.head != info->xmit.tail || /* More in send queue */ - (*info->ostatusadr & 0x007f) || /* more in FIFO */ - (elapsed_usec < 2*info->char_time_usec)) { - schedule_timeout_interruptible(1); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - curr_time = jiffies; - curr_time_usec = GET_JIFFIES_USEC(); - elapsed_usec = - (curr_time - info->last_tx_active) * (1000000/HZ) + - curr_time_usec - info->last_tx_active_usec; - } - set_current_state(TASK_RUNNING); -} - -/* - * rs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void -rs_hangup(struct tty_struct *tty) -{ - struct e100_serial * info = (struct e100_serial *)tty->driver_data; - - rs_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int -block_til_ready(struct tty_struct *tty, struct file * filp, - struct e100_serial *info) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int retval; - int do_clocal = 0, extra_count = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, - !(info->flags & ASYNC_CLOSING)); -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) { - do_clocal = 1; - } - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * rs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); -#endif - local_irq_save(flags); - if (!tty_hung_up_p(filp)) { - extra_count++; - info->count--; - } - local_irq_restore(flags); - info->blocked_open++; - while (1) { - local_irq_save(flags); - /* assert RTS and DTR */ - e100_rts(info, 1); - e100_dtr(info, 1); - local_irq_restore(flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ASYNC_CLOSING) && do_clocal) - /* && (do_clocal || DCD_IS_ASSERTED) */ - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyS%d, count = %d\n", - info->line, info->count); -#endif - tty_unlock(); - schedule(); - tty_lock(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - if (extra_count) - info->count++; - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static void -deinit_port(struct e100_serial *info) -{ - if (info->dma_out_enabled) { - cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description); - free_irq(info->dma_out_irq_nbr, info); - } - if (info->dma_in_enabled) { - cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description); - free_irq(info->dma_in_irq_nbr, info); - } -} - -/* - * This routine is called whenever a serial port is opened. - * It performs the serial-specific initialization for the tty structure. - */ -static int -rs_open(struct tty_struct *tty, struct file * filp) -{ - struct e100_serial *info; - int retval, line; - unsigned long page; - int allocated_resources = 0; - - /* find which port we want to open */ - line = tty->index; - - if (line < 0 || line >= NR_PORTS) - return -ENODEV; - - /* find the corresponding e100_serial struct in the table */ - info = rs_table + line; - - /* don't allow the opening of ports that are not enabled in the HW config */ - if (!info->enabled) - return -ENODEV; - -#ifdef SERIAL_DEBUG_OPEN - printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, - info->count); -#endif - - info->count++; - tty->driver_data = info; - info->port.tty = tty; - - info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - if (!tmp_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) { - return -ENOMEM; - } - if (tmp_buf) - free_page(page); - else - tmp_buf = (unsigned char *) page; - } - - /* - * If the port is in the middle of closing, bail out now - */ - if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible_tty(info->close_wait, - !(info->flags & ASYNC_CLOSING)); -#ifdef SERIAL_DO_RESTART - return ((info->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); -#else - return -EAGAIN; -#endif - } - - /* - * If DMA is enabled try to allocate the irq's. - */ - if (info->count == 1) { - allocated_resources = 1; - if (info->dma_in_enabled) { - if (request_irq(info->dma_in_irq_nbr, - rec_interrupt, - info->dma_in_irq_flags, - info->dma_in_irq_description, - info)) { - printk(KERN_WARNING "DMA irq '%s' busy; " - "falling back to non-DMA mode\n", - info->dma_in_irq_description); - /* Make sure we never try to use DMA in */ - /* for the port again. */ - info->dma_in_enabled = 0; - } else if (cris_request_dma(info->dma_in_nbr, - info->dma_in_irq_description, - DMA_VERBOSE_ON_ERROR, - info->dma_owner)) { - free_irq(info->dma_in_irq_nbr, info); - printk(KERN_WARNING "DMA '%s' busy; " - "falling back to non-DMA mode\n", - info->dma_in_irq_description); - /* Make sure we never try to use DMA in */ - /* for the port again. */ - info->dma_in_enabled = 0; - } -#ifdef SERIAL_DEBUG_OPEN - else - printk(KERN_DEBUG "DMA irq '%s' allocated\n", - info->dma_in_irq_description); -#endif - } - if (info->dma_out_enabled) { - if (request_irq(info->dma_out_irq_nbr, - tr_interrupt, - info->dma_out_irq_flags, - info->dma_out_irq_description, - info)) { - printk(KERN_WARNING "DMA irq '%s' busy; " - "falling back to non-DMA mode\n", - info->dma_out_irq_description); - /* Make sure we never try to use DMA out */ - /* for the port again. */ - info->dma_out_enabled = 0; - } else if (cris_request_dma(info->dma_out_nbr, - info->dma_out_irq_description, - DMA_VERBOSE_ON_ERROR, - info->dma_owner)) { - free_irq(info->dma_out_irq_nbr, info); - printk(KERN_WARNING "DMA '%s' busy; " - "falling back to non-DMA mode\n", - info->dma_out_irq_description); - /* Make sure we never try to use DMA out */ - /* for the port again. */ - info->dma_out_enabled = 0; - } -#ifdef SERIAL_DEBUG_OPEN - else - printk(KERN_DEBUG "DMA irq '%s' allocated\n", - info->dma_out_irq_description); -#endif - } - } - - /* - * Start up the serial port - */ - - retval = startup(info); - if (retval) { - if (allocated_resources) - deinit_port(info); - - /* FIXME Decrease count info->count here too? */ - return retval; - } - - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open returning after block_til_ready with %d\n", - retval); -#endif - if (allocated_resources) - deinit_port(info); - - return retval; - } - - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - *tty->termios = info->normal_termios; - change_speed(info); - } - -#ifdef SERIAL_DEBUG_OPEN - printk("rs_open ttyS%d successful...\n", info->line); -#endif - DLOG_INT_TRIG( log_int_pos = 0); - - DFLIP( if (info->line == SERIAL_DEBUG_LINE) { - info->icount.rx = 0; - } ); - - return 0; -} - -#ifdef CONFIG_PROC_FS -/* - * /proc fs routines.... - */ - -static void seq_line_info(struct seq_file *m, struct e100_serial *info) -{ - unsigned long tmp; - - seq_printf(m, "%d: uart:E100 port:%lX irq:%d", - info->line, (unsigned long)info->ioport, info->irq); - - if (!info->ioport || (info->type == PORT_UNKNOWN)) { - seq_printf(m, "\n"); - return; - } - - seq_printf(m, " baud:%d", info->baud); - seq_printf(m, " tx:%lu rx:%lu", - (unsigned long)info->icount.tx, - (unsigned long)info->icount.rx); - tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); - if (tmp) - seq_printf(m, " tx_pend:%lu/%lu", - (unsigned long)tmp, - (unsigned long)SERIAL_XMIT_SIZE); - - seq_printf(m, " rx_pend:%lu/%lu", - (unsigned long)info->recv_cnt, - (unsigned long)info->max_recv_cnt); - -#if 1 - if (info->port.tty) { - if (info->port.tty->stopped) - seq_printf(m, " stopped:%i", - (int)info->port.tty->stopped); - if (info->port.tty->hw_stopped) - seq_printf(m, " hw_stopped:%i", - (int)info->port.tty->hw_stopped); - } - - { - unsigned char rstat = info->ioport[REG_STATUS]; - if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect)) - seq_printf(m, " xoff_detect:1"); - } - -#endif - - if (info->icount.frame) - seq_printf(m, " fe:%lu", (unsigned long)info->icount.frame); - - if (info->icount.parity) - seq_printf(m, " pe:%lu", (unsigned long)info->icount.parity); - - if (info->icount.brk) - seq_printf(m, " brk:%lu", (unsigned long)info->icount.brk); - - if (info->icount.overrun) - seq_printf(m, " oe:%lu", (unsigned long)info->icount.overrun); - - /* - * Last thing is the RS-232 status lines - */ - if (!E100_RTS_GET(info)) - seq_puts(m, "|RTS"); - if (!E100_CTS_GET(info)) - seq_puts(m, "|CTS"); - if (!E100_DTR_GET(info)) - seq_puts(m, "|DTR"); - if (!E100_DSR_GET(info)) - seq_puts(m, "|DSR"); - if (!E100_CD_GET(info)) - seq_puts(m, "|CD"); - if (!E100_RI_GET(info)) - seq_puts(m, "|RI"); - seq_puts(m, "\n"); -} - - -static int crisv10_proc_show(struct seq_file *m, void *v) -{ - int i; - - seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version); - - for (i = 0; i < NR_PORTS; i++) { - if (!rs_table[i].enabled) - continue; - seq_line_info(m, &rs_table[i]); - } -#ifdef DEBUG_LOG_INCLUDED - for (i = 0; i < debug_log_pos; i++) { - seq_printf(m, "%-4i %lu.%lu ", - i, debug_log[i].time, - timer_data_to_ns(debug_log[i].timer_data)); - seq_printf(m, debug_log[i].string, debug_log[i].value); - } - seq_printf(m, "debug_log %i/%i\n", i, DEBUG_LOG_SIZE); - debug_log_pos = 0; -#endif - return 0; -} - -static int crisv10_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, crisv10_proc_show, NULL); -} - -static const struct file_operations crisv10_proc_fops = { - .owner = THIS_MODULE, - .open = crisv10_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - - -/* Finally, routines used to initialize the serial driver. */ - -static void show_serial_version(void) -{ - printk(KERN_INFO - "ETRAX 100LX serial-driver %s, " - "(c) 2000-2004 Axis Communications AB\r\n", - &serial_version[11]); /* "$Revision: x.yy" */ -} - -/* rs_init inits the driver at boot (using the module_init chain) */ - -static const struct tty_operations rs_ops = { - .open = rs_open, - .close = rs_close, - .write = rs_write, - .flush_chars = rs_flush_chars, - .write_room = rs_write_room, - .chars_in_buffer = rs_chars_in_buffer, - .flush_buffer = rs_flush_buffer, - .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, - .set_termios = rs_set_termios, - .stop = rs_stop, - .start = rs_start, - .hangup = rs_hangup, - .break_ctl = rs_break, - .send_xchar = rs_send_xchar, - .wait_until_sent = rs_wait_until_sent, - .tiocmget = rs_tiocmget, - .tiocmset = rs_tiocmset, -#ifdef CONFIG_PROC_FS - .proc_fops = &crisv10_proc_fops, -#endif -}; - -static int __init rs_init(void) -{ - int i; - struct e100_serial *info; - struct tty_driver *driver = alloc_tty_driver(NR_PORTS); - - if (!driver) - return -ENOMEM; - - show_serial_version(); - - /* Setup the timed flush handler system */ - -#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) - setup_timer(&flush_timer, timed_flush_handler, 0); - mod_timer(&flush_timer, jiffies + 5); -#endif - -#if defined(CONFIG_ETRAX_RS485) -#if defined(CONFIG_ETRAX_RS485_ON_PA) - if (cris_io_interface_allocate_pins(if_ser0, 'a', rs485_pa_bit, - rs485_pa_bit)) { - printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " - "RS485 pin\n"); - put_tty_driver(driver); - return -EBUSY; - } -#endif -#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) - if (cris_io_interface_allocate_pins(if_ser0, 'g', rs485_pa_bit, - rs485_port_g_bit)) { - printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " - "RS485 pin\n"); - put_tty_driver(driver); - return -EBUSY; - } -#endif -#endif - - /* Initialize the tty_driver structure */ - - driver->driver_name = "serial"; - driver->name = "ttyS"; - driver->major = TTY_MAJOR; - driver->minor_start = 64; - driver->type = TTY_DRIVER_TYPE_SERIAL; - driver->subtype = SERIAL_TYPE_NORMAL; - driver->init_termios = tty_std_termios; - driver->init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ - driver->init_termios.c_ispeed = 115200; - driver->init_termios.c_ospeed = 115200; - driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - - tty_set_operations(driver, &rs_ops); - serial_driver = driver; - if (tty_register_driver(driver)) - panic("Couldn't register serial driver\n"); - /* do some initializing for the separate ports */ - - for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { - if (info->enabled) { - if (cris_request_io_interface(info->io_if, - info->io_if_description)) { - printk(KERN_CRIT "ETRAX100LX async serial: " - "Could not allocate IO pins for " - "%s, port %d\n", - info->io_if_description, i); - info->enabled = 0; - } - } - info->uses_dma_in = 0; - info->uses_dma_out = 0; - info->line = i; - info->port.tty = NULL; - info->type = PORT_ETRAX; - info->tr_running = 0; - info->forced_eop = 0; - info->baud_base = DEF_BAUD_BASE; - info->custom_divisor = 0; - info->flags = 0; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - info->normal_termios = driver->init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - info->xmit.buf = NULL; - info->xmit.tail = info->xmit.head = 0; - info->first_recv_buffer = info->last_recv_buffer = NULL; - info->recv_cnt = info->max_recv_cnt = 0; - info->last_tx_active_usec = 0; - info->last_tx_active = 0; - -#if defined(CONFIG_ETRAX_RS485) - /* Set sane defaults */ - info->rs485.flags &= ~(SER_RS485_RTS_ON_SEND); - info->rs485.flags |= SER_RS485_RTS_AFTER_SEND; - info->rs485.flags &= ~(SER_RS485_RTS_BEFORE_SEND); - info->rs485.delay_rts_before_send = 0; - info->rs485.flags &= ~(SER_RS485_ENABLED); -#endif - INIT_WORK(&info->work, do_softint); - - if (info->enabled) { - printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n", - serial_driver->name, info->line, info->ioport); - } - } -#ifdef CONFIG_ETRAX_FAST_TIMER -#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER - memset(fast_timers, 0, sizeof(fast_timers)); -#endif -#ifdef CONFIG_ETRAX_RS485 - memset(fast_timers_rs485, 0, sizeof(fast_timers_rs485)); -#endif - fast_timer_init(); -#endif - -#ifndef CONFIG_SVINTO_SIM -#ifndef CONFIG_ETRAX_KGDB - /* Not needed in simulator. May only complicate stuff. */ - /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */ - - if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, - IRQF_SHARED | IRQF_DISABLED, "serial ", driver)) - panic("%s: Failed to request irq8", __func__); - -#endif -#endif /* CONFIG_SVINTO_SIM */ - - return 0; -} - -/* this makes sure that rs_init is called during kernel boot */ - -module_init(rs_init); diff --git a/drivers/serial/crisv10.h b/drivers/serial/crisv10.h deleted file mode 100644 index ea0beb46a10d..000000000000 --- a/drivers/serial/crisv10.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * serial.h: Arch-dep definitions for the Etrax100 serial driver. - * - * Copyright (C) 1998-2007 Axis Communications AB - */ - -#ifndef _ETRAX_SERIAL_H -#define _ETRAX_SERIAL_H - -#include -#include -#include -#include - -/* Software state per channel */ - -#ifdef __KERNEL__ -/* - * This is our internal structure for each serial port's state. - * - * Many fields are paralleled by the structure used by the serial_struct - * structure. - * - * For definitions of the flags field, see tty.h - */ - -#define SERIAL_RECV_DESCRIPTORS 8 - -struct etrax_recv_buffer { - struct etrax_recv_buffer *next; - unsigned short length; - unsigned char error; - unsigned char pad; - - unsigned char buffer[0]; -}; - -struct e100_serial { - struct tty_port port; - int baud; - volatile u8 *ioport; /* R_SERIALx_CTRL */ - u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ - - /* Output registers */ - volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ - volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */ - volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */ - const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */ - - /* Input registers */ - volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ - volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */ - volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ - volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ - - int flags; /* defined in tty.h */ - - u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ - u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ - u8 iseteop; /* bit number for R_SET_EOP for the input dma */ - int enabled; /* Set to 1 if the port is enabled in HW config */ - - u8 dma_out_enabled; /* Set to 1 if DMA should be used */ - u8 dma_in_enabled; /* Set to 1 if DMA should be used */ - - /* end of fields defined in rs_table[] in .c-file */ - int dma_owner; - unsigned int dma_in_nbr; - unsigned int dma_out_nbr; - unsigned int dma_in_irq_nbr; - unsigned int dma_out_irq_nbr; - unsigned long dma_in_irq_flags; - unsigned long dma_out_irq_flags; - char *dma_in_irq_description; - char *dma_out_irq_description; - - enum cris_io_interface io_if; - char *io_if_description; - - u8 uses_dma_in; /* Set to 1 if DMA is used */ - u8 uses_dma_out; /* Set to 1 if DMA is used */ - u8 forced_eop; /* a fifo eop has been forced */ - int baud_base; /* For special baudrates */ - int custom_divisor; /* For special baudrates */ - struct etrax_dma_descr tr_descr; - struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS]; - int cur_rec_descr; - - volatile int tr_running; /* 1 if output is running */ - - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - unsigned long last_active; - int line; - int type; /* PORT_ETRAX */ - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - struct circ_buf xmit; - struct etrax_recv_buffer *first_recv_buffer; - struct etrax_recv_buffer *last_recv_buffer; - unsigned int recv_cnt; - unsigned int max_recv_cnt; - - struct work_struct work; - struct async_icount icount; /* error-statistics etc.*/ - struct ktermios normal_termios; - struct ktermios callout_termios; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - - unsigned long char_time_usec; /* The time for 1 char, in usecs */ - unsigned long flush_time_usec; /* How often we should flush */ - unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */ - unsigned long last_tx_active; /* Last tx time in jiffies */ - unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */ - unsigned long last_rx_active; /* Last rx time in jiffies */ - - int break_detected_cnt; - int errorcode; - -#ifdef CONFIG_ETRAX_RS485 - struct serial_rs485 rs485; /* RS-485 support */ -#endif -}; - -/* this PORT is not in the standard serial.h. it's not actually used for - * anything since we only have one type of async serial-port anyway in this - * system. - */ - -#define PORT_ETRAX 1 - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define RS_EVENT_WRITE_WAKEUP 0 - -#endif /* __KERNEL__ */ - -#endif /* !_ETRAX_SERIAL_H */ diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c deleted file mode 100644 index 57421d776329..000000000000 --- a/drivers/serial/dz.c +++ /dev/null @@ -1,955 +0,0 @@ -/* - * dz.c: Serial port driver for DECstations equipped - * with the DZ chipset. - * - * Copyright (C) 1998 Olivier A. D. Lebaillif - * - * Email: olivier.lebaillif@ifrsys.com - * - * Copyright (C) 2004, 2006, 2007 Maciej W. Rozycki - * - * [31-AUG-98] triemer - * Changed IRQ to use Harald's dec internals interrupts.h - * removed base_addr code - moving address assignment to setup.c - * Changed name of dz_init to rs_init to be consistent with tc code - * [13-NOV-98] triemer fixed code to receive characters - * after patches by harald to irq code. - * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout - * field from "current" - somewhere between 2.1.121 and 2.1.131 - Qua Jun 27 15:02:26 BRT 2001 - * [27-JUN-2001] Arnaldo Carvalho de Melo - cleanups - * - * Parts (C) 1999 David Airlie, airlied@linux.ie - * [07-SEP-99] Bugfixes - * - * [06-Jan-2002] Russell King - * Converted to new serial core - */ - -#undef DEBUG_DZ - -#if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#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 - -#include "dz.h" - - -MODULE_DESCRIPTION("DECstation DZ serial driver"); -MODULE_LICENSE("GPL"); - - -static char dz_name[] __initdata = "DECstation DZ serial driver version "; -static char dz_version[] __initdata = "1.04"; - -struct dz_port { - struct dz_mux *mux; - struct uart_port port; - unsigned int cflag; -}; - -struct dz_mux { - struct dz_port dport[DZ_NB_PORT]; - atomic_t map_guard; - atomic_t irq_guard; - int initialised; -}; - -static struct dz_mux dz_mux; - -static inline struct dz_port *to_dport(struct uart_port *uport) -{ - return container_of(uport, struct dz_port, port); -} - -/* - * ------------------------------------------------------------ - * dz_in () and dz_out () - * - * These routines are used to access the registers of the DZ - * chip, hiding relocation differences between implementation. - * ------------------------------------------------------------ - */ - -static u16 dz_in(struct dz_port *dport, unsigned offset) -{ - void __iomem *addr = dport->port.membase + offset; - - return readw(addr); -} - -static void dz_out(struct dz_port *dport, unsigned offset, u16 value) -{ - void __iomem *addr = dport->port.membase + offset; - - writew(value, addr); -} - -/* - * ------------------------------------------------------------ - * rs_stop () and rs_start () - * - * These routines are called before setting or resetting - * tty->stopped. They enable or disable transmitter interrupts, - * as necessary. - * ------------------------------------------------------------ - */ - -static void dz_stop_tx(struct uart_port *uport) -{ - struct dz_port *dport = to_dport(uport); - u16 tmp, mask = 1 << dport->port.line; - - tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ - tmp &= ~mask; /* clear the TX flag */ - dz_out(dport, DZ_TCR, tmp); -} - -static void dz_start_tx(struct uart_port *uport) -{ - struct dz_port *dport = to_dport(uport); - u16 tmp, mask = 1 << dport->port.line; - - tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ - tmp |= mask; /* set the TX flag */ - dz_out(dport, DZ_TCR, tmp); -} - -static void dz_stop_rx(struct uart_port *uport) -{ - struct dz_port *dport = to_dport(uport); - - dport->cflag &= ~DZ_RXENAB; - dz_out(dport, DZ_LPR, dport->cflag); -} - -static void dz_enable_ms(struct uart_port *uport) -{ - /* nothing to do */ -} - -/* - * ------------------------------------------------------------ - * - * Here start the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * dz_interrupt. They were separated out for readability's sake. - * - * Note: dz_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * dz_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * make drivers/serial/dz.s - * - * and look at the resulting assemble code in dz.s. - * - * ------------------------------------------------------------ - */ - -/* - * ------------------------------------------------------------ - * receive_char () - * - * This routine deals with inputs from any lines. - * ------------------------------------------------------------ - */ -static inline void dz_receive_chars(struct dz_mux *mux) -{ - struct uart_port *uport; - struct dz_port *dport = &mux->dport[0]; - struct tty_struct *tty = NULL; - struct uart_icount *icount; - int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; - unsigned char ch, flag; - u16 status; - int i; - - while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) { - dport = &mux->dport[LINE(status)]; - uport = &dport->port; - tty = uport->state->port.tty; /* point to the proper dev */ - - ch = UCHAR(status); /* grab the char */ - flag = TTY_NORMAL; - - icount = &uport->icount; - icount->rx++; - - if (unlikely(status & (DZ_OERR | DZ_FERR | DZ_PERR))) { - - /* - * There is no separate BREAK status bit, so treat - * null characters with framing errors as BREAKs; - * normally, otherwise. For this move the Framing - * Error bit to a simulated BREAK bit. - */ - if (!ch) { - status |= (status & DZ_FERR) >> - (ffs(DZ_FERR) - ffs(DZ_BREAK)); - status &= ~DZ_FERR; - } - - /* Handle SysRq/SAK & keep track of the statistics. */ - if (status & DZ_BREAK) { - icount->brk++; - if (uart_handle_break(uport)) - continue; - } else if (status & DZ_FERR) - icount->frame++; - else if (status & DZ_PERR) - icount->parity++; - if (status & DZ_OERR) - icount->overrun++; - - status &= uport->read_status_mask; - if (status & DZ_BREAK) - flag = TTY_BREAK; - else if (status & DZ_FERR) - flag = TTY_FRAME; - else if (status & DZ_PERR) - flag = TTY_PARITY; - - } - - if (uart_handle_sysrq_char(uport, ch)) - continue; - - uart_insert_char(uport, status, DZ_OERR, ch, flag); - lines_rx[LINE(status)] = 1; - } - for (i = 0; i < DZ_NB_PORT; i++) - if (lines_rx[i]) - tty_flip_buffer_push(mux->dport[i].port.state->port.tty); -} - -/* - * ------------------------------------------------------------ - * transmit_char () - * - * This routine deals with outputs to any lines. - * ------------------------------------------------------------ - */ -static inline void dz_transmit_chars(struct dz_mux *mux) -{ - struct dz_port *dport = &mux->dport[0]; - struct circ_buf *xmit; - unsigned char tmp; - u16 status; - - status = dz_in(dport, DZ_CSR); - dport = &mux->dport[LINE(status)]; - xmit = &dport->port.state->xmit; - - if (dport->port.x_char) { /* XON/XOFF chars */ - dz_out(dport, DZ_TDR, dport->port.x_char); - dport->port.icount.tx++; - dport->port.x_char = 0; - return; - } - /* If nothing to do or stopped or hardware stopped. */ - if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { - spin_lock(&dport->port.lock); - dz_stop_tx(&dport->port); - spin_unlock(&dport->port.lock); - return; - } - - /* - * If something to do... (remember the dz has no output fifo, - * so we go one char at a time) :-< - */ - tmp = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); - dz_out(dport, DZ_TDR, tmp); - dport->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) - uart_write_wakeup(&dport->port); - - /* Are we are done. */ - if (uart_circ_empty(xmit)) { - spin_lock(&dport->port.lock); - dz_stop_tx(&dport->port); - spin_unlock(&dport->port.lock); - } -} - -/* - * ------------------------------------------------------------ - * check_modem_status() - * - * DS 3100 & 5100: Only valid for the MODEM line, duh! - * DS 5000/200: Valid for the MODEM and PRINTER line. - * ------------------------------------------------------------ - */ -static inline void check_modem_status(struct dz_port *dport) -{ - /* - * FIXME: - * 1. No status change interrupt; use a timer. - * 2. Handle the 3100/5000 as appropriate. --macro - */ - u16 status; - - /* If not the modem line just return. */ - if (dport->port.line != DZ_MODEM) - return; - - status = dz_in(dport, DZ_MSR); - - /* it's easy, since DSR2 is the only bit in the register */ - if (status) - dport->port.icount.dsr++; -} - -/* - * ------------------------------------------------------------ - * dz_interrupt () - * - * this is the main interrupt routine for the DZ chip. - * It deals with the multiple ports. - * ------------------------------------------------------------ - */ -static irqreturn_t dz_interrupt(int irq, void *dev_id) -{ - struct dz_mux *mux = dev_id; - struct dz_port *dport = &mux->dport[0]; - u16 status; - - /* get the reason why we just got an irq */ - status = dz_in(dport, DZ_CSR); - - if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) - dz_receive_chars(mux); - - if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) - dz_transmit_chars(mux); - - return IRQ_HANDLED; -} - -/* - * ------------------------------------------------------------------- - * Here ends the DZ interrupt routines. - * ------------------------------------------------------------------- - */ - -static unsigned int dz_get_mctrl(struct uart_port *uport) -{ - /* - * FIXME: Handle the 3100/5000 as appropriate. --macro - */ - struct dz_port *dport = to_dport(uport); - unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; - - if (dport->port.line == DZ_MODEM) { - if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) - mctrl &= ~TIOCM_DSR; - } - - return mctrl; -} - -static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) -{ - /* - * FIXME: Handle the 3100/5000 as appropriate. --macro - */ - struct dz_port *dport = to_dport(uport); - u16 tmp; - - if (dport->port.line == DZ_MODEM) { - tmp = dz_in(dport, DZ_TCR); - if (mctrl & TIOCM_DTR) - tmp &= ~DZ_MODEM_DTR; - else - tmp |= DZ_MODEM_DTR; - dz_out(dport, DZ_TCR, tmp); - } -} - -/* - * ------------------------------------------------------------------- - * startup () - * - * various initialization tasks - * ------------------------------------------------------------------- - */ -static int dz_startup(struct uart_port *uport) -{ - struct dz_port *dport = to_dport(uport); - struct dz_mux *mux = dport->mux; - unsigned long flags; - int irq_guard; - int ret; - u16 tmp; - - irq_guard = atomic_add_return(1, &mux->irq_guard); - if (irq_guard != 1) - return 0; - - ret = request_irq(dport->port.irq, dz_interrupt, - IRQF_SHARED, "dz", mux); - if (ret) { - atomic_add(-1, &mux->irq_guard); - printk(KERN_ERR "dz: Cannot get IRQ %d!\n", dport->port.irq); - return ret; - } - - spin_lock_irqsave(&dport->port.lock, flags); - - /* Enable interrupts. */ - tmp = dz_in(dport, DZ_CSR); - tmp |= DZ_RIE | DZ_TIE; - dz_out(dport, DZ_CSR, tmp); - - spin_unlock_irqrestore(&dport->port.lock, flags); - - return 0; -} - -/* - * ------------------------------------------------------------------- - * shutdown () - * - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - * ------------------------------------------------------------------- - */ -static void dz_shutdown(struct uart_port *uport) -{ - struct dz_port *dport = to_dport(uport); - struct dz_mux *mux = dport->mux; - unsigned long flags; - int irq_guard; - u16 tmp; - - spin_lock_irqsave(&dport->port.lock, flags); - dz_stop_tx(&dport->port); - spin_unlock_irqrestore(&dport->port.lock, flags); - - irq_guard = atomic_add_return(-1, &mux->irq_guard); - if (!irq_guard) { - /* Disable interrupts. */ - tmp = dz_in(dport, DZ_CSR); - tmp &= ~(DZ_RIE | DZ_TIE); - dz_out(dport, DZ_CSR, tmp); - - free_irq(dport->port.irq, mux); - } -} - -/* - * ------------------------------------------------------------------- - * dz_tx_empty() -- get the transmitter empty status - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - * ------------------------------------------------------------------- - */ -static unsigned int dz_tx_empty(struct uart_port *uport) -{ - struct dz_port *dport = to_dport(uport); - unsigned short tmp, mask = 1 << dport->port.line; - - tmp = dz_in(dport, DZ_TCR); - tmp &= mask; - - return tmp ? 0 : TIOCSER_TEMT; -} - -static void dz_break_ctl(struct uart_port *uport, int break_state) -{ - /* - * FIXME: Can't access BREAK bits in TDR easily; - * reuse the code for polled TX. --macro - */ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - unsigned short tmp, mask = 1 << dport->port.line; - - spin_lock_irqsave(&uport->lock, flags); - tmp = dz_in(dport, DZ_TCR); - if (break_state) - tmp |= mask; - else - tmp &= ~mask; - dz_out(dport, DZ_TCR, tmp); - spin_unlock_irqrestore(&uport->lock, flags); -} - -static int dz_encode_baud_rate(unsigned int baud) -{ - switch (baud) { - case 50: - return DZ_B50; - case 75: - return DZ_B75; - case 110: - return DZ_B110; - case 134: - return DZ_B134; - case 150: - return DZ_B150; - case 300: - return DZ_B300; - case 600: - return DZ_B600; - case 1200: - return DZ_B1200; - case 1800: - return DZ_B1800; - case 2000: - return DZ_B2000; - case 2400: - return DZ_B2400; - case 3600: - return DZ_B3600; - case 4800: - return DZ_B4800; - case 7200: - return DZ_B7200; - case 9600: - return DZ_B9600; - default: - return -1; - } -} - - -static void dz_reset(struct dz_port *dport) -{ - struct dz_mux *mux = dport->mux; - - if (mux->initialised) - return; - - dz_out(dport, DZ_CSR, DZ_CLR); - while (dz_in(dport, DZ_CSR) & DZ_CLR); - iob(); - - /* Enable scanning. */ - dz_out(dport, DZ_CSR, DZ_MSE); - - mux->initialised = 1; -} - -static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, - struct ktermios *old_termios) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - unsigned int cflag, baud; - int bflag; - - cflag = dport->port.line; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cflag |= DZ_CS5; - break; - case CS6: - cflag |= DZ_CS6; - break; - case CS7: - cflag |= DZ_CS7; - break; - case CS8: - default: - cflag |= DZ_CS8; - } - - if (termios->c_cflag & CSTOPB) - cflag |= DZ_CSTOPB; - if (termios->c_cflag & PARENB) - cflag |= DZ_PARENB; - if (termios->c_cflag & PARODD) - cflag |= DZ_PARODD; - - baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600); - bflag = dz_encode_baud_rate(baud); - if (bflag < 0) { /* Try to keep unchanged. */ - baud = uart_get_baud_rate(uport, old_termios, NULL, 50, 9600); - bflag = dz_encode_baud_rate(baud); - if (bflag < 0) { /* Resort to 9600. */ - baud = 9600; - bflag = DZ_B9600; - } - tty_termios_encode_baud_rate(termios, baud, baud); - } - cflag |= bflag; - - if (termios->c_cflag & CREAD) - cflag |= DZ_RXENAB; - - spin_lock_irqsave(&dport->port.lock, flags); - - uart_update_timeout(uport, termios->c_cflag, baud); - - dz_out(dport, DZ_LPR, cflag); - dport->cflag = cflag; - - /* setup accept flag */ - dport->port.read_status_mask = DZ_OERR; - if (termios->c_iflag & INPCK) - dport->port.read_status_mask |= DZ_FERR | DZ_PERR; - if (termios->c_iflag & (BRKINT | PARMRK)) - dport->port.read_status_mask |= DZ_BREAK; - - /* characters to ignore */ - uport->ignore_status_mask = 0; - if ((termios->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) - dport->port.ignore_status_mask |= DZ_OERR; - if (termios->c_iflag & IGNPAR) - dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR; - if (termios->c_iflag & IGNBRK) - dport->port.ignore_status_mask |= DZ_BREAK; - - spin_unlock_irqrestore(&dport->port.lock, flags); -} - -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void dz_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - - spin_lock_irqsave(&dport->port.lock, flags); - if (state < 3) - dz_start_tx(&dport->port); - else - dz_stop_tx(&dport->port); - spin_unlock_irqrestore(&dport->port.lock, flags); -} - - -static const char *dz_type(struct uart_port *uport) -{ - return "DZ"; -} - -static void dz_release_port(struct uart_port *uport) -{ - struct dz_mux *mux = to_dport(uport)->mux; - int map_guard; - - iounmap(uport->membase); - uport->membase = NULL; - - map_guard = atomic_add_return(-1, &mux->map_guard); - if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); -} - -static int dz_map_port(struct uart_port *uport) -{ - if (!uport->membase) - uport->membase = ioremap_nocache(uport->mapbase, - dec_kn_slot_size); - if (!uport->membase) { - printk(KERN_ERR "dz: Cannot map MMIO\n"); - return -ENOMEM; - } - return 0; -} - -static int dz_request_port(struct uart_port *uport) -{ - struct dz_mux *mux = to_dport(uport)->mux; - int map_guard; - int ret; - - map_guard = atomic_add_return(1, &mux->map_guard); - if (map_guard == 1) { - if (!request_mem_region(uport->mapbase, dec_kn_slot_size, - "dz")) { - atomic_add(-1, &mux->map_guard); - printk(KERN_ERR - "dz: Unable to reserve MMIO resource\n"); - return -EBUSY; - } - } - ret = dz_map_port(uport); - if (ret) { - map_guard = atomic_add_return(-1, &mux->map_guard); - if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); - return ret; - } - return 0; -} - -static void dz_config_port(struct uart_port *uport, int flags) -{ - struct dz_port *dport = to_dport(uport); - - if (flags & UART_CONFIG_TYPE) { - if (dz_request_port(uport)) - return; - - uport->type = PORT_DZ; - - dz_reset(dport); - } -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - */ -static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser) -{ - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ) - ret = -EINVAL; - if (ser->irq != uport->irq) - ret = -EINVAL; - return ret; -} - -static struct uart_ops dz_ops = { - .tx_empty = dz_tx_empty, - .get_mctrl = dz_get_mctrl, - .set_mctrl = dz_set_mctrl, - .stop_tx = dz_stop_tx, - .start_tx = dz_start_tx, - .stop_rx = dz_stop_rx, - .enable_ms = dz_enable_ms, - .break_ctl = dz_break_ctl, - .startup = dz_startup, - .shutdown = dz_shutdown, - .set_termios = dz_set_termios, - .pm = dz_pm, - .type = dz_type, - .release_port = dz_release_port, - .request_port = dz_request_port, - .config_port = dz_config_port, - .verify_port = dz_verify_port, -}; - -static void __init dz_init_ports(void) -{ - static int first = 1; - unsigned long base; - int line; - - if (!first) - return; - first = 0; - - if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) - base = dec_kn_slot_base + KN01_DZ11; - else - base = dec_kn_slot_base + KN02_DZ11; - - for (line = 0; line < DZ_NB_PORT; line++) { - struct dz_port *dport = &dz_mux.dport[line]; - struct uart_port *uport = &dport->port; - - dport->mux = &dz_mux; - - uport->irq = dec_interrupt[DEC_IRQ_DZ11]; - uport->fifosize = 1; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &dz_ops; - uport->line = line; - uport->mapbase = base; - } -} - -#ifdef CONFIG_SERIAL_DZ_CONSOLE -/* - * ------------------------------------------------------------------- - * dz_console_putchar() -- transmit a character - * - * Polled transmission. This is tricky. We need to mask transmit - * interrupts so that they do not interfere, enable the transmitter - * for the line requested and then wait till the transmit scanner - * requests data for this line. But it may request data for another - * line first, in which case we have to disable its transmitter and - * repeat waiting till our line pops up. Only then the character may - * be transmitted. Finally, the state of the transmitter mask is - * restored. Welcome to the world of PDP-11! - * ------------------------------------------------------------------- - */ -static void dz_console_putchar(struct uart_port *uport, int ch) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - unsigned short csr, tcr, trdy, mask; - int loops = 10000; - - spin_lock_irqsave(&dport->port.lock, flags); - csr = dz_in(dport, DZ_CSR); - dz_out(dport, DZ_CSR, csr & ~DZ_TIE); - tcr = dz_in(dport, DZ_TCR); - tcr |= 1 << dport->port.line; - mask = tcr; - dz_out(dport, DZ_TCR, mask); - iob(); - spin_unlock_irqrestore(&dport->port.lock, flags); - - do { - trdy = dz_in(dport, DZ_CSR); - if (!(trdy & DZ_TRDY)) - continue; - trdy = (trdy & DZ_TLINE) >> 8; - if (trdy == dport->port.line) - break; - mask &= ~(1 << trdy); - dz_out(dport, DZ_TCR, mask); - iob(); - udelay(2); - } while (--loops); - - if (loops) /* Cannot send otherwise. */ - dz_out(dport, DZ_TDR, ch); - - dz_out(dport, DZ_TCR, tcr); - dz_out(dport, DZ_CSR, csr); -} - -/* - * ------------------------------------------------------------------- - * dz_console_print () - * - * dz_console_print is registered for printk. - * The console must be locked when we get here. - * ------------------------------------------------------------------- - */ -static void dz_console_print(struct console *co, - const char *str, - unsigned int count) -{ - struct dz_port *dport = &dz_mux.dport[co->index]; -#ifdef DEBUG_DZ - prom_printf((char *) str); -#endif - uart_console_write(&dport->port, str, count, dz_console_putchar); -} - -static int __init dz_console_setup(struct console *co, char *options) -{ - struct dz_port *dport = &dz_mux.dport[co->index]; - struct uart_port *uport = &dport->port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - ret = dz_map_port(uport); - if (ret) - return ret; - - spin_lock_init(&dport->port.lock); /* For dz_pm(). */ - - dz_reset(dport); - dz_pm(uport, 0, -1); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&dport->port, co, baud, parity, bits, flow); -} - -static struct uart_driver dz_reg; -static struct console dz_console = { - .name = "ttyS", - .write = dz_console_print, - .device = uart_console_device, - .setup = dz_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &dz_reg, -}; - -static int __init dz_serial_console_init(void) -{ - if (!IOASIC) { - dz_init_ports(); - register_console(&dz_console); - return 0; - } else - return -ENXIO; -} - -console_initcall(dz_serial_console_init); - -#define SERIAL_DZ_CONSOLE &dz_console -#else -#define SERIAL_DZ_CONSOLE NULL -#endif /* CONFIG_SERIAL_DZ_CONSOLE */ - -static struct uart_driver dz_reg = { - .owner = THIS_MODULE, - .driver_name = "serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = DZ_NB_PORT, - .cons = SERIAL_DZ_CONSOLE, -}; - -static int __init dz_init(void) -{ - int ret, i; - - if (IOASIC) - return -ENXIO; - - printk("%s%s\n", dz_name, dz_version); - - dz_init_ports(); - - ret = uart_register_driver(&dz_reg); - if (ret) - return ret; - - for (i = 0; i < DZ_NB_PORT; i++) - uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); - - return 0; -} - -module_init(dz_init); diff --git a/drivers/serial/dz.h b/drivers/serial/dz.h deleted file mode 100644 index faf169ed27b3..000000000000 --- a/drivers/serial/dz.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * dz.h: Serial port driver for DECstations equipped - * with the DZ chipset. - * - * Copyright (C) 1998 Olivier A. D. Lebaillif - * - * Email: olivier.lebaillif@ifrsys.com - * - * Copyright (C) 2004, 2006 Maciej W. Rozycki - */ -#ifndef DZ_SERIAL_H -#define DZ_SERIAL_H - -/* - * Definitions for the Control and Status Register. - */ -#define DZ_TRDY 0x8000 /* Transmitter empty */ -#define DZ_TIE 0x4000 /* Transmitter Interrupt Enbl */ -#define DZ_TLINE 0x0300 /* Transmitter Line Number */ -#define DZ_RDONE 0x0080 /* Receiver data ready */ -#define DZ_RIE 0x0040 /* Receive Interrupt Enable */ -#define DZ_MSE 0x0020 /* Master Scan Enable */ -#define DZ_CLR 0x0010 /* Master reset */ -#define DZ_MAINT 0x0008 /* Loop Back Mode */ - -/* - * Definitions for the Receiver Buffer Register. - */ -#define DZ_RBUF_MASK 0x00FF /* Data Mask */ -#define DZ_LINE_MASK 0x0300 /* Line Mask */ -#define DZ_DVAL 0x8000 /* Valid Data indicator */ -#define DZ_OERR 0x4000 /* Overrun error indicator */ -#define DZ_FERR 0x2000 /* Frame error indicator */ -#define DZ_PERR 0x1000 /* Parity error indicator */ - -#define DZ_BREAK 0x0800 /* BREAK event software flag */ - -#define LINE(x) ((x & DZ_LINE_MASK) >> 8) /* Get the line number - from the input buffer */ -#define UCHAR(x) ((unsigned char)(x & DZ_RBUF_MASK)) - -/* - * Definitions for the Transmit Control Register. - */ -#define DZ_LINE_KEYBOARD 0x0001 -#define DZ_LINE_MOUSE 0x0002 -#define DZ_LINE_MODEM 0x0004 -#define DZ_LINE_PRINTER 0x0008 - -#define DZ_MODEM_RTS 0x0800 /* RTS for the modem line (2) */ -#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */ -#define DZ_PRINT_RTS 0x0200 /* RTS for the prntr line (3) */ -#define DZ_PRINT_DTR 0x0100 /* DTR for the prntr line (3) */ -#define DZ_LNENB 0x000f /* Transmitter Line Enable */ - -/* - * Definitions for the Modem Status Register. - */ -#define DZ_MODEM_RI 0x0800 /* RI for the modem line (2) */ -#define DZ_MODEM_CD 0x0400 /* CD for the modem line (2) */ -#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */ -#define DZ_MODEM_CTS 0x0100 /* CTS for the modem line (2) */ -#define DZ_PRINT_RI 0x0008 /* RI for the printer line (3) */ -#define DZ_PRINT_CD 0x0004 /* CD for the printer line (3) */ -#define DZ_PRINT_DSR 0x0002 /* DSR for the prntr line (3) */ -#define DZ_PRINT_CTS 0x0001 /* CTS for the prntr line (3) */ - -/* - * Definitions for the Transmit Data Register. - */ -#define DZ_BRK0 0x0100 /* Break assertion for line 0 */ -#define DZ_BRK1 0x0200 /* Break assertion for line 1 */ -#define DZ_BRK2 0x0400 /* Break assertion for line 2 */ -#define DZ_BRK3 0x0800 /* Break assertion for line 3 */ - -/* - * Definitions for the Line Parameter Register. - */ -#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */ -#define DZ_MOUSE 0x0001 /* line 1 = mouse */ -#define DZ_MODEM 0x0002 /* line 2 = modem */ -#define DZ_PRINTER 0x0003 /* line 3 = printer */ - -#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */ -#define DZ_CS5 0x0000 /* 5 bits per byte */ -#define DZ_CS6 0x0008 /* 6 bits per byte */ -#define DZ_CS7 0x0010 /* 7 bits per byte */ -#define DZ_CS8 0x0018 /* 8 bits per byte */ - -#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */ - -#define DZ_PARENB 0x0040 /* Parity enable */ -#define DZ_PARODD 0x0080 /* Odd parity instead of even */ - -#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */ -#define DZ_B50 0x0000 -#define DZ_B75 0x0100 -#define DZ_B110 0x0200 -#define DZ_B134 0x0300 -#define DZ_B150 0x0400 -#define DZ_B300 0x0500 -#define DZ_B600 0x0600 -#define DZ_B1200 0x0700 -#define DZ_B1800 0x0800 -#define DZ_B2000 0x0900 -#define DZ_B2400 0x0A00 -#define DZ_B3600 0x0B00 -#define DZ_B4800 0x0C00 -#define DZ_B7200 0x0D00 -#define DZ_B9600 0x0E00 - -#define DZ_RXENAB 0x1000 /* Receiver Enable */ - -/* - * Addresses for the DZ registers - */ -#define DZ_CSR 0x00 /* Control and Status Register */ -#define DZ_RBUF 0x08 /* Receive Buffer */ -#define DZ_LPR 0x08 /* Line Parameters Register */ -#define DZ_TCR 0x10 /* Transmitter Control Register */ -#define DZ_MSR 0x18 /* Modem Status Register */ -#define DZ_TDR 0x18 /* Transmit Data Register */ - -#define DZ_NB_PORT 4 - -#define DZ_XMIT_SIZE 4096 /* buffer size */ -#define DZ_WAKEUP_CHARS DZ_XMIT_SIZE/4 - -#endif /* DZ_SERIAL_H */ diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c deleted file mode 100644 index 53a468227056..000000000000 --- a/drivers/serial/icom.c +++ /dev/null @@ -1,1658 +0,0 @@ -/* - * icom.c - * - * Copyright (C) 2001 IBM Corporation. All rights reserved. - * - * Serial device driver. - * - * Based on code from serial.c - * - * 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 - * - */ -#define SERIAL_DO_RESTART -#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 -#include -#include -#include - -#include -#include -#include -#include - -#include "icom.h" - -/*#define ICOM_TRACE enable port trace capabilities */ - -#define ICOM_DRIVER_NAME "icom" -#define ICOM_VERSION_STR "1.3.1" -#define NR_PORTS 128 -#define ICOM_PORT ((struct icom_port *)port) -#define to_icom_adapter(d) container_of(d, struct icom_adapter, kref) - -static const struct pci_device_id icom_pci_table[] = { - { - .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = ADAPTER_V1, - }, - { - .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, - .subvendor = PCI_VENDOR_ID_IBM, - .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX, - .driver_data = ADAPTER_V2, - }, - { - .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, - .subvendor = PCI_VENDOR_ID_IBM, - .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM, - .driver_data = ADAPTER_V2, - }, - { - .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, - .subvendor = PCI_VENDOR_ID_IBM, - .subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL, - .driver_data = ADAPTER_V2, - }, - { - .vendor = PCI_VENDOR_ID_IBM, - .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, - .subvendor = PCI_VENDOR_ID_IBM, - .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE, - .driver_data = ADAPTER_V2, - }, - {} -}; - -struct lookup_proc_table start_proc[4] = { - {NULL, ICOM_CONTROL_START_A}, - {NULL, ICOM_CONTROL_START_B}, - {NULL, ICOM_CONTROL_START_C}, - {NULL, ICOM_CONTROL_START_D} -}; - - -struct lookup_proc_table stop_proc[4] = { - {NULL, ICOM_CONTROL_STOP_A}, - {NULL, ICOM_CONTROL_STOP_B}, - {NULL, ICOM_CONTROL_STOP_C}, - {NULL, ICOM_CONTROL_STOP_D} -}; - -struct lookup_int_table int_mask_tbl[4] = { - {NULL, ICOM_INT_MASK_PRC_A}, - {NULL, ICOM_INT_MASK_PRC_B}, - {NULL, ICOM_INT_MASK_PRC_C}, - {NULL, ICOM_INT_MASK_PRC_D}, -}; - - -MODULE_DEVICE_TABLE(pci, icom_pci_table); - -static LIST_HEAD(icom_adapter_head); - -/* spinlock for adapter initialization and changing adapter operations */ -static spinlock_t icom_lock; - -#ifdef ICOM_TRACE -static inline void trace(struct icom_port *icom_port, char *trace_pt, - unsigned long trace_data) -{ - dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n", - icom_port->port, trace_pt, trace_data); -} -#else -static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {}; -#endif -static void icom_kref_release(struct kref *kref); - -static void free_port_memory(struct icom_port *icom_port) -{ - struct pci_dev *dev = icom_port->adapter->pci_dev; - - trace(icom_port, "RET_PORT_MEM", 0); - if (icom_port->recv_buf) { - pci_free_consistent(dev, 4096, icom_port->recv_buf, - icom_port->recv_buf_pci); - icom_port->recv_buf = NULL; - } - if (icom_port->xmit_buf) { - pci_free_consistent(dev, 4096, icom_port->xmit_buf, - icom_port->xmit_buf_pci); - icom_port->xmit_buf = NULL; - } - if (icom_port->statStg) { - pci_free_consistent(dev, 4096, icom_port->statStg, - icom_port->statStg_pci); - icom_port->statStg = NULL; - } - - if (icom_port->xmitRestart) { - pci_free_consistent(dev, 4096, icom_port->xmitRestart, - icom_port->xmitRestart_pci); - icom_port->xmitRestart = NULL; - } -} - -static int __devinit get_port_memory(struct icom_port *icom_port) -{ - int index; - unsigned long stgAddr; - unsigned long startStgAddr; - unsigned long offset; - struct pci_dev *dev = icom_port->adapter->pci_dev; - - icom_port->xmit_buf = - pci_alloc_consistent(dev, 4096, &icom_port->xmit_buf_pci); - if (!icom_port->xmit_buf) { - dev_err(&dev->dev, "Can not allocate Transmit buffer\n"); - return -ENOMEM; - } - - trace(icom_port, "GET_PORT_MEM", - (unsigned long) icom_port->xmit_buf); - - icom_port->recv_buf = - pci_alloc_consistent(dev, 4096, &icom_port->recv_buf_pci); - if (!icom_port->recv_buf) { - dev_err(&dev->dev, "Can not allocate Receive buffer\n"); - free_port_memory(icom_port); - return -ENOMEM; - } - trace(icom_port, "GET_PORT_MEM", - (unsigned long) icom_port->recv_buf); - - icom_port->statStg = - pci_alloc_consistent(dev, 4096, &icom_port->statStg_pci); - if (!icom_port->statStg) { - dev_err(&dev->dev, "Can not allocate Status buffer\n"); - free_port_memory(icom_port); - return -ENOMEM; - } - trace(icom_port, "GET_PORT_MEM", - (unsigned long) icom_port->statStg); - - icom_port->xmitRestart = - pci_alloc_consistent(dev, 4096, &icom_port->xmitRestart_pci); - if (!icom_port->xmitRestart) { - dev_err(&dev->dev, - "Can not allocate xmit Restart buffer\n"); - free_port_memory(icom_port); - return -ENOMEM; - } - - memset(icom_port->statStg, 0, 4096); - - /* FODs: Frame Out Descriptor Queue, this is a FIFO queue that - indicates that frames are to be transmitted - */ - - stgAddr = (unsigned long) icom_port->statStg; - for (index = 0; index < NUM_XBUFFS; index++) { - trace(icom_port, "FOD_ADDR", stgAddr); - stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]); - if (index < (NUM_XBUFFS - 1)) { - memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); - icom_port->statStg->xmit[index].leLengthASD = - (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ); - trace(icom_port, "FOD_ADDR", stgAddr); - trace(icom_port, "FOD_XBUFF", - (unsigned long) icom_port->xmit_buf); - icom_port->statStg->xmit[index].leBuffer = - cpu_to_le32(icom_port->xmit_buf_pci); - } else if (index == (NUM_XBUFFS - 1)) { - memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); - icom_port->statStg->xmit[index].leLengthASD = - (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ); - trace(icom_port, "FOD_XBUFF", - (unsigned long) icom_port->xmit_buf); - icom_port->statStg->xmit[index].leBuffer = - cpu_to_le32(icom_port->xmit_buf_pci); - } else { - memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); - } - } - /* FIDs */ - startStgAddr = stgAddr; - - /* fill in every entry, even if no buffer */ - for (index = 0; index < NUM_RBUFFS; index++) { - trace(icom_port, "FID_ADDR", stgAddr); - stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]); - icom_port->statStg->rcv[index].leLength = 0; - icom_port->statStg->rcv[index].WorkingLength = - (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); - if (index < (NUM_RBUFFS - 1) ) { - offset = stgAddr - (unsigned long) icom_port->statStg; - icom_port->statStg->rcv[index].leNext = - cpu_to_le32(icom_port-> statStg_pci + offset); - trace(icom_port, "FID_RBUFF", - (unsigned long) icom_port->recv_buf); - icom_port->statStg->rcv[index].leBuffer = - cpu_to_le32(icom_port->recv_buf_pci); - } else if (index == (NUM_RBUFFS -1) ) { - offset = startStgAddr - (unsigned long) icom_port->statStg; - icom_port->statStg->rcv[index].leNext = - cpu_to_le32(icom_port-> statStg_pci + offset); - trace(icom_port, "FID_RBUFF", - (unsigned long) icom_port->recv_buf + 2048); - icom_port->statStg->rcv[index].leBuffer = - cpu_to_le32(icom_port->recv_buf_pci + 2048); - } else { - icom_port->statStg->rcv[index].leNext = 0; - icom_port->statStg->rcv[index].leBuffer = 0; - } - } - - return 0; -} - -static void stop_processor(struct icom_port *icom_port) -{ - unsigned long temp; - unsigned long flags; - int port; - - spin_lock_irqsave(&icom_lock, flags); - - port = icom_port->port; - if (port == 0 || port == 1) - stop_proc[port].global_control_reg = &icom_port->global_reg->control; - else - stop_proc[port].global_control_reg = &icom_port->global_reg->control_2; - - - if (port < 4) { - temp = readl(stop_proc[port].global_control_reg); - temp = - (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; - writel(temp, stop_proc[port].global_control_reg); - - /* write flush */ - readl(stop_proc[port].global_control_reg); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } - - spin_unlock_irqrestore(&icom_lock, flags); -} - -static void start_processor(struct icom_port *icom_port) -{ - unsigned long temp; - unsigned long flags; - int port; - - spin_lock_irqsave(&icom_lock, flags); - - port = icom_port->port; - if (port == 0 || port == 1) - start_proc[port].global_control_reg = &icom_port->global_reg->control; - else - start_proc[port].global_control_reg = &icom_port->global_reg->control_2; - if (port < 4) { - temp = readl(start_proc[port].global_control_reg); - temp = - (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; - writel(temp, start_proc[port].global_control_reg); - - /* write flush */ - readl(start_proc[port].global_control_reg); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } - - spin_unlock_irqrestore(&icom_lock, flags); -} - -static void load_code(struct icom_port *icom_port) -{ - const struct firmware *fw; - char __iomem *iram_ptr; - int index; - int status = 0; - void __iomem *dram_ptr = icom_port->dram; - dma_addr_t temp_pci; - unsigned char *new_page = NULL; - unsigned char cable_id = NO_CABLE; - struct pci_dev *dev = icom_port->adapter->pci_dev; - - /* Clear out any pending interrupts */ - writew(0x3FFF, icom_port->int_reg); - - trace(icom_port, "CLEAR_INTERRUPTS", 0); - - /* Stop processor */ - stop_processor(icom_port); - - /* Zero out DRAM */ - memset_io(dram_ptr, 0, 512); - - /* Load Call Setup into Adapter */ - if (request_firmware(&fw, "icom_call_setup.bin", &dev->dev) < 0) { - dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n"); - status = -1; - goto load_code_exit; - } - - if (fw->size > ICOM_DCE_IRAM_OFFSET) { - dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n"); - release_firmware(fw); - status = -1; - goto load_code_exit; - } - - iram_ptr = (char __iomem *)icom_port->dram + ICOM_IRAM_OFFSET; - for (index = 0; index < fw->size; index++) - writeb(fw->data[index], &iram_ptr[index]); - - release_firmware(fw); - - /* Load Resident DCE portion of Adapter */ - if (request_firmware(&fw, "icom_res_dce.bin", &dev->dev) < 0) { - dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n"); - status = -1; - goto load_code_exit; - } - - if (fw->size > ICOM_IRAM_SIZE) { - dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n"); - release_firmware(fw); - status = -1; - goto load_code_exit; - } - - iram_ptr = (char __iomem *) icom_port->dram + ICOM_IRAM_OFFSET; - for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++) - writeb(fw->data[index], &iram_ptr[index]); - - release_firmware(fw); - - /* Set Hardware level */ - if (icom_port->adapter->version == ADAPTER_V2) - writeb(V2_HARDWARE, &(icom_port->dram->misc_flags)); - - /* Start the processor in Adapter */ - start_processor(icom_port); - - writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL), - &(icom_port->dram->HDLCConfigReg)); - writeb(0x04, &(icom_port->dram->FlagFillIdleTimer)); /* 0.5 seconds */ - writeb(0x00, &(icom_port->dram->CmdReg)); - writeb(0x10, &(icom_port->dram->async_config3)); - writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC | - ICOM_ACFG_1STOP_BIT), &(icom_port->dram->async_config2)); - - /*Set up data in icom DRAM to indicate where personality - *code is located and its length. - */ - new_page = pci_alloc_consistent(dev, 4096, &temp_pci); - - if (!new_page) { - dev_err(&dev->dev, "Can not allocate DMA buffer\n"); - status = -1; - goto load_code_exit; - } - - if (request_firmware(&fw, "icom_asc.bin", &dev->dev) < 0) { - dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n"); - status = -1; - goto load_code_exit; - } - - if (fw->size > ICOM_DCE_IRAM_OFFSET) { - dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n"); - release_firmware(fw); - status = -1; - goto load_code_exit; - } - - for (index = 0; index < fw->size; index++) - new_page[index] = fw->data[index]; - - release_firmware(fw); - - writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length); - writel(temp_pci, &icom_port->dram->mac_load_addr); - - /*Setting the syncReg to 0x80 causes adapter to start downloading - the personality code into adapter instruction RAM. - Once code is loaded, it will begin executing and, based on - information provided above, will start DMAing data from - shared memory to adapter DRAM. - */ - /* the wait loop below verifies this write operation has been done - and processed - */ - writeb(START_DOWNLOAD, &icom_port->dram->sync); - - /* Wait max 1 Sec for data download and processor to start */ - for (index = 0; index < 10; index++) { - msleep(100); - if (readb(&icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE) - break; - } - - if (index == 10) - status = -1; - - /* - * check Cable ID - */ - cable_id = readb(&icom_port->dram->cable_id); - - if (cable_id & ICOM_CABLE_ID_VALID) { - /* Get cable ID into the lower 4 bits (standard form) */ - cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4; - icom_port->cable_id = cable_id; - } else { - dev_err(&dev->dev,"Invalid or no cable attached\n"); - icom_port->cable_id = NO_CABLE; - } - - load_code_exit: - - if (status != 0) { - /* Clear out any pending interrupts */ - writew(0x3FFF, icom_port->int_reg); - - /* Turn off port */ - writeb(ICOM_DISABLE, &(icom_port->dram->disable)); - - /* Stop processor */ - stop_processor(icom_port); - - dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n"); - } - - if (new_page != NULL) - pci_free_consistent(dev, 4096, new_page, temp_pci); -} - -static int startup(struct icom_port *icom_port) -{ - unsigned long temp; - unsigned char cable_id, raw_cable_id; - unsigned long flags; - int port; - - trace(icom_port, "STARTUP", 0); - - if (!icom_port->dram) { - /* should NEVER be NULL */ - dev_err(&icom_port->adapter->pci_dev->dev, - "Unusable Port, port configuration missing\n"); - return -ENODEV; - } - - /* - * check Cable ID - */ - raw_cable_id = readb(&icom_port->dram->cable_id); - trace(icom_port, "CABLE_ID", raw_cable_id); - - /* Get cable ID into the lower 4 bits (standard form) */ - cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; - - /* Check for valid Cable ID */ - if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || - (cable_id != icom_port->cable_id)) { - - /* reload adapter code, pick up any potential changes in cable id */ - load_code(icom_port); - - /* still no sign of cable, error out */ - raw_cable_id = readb(&icom_port->dram->cable_id); - cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; - if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || - (icom_port->cable_id == NO_CABLE)) - return -EIO; - } - - /* - * Finally, clear and enable interrupts - */ - spin_lock_irqsave(&icom_lock, flags); - port = icom_port->port; - if (port == 0 || port == 1) - int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; - else - int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; - - if (port == 0 || port == 2) - writew(0x00FF, icom_port->int_reg); - else - writew(0x3F00, icom_port->int_reg); - if (port < 4) { - temp = readl(int_mask_tbl[port].global_int_mask); - writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); - - /* write flush */ - readl(int_mask_tbl[port].global_int_mask); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } - - spin_unlock_irqrestore(&icom_lock, flags); - return 0; -} - -static void shutdown(struct icom_port *icom_port) -{ - unsigned long temp; - unsigned char cmdReg; - unsigned long flags; - int port; - - spin_lock_irqsave(&icom_lock, flags); - trace(icom_port, "SHUTDOWN", 0); - - /* - * disable all interrupts - */ - port = icom_port->port; - if (port == 0 || port == 1) - int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; - else - int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; - - if (port < 4) { - temp = readl(int_mask_tbl[port].global_int_mask); - writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); - - /* write flush */ - readl(int_mask_tbl[port].global_int_mask); - } else { - dev_err(&icom_port->adapter->pci_dev->dev, - "Invalid port assignment\n"); - } - spin_unlock_irqrestore(&icom_lock, flags); - - /* - * disable break condition - */ - cmdReg = readb(&icom_port->dram->CmdReg); - if (cmdReg & CMD_SND_BREAK) { - writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg); - } -} - -static int icom_write(struct uart_port *port) -{ - unsigned long data_count; - unsigned char cmdReg; - unsigned long offset; - int temp_tail = port->state->xmit.tail; - - trace(ICOM_PORT, "WRITE", 0); - - if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) & - SA_FLAGS_READY_TO_XMIT) { - trace(ICOM_PORT, "WRITE_FULL", 0); - return 0; - } - - data_count = 0; - while ((port->state->xmit.head != temp_tail) && - (data_count <= XMIT_BUFF_SZ)) { - - ICOM_PORT->xmit_buf[data_count++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - } - - if (data_count) { - ICOM_PORT->statStg->xmit[0].flags = - cpu_to_le16(SA_FLAGS_READY_TO_XMIT); - ICOM_PORT->statStg->xmit[0].leLength = - cpu_to_le16(data_count); - offset = - (unsigned long) &ICOM_PORT->statStg->xmit[0] - - (unsigned long) ICOM_PORT->statStg; - *ICOM_PORT->xmitRestart = - cpu_to_le32(ICOM_PORT->statStg_pci + offset); - cmdReg = readb(&ICOM_PORT->dram->CmdReg); - writeb(cmdReg | CMD_XMIT_RCV_ENABLE, - &ICOM_PORT->dram->CmdReg); - writeb(START_XMIT, &ICOM_PORT->dram->StartXmitCmd); - trace(ICOM_PORT, "WRITE_START", data_count); - /* write flush */ - readb(&ICOM_PORT->dram->StartXmitCmd); - } - - return data_count; -} - -static inline void check_modem_status(struct icom_port *icom_port) -{ - static char old_status = 0; - char delta_status; - unsigned char status; - - spin_lock(&icom_port->uart_port.lock); - - /*modem input register */ - status = readb(&icom_port->dram->isr); - trace(icom_port, "CHECK_MODEM", status); - delta_status = status ^ old_status; - if (delta_status) { - if (delta_status & ICOM_RI) - icom_port->uart_port.icount.rng++; - if (delta_status & ICOM_DSR) - icom_port->uart_port.icount.dsr++; - if (delta_status & ICOM_DCD) - uart_handle_dcd_change(&icom_port->uart_port, - delta_status & ICOM_DCD); - if (delta_status & ICOM_CTS) - uart_handle_cts_change(&icom_port->uart_port, - delta_status & ICOM_CTS); - - wake_up_interruptible(&icom_port->uart_port.state-> - port.delta_msr_wait); - old_status = status; - } - spin_unlock(&icom_port->uart_port.lock); -} - -static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) -{ - unsigned short int count; - int i; - - if (port_int_reg & (INT_XMIT_COMPLETED)) { - trace(icom_port, "XMIT_COMPLETE", 0); - - /* clear buffer in use bit */ - icom_port->statStg->xmit[0].flags &= - cpu_to_le16(~SA_FLAGS_READY_TO_XMIT); - - count = (unsigned short int) - cpu_to_le16(icom_port->statStg->xmit[0].leLength); - icom_port->uart_port.icount.tx += count; - - for (i=0; iuart_port.state->xmit); i++) { - - icom_port->uart_port.state->xmit.tail++; - icom_port->uart_port.state->xmit.tail &= - (UART_XMIT_SIZE - 1); - } - - if (!icom_write(&icom_port->uart_port)) - /* activate write queue */ - uart_write_wakeup(&icom_port->uart_port); - } else - trace(icom_port, "XMIT_DISABLED", 0); -} - -static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) -{ - short int count, rcv_buff; - struct tty_struct *tty = icom_port->uart_port.state->port.tty; - unsigned short int status; - struct uart_icount *icount; - unsigned long offset; - unsigned char flag; - - trace(icom_port, "RCV_COMPLETE", 0); - rcv_buff = icom_port->next_rcv; - - status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); - while (status & SA_FL_RCV_DONE) { - int first = -1; - - trace(icom_port, "FID_STATUS", status); - count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength); - - trace(icom_port, "RCV_COUNT", count); - - trace(icom_port, "REAL_COUNT", count); - - offset = - cpu_to_le32(icom_port->statStg->rcv[rcv_buff].leBuffer) - - icom_port->recv_buf_pci; - - /* Block copy all but the last byte as this may have status */ - if (count > 0) { - first = icom_port->recv_buf[offset]; - tty_insert_flip_string(tty, icom_port->recv_buf + offset, count - 1); - } - - icount = &icom_port->uart_port.icount; - icount->rx += count; - - /* Break detect logic */ - if ((status & SA_FLAGS_FRAME_ERROR) - && first == 0) { - status &= ~SA_FLAGS_FRAME_ERROR; - status |= SA_FLAGS_BREAK_DET; - trace(icom_port, "BREAK_DET", 0); - } - - flag = TTY_NORMAL; - - if (status & - (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR | - SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) { - - if (status & SA_FLAGS_BREAK_DET) - icount->brk++; - if (status & SA_FLAGS_PARITY_ERROR) - icount->parity++; - if (status & SA_FLAGS_FRAME_ERROR) - icount->frame++; - if (status & SA_FLAGS_OVERRUN) - icount->overrun++; - - /* - * Now check to see if character should be - * ignored, and mask off conditions which - * should be ignored. - */ - if (status & icom_port->ignore_status_mask) { - trace(icom_port, "IGNORE_CHAR", 0); - goto ignore_char; - } - - status &= icom_port->read_status_mask; - - if (status & SA_FLAGS_BREAK_DET) { - flag = TTY_BREAK; - } else if (status & SA_FLAGS_PARITY_ERROR) { - trace(icom_port, "PARITY_ERROR", 0); - flag = TTY_PARITY; - } else if (status & SA_FLAGS_FRAME_ERROR) - flag = TTY_FRAME; - - } - - tty_insert_flip_char(tty, *(icom_port->recv_buf + offset + count - 1), flag); - - if (status & SA_FLAGS_OVERRUN) - /* - * Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); -ignore_char: - icom_port->statStg->rcv[rcv_buff].flags = 0; - icom_port->statStg->rcv[rcv_buff].leLength = 0; - icom_port->statStg->rcv[rcv_buff].WorkingLength = - (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); - - rcv_buff++; - if (rcv_buff == NUM_RBUFFS) - rcv_buff = 0; - - status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); - } - icom_port->next_rcv = rcv_buff; - tty_flip_buffer_push(tty); -} - -static void process_interrupt(u16 port_int_reg, - struct icom_port *icom_port) -{ - - spin_lock(&icom_port->uart_port.lock); - trace(icom_port, "INTERRUPT", port_int_reg); - - if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) - xmit_interrupt(port_int_reg, icom_port); - - if (port_int_reg & INT_RCV_COMPLETED) - recv_interrupt(port_int_reg, icom_port); - - spin_unlock(&icom_port->uart_port.lock); -} - -static irqreturn_t icom_interrupt(int irq, void *dev_id) -{ - void __iomem * int_reg; - u32 adapter_interrupts; - u16 port_int_reg; - struct icom_adapter *icom_adapter; - struct icom_port *icom_port; - - /* find icom_port for this interrupt */ - icom_adapter = (struct icom_adapter *) dev_id; - - if (icom_adapter->version == ADAPTER_V2) { - int_reg = icom_adapter->base_addr + 0x8024; - - adapter_interrupts = readl(int_reg); - - if (adapter_interrupts & 0x00003FFF) { - /* port 2 interrupt, NOTE: for all ADAPTER_V2, port 2 will be active */ - icom_port = &icom_adapter->port_info[2]; - port_int_reg = (u16) adapter_interrupts; - process_interrupt(port_int_reg, icom_port); - check_modem_status(icom_port); - } - if (adapter_interrupts & 0x3FFF0000) { - /* port 3 interrupt */ - icom_port = &icom_adapter->port_info[3]; - if (icom_port->status == ICOM_PORT_ACTIVE) { - port_int_reg = - (u16) (adapter_interrupts >> 16); - process_interrupt(port_int_reg, icom_port); - check_modem_status(icom_port); - } - } - - /* Clear out any pending interrupts */ - writel(adapter_interrupts, int_reg); - - int_reg = icom_adapter->base_addr + 0x8004; - } else { - int_reg = icom_adapter->base_addr + 0x4004; - } - - adapter_interrupts = readl(int_reg); - - if (adapter_interrupts & 0x00003FFF) { - /* port 0 interrupt, NOTE: for all adapters, port 0 will be active */ - icom_port = &icom_adapter->port_info[0]; - port_int_reg = (u16) adapter_interrupts; - process_interrupt(port_int_reg, icom_port); - check_modem_status(icom_port); - } - if (adapter_interrupts & 0x3FFF0000) { - /* port 1 interrupt */ - icom_port = &icom_adapter->port_info[1]; - if (icom_port->status == ICOM_PORT_ACTIVE) { - port_int_reg = (u16) (adapter_interrupts >> 16); - process_interrupt(port_int_reg, icom_port); - check_modem_status(icom_port); - } - } - - /* Clear out any pending interrupts */ - writel(adapter_interrupts, int_reg); - - /* flush the write */ - adapter_interrupts = readl(int_reg); - - return IRQ_HANDLED; -} - -/* - * ------------------------------------------------------------------ - * Begin serial-core API - * ------------------------------------------------------------------ - */ -static unsigned int icom_tx_empty(struct uart_port *port) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) & - SA_FLAGS_READY_TO_XMIT) - ret = TIOCSER_TEMT; - else - ret = 0; - - spin_unlock_irqrestore(&port->lock, flags); - return ret; -} - -static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - unsigned char local_osr; - - trace(ICOM_PORT, "SET_MODEM", 0); - local_osr = readb(&ICOM_PORT->dram->osr); - - if (mctrl & TIOCM_RTS) { - trace(ICOM_PORT, "RAISE_RTS", 0); - local_osr |= ICOM_RTS; - } else { - trace(ICOM_PORT, "LOWER_RTS", 0); - local_osr &= ~ICOM_RTS; - } - - if (mctrl & TIOCM_DTR) { - trace(ICOM_PORT, "RAISE_DTR", 0); - local_osr |= ICOM_DTR; - } else { - trace(ICOM_PORT, "LOWER_DTR", 0); - local_osr &= ~ICOM_DTR; - } - - writeb(local_osr, &ICOM_PORT->dram->osr); -} - -static unsigned int icom_get_mctrl(struct uart_port *port) -{ - unsigned char status; - unsigned int result; - - trace(ICOM_PORT, "GET_MODEM", 0); - - status = readb(&ICOM_PORT->dram->isr); - - result = ((status & ICOM_DCD) ? TIOCM_CAR : 0) - | ((status & ICOM_RI) ? TIOCM_RNG : 0) - | ((status & ICOM_DSR) ? TIOCM_DSR : 0) - | ((status & ICOM_CTS) ? TIOCM_CTS : 0); - return result; -} - -static void icom_stop_tx(struct uart_port *port) -{ - unsigned char cmdReg; - - trace(ICOM_PORT, "STOP", 0); - cmdReg = readb(&ICOM_PORT->dram->CmdReg); - writeb(cmdReg | CMD_HOLD_XMIT, &ICOM_PORT->dram->CmdReg); -} - -static void icom_start_tx(struct uart_port *port) -{ - unsigned char cmdReg; - - trace(ICOM_PORT, "START", 0); - cmdReg = readb(&ICOM_PORT->dram->CmdReg); - if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT) - writeb(cmdReg & ~CMD_HOLD_XMIT, - &ICOM_PORT->dram->CmdReg); - - icom_write(port); -} - -static void icom_send_xchar(struct uart_port *port, char ch) -{ - unsigned char xdata; - int index; - unsigned long flags; - - trace(ICOM_PORT, "SEND_XCHAR", ch); - - /* wait .1 sec to send char */ - for (index = 0; index < 10; index++) { - spin_lock_irqsave(&port->lock, flags); - xdata = readb(&ICOM_PORT->dram->xchar); - if (xdata == 0x00) { - trace(ICOM_PORT, "QUICK_WRITE", 0); - writeb(ch, &ICOM_PORT->dram->xchar); - - /* flush write operation */ - xdata = readb(&ICOM_PORT->dram->xchar); - spin_unlock_irqrestore(&port->lock, flags); - break; - } - spin_unlock_irqrestore(&port->lock, flags); - msleep(10); - } -} - -static void icom_stop_rx(struct uart_port *port) -{ - unsigned char cmdReg; - - cmdReg = readb(&ICOM_PORT->dram->CmdReg); - writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); -} - -static void icom_enable_ms(struct uart_port *port) -{ - /* no-op */ -} - -static void icom_break(struct uart_port *port, int break_state) -{ - unsigned char cmdReg; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - trace(ICOM_PORT, "BREAK", 0); - cmdReg = readb(&ICOM_PORT->dram->CmdReg); - if (break_state == -1) { - writeb(cmdReg | CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg); - } else { - writeb(cmdReg & ~CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg); - } - spin_unlock_irqrestore(&port->lock, flags); -} - -static int icom_open(struct uart_port *port) -{ - int retval; - - kref_get(&ICOM_PORT->adapter->kref); - retval = startup(ICOM_PORT); - - if (retval) { - kref_put(&ICOM_PORT->adapter->kref, icom_kref_release); - trace(ICOM_PORT, "STARTUP_ERROR", 0); - return retval; - } - - return 0; -} - -static void icom_close(struct uart_port *port) -{ - unsigned char cmdReg; - - trace(ICOM_PORT, "CLOSE", 0); - - /* stop receiver */ - cmdReg = readb(&ICOM_PORT->dram->CmdReg); - writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE, - &ICOM_PORT->dram->CmdReg); - - shutdown(ICOM_PORT); - - kref_put(&ICOM_PORT->adapter->kref, icom_kref_release); -} - -static void icom_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old_termios) -{ - int baud; - unsigned cflag, iflag; - char new_config2; - char new_config3 = 0; - char tmp_byte; - int index; - int rcv_buff, xmit_buff; - unsigned long offset; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - trace(ICOM_PORT, "CHANGE_SPEED", 0); - - cflag = termios->c_cflag; - iflag = termios->c_iflag; - - new_config2 = ICOM_ACFG_DRIVE1; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: /* 5 bits/char */ - new_config2 |= ICOM_ACFG_5BPC; - break; - case CS6: /* 6 bits/char */ - new_config2 |= ICOM_ACFG_6BPC; - break; - case CS7: /* 7 bits/char */ - new_config2 |= ICOM_ACFG_7BPC; - break; - case CS8: /* 8 bits/char */ - new_config2 |= ICOM_ACFG_8BPC; - break; - default: - break; - } - if (cflag & CSTOPB) { - /* 2 stop bits */ - new_config2 |= ICOM_ACFG_2STOP_BIT; - } - if (cflag & PARENB) { - /* parity bit enabled */ - new_config2 |= ICOM_ACFG_PARITY_ENAB; - trace(ICOM_PORT, "PARENB", 0); - } - if (cflag & PARODD) { - /* odd parity */ - new_config2 |= ICOM_ACFG_PARITY_ODD; - trace(ICOM_PORT, "PARODD", 0); - } - - /* Determine divisor based on baud rate */ - baud = uart_get_baud_rate(port, termios, old_termios, - icom_acfg_baud[0], - icom_acfg_baud[BAUD_TABLE_LIMIT]); - if (!baud) - baud = 9600; /* B0 transition handled in rs_set_termios */ - - for (index = 0; index < BAUD_TABLE_LIMIT; index++) { - if (icom_acfg_baud[index] == baud) { - new_config3 = index; - break; - } - } - - uart_update_timeout(port, cflag, baud); - - /* CTS flow control flag and modem status interrupts */ - tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg)); - if (cflag & CRTSCTS) - tmp_byte |= HDLC_HDW_FLOW; - else - tmp_byte &= ~HDLC_HDW_FLOW; - writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg)); - - /* - * Set up parity check flag - */ - ICOM_PORT->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE; - if (iflag & INPCK) - ICOM_PORT->read_status_mask |= - SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR; - - if ((iflag & BRKINT) || (iflag & PARMRK)) - ICOM_PORT->read_status_mask |= SA_FLAGS_BREAK_DET; - - /* - * Characters to ignore - */ - ICOM_PORT->ignore_status_mask = 0; - if (iflag & IGNPAR) - ICOM_PORT->ignore_status_mask |= - SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR; - if (iflag & IGNBRK) { - ICOM_PORT->ignore_status_mask |= SA_FLAGS_BREAK_DET; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (iflag & IGNPAR) - ICOM_PORT->ignore_status_mask |= SA_FLAGS_OVERRUN; - } - - /* - * !!! ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - ICOM_PORT->ignore_status_mask |= SA_FL_RCV_DONE; - - /* Turn off Receiver to prepare for reset */ - writeb(CMD_RCV_DISABLE, &ICOM_PORT->dram->CmdReg); - - for (index = 0; index < 10; index++) { - if (readb(&ICOM_PORT->dram->PrevCmdReg) == 0x00) { - break; - } - } - - /* clear all current buffers of data */ - for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) { - ICOM_PORT->statStg->rcv[rcv_buff].flags = 0; - ICOM_PORT->statStg->rcv[rcv_buff].leLength = 0; - ICOM_PORT->statStg->rcv[rcv_buff].WorkingLength = - (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); - } - - for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) { - ICOM_PORT->statStg->xmit[xmit_buff].flags = 0; - } - - /* activate changes and start xmit and receiver here */ - /* Enable the receiver */ - writeb(new_config3, &(ICOM_PORT->dram->async_config3)); - writeb(new_config2, &(ICOM_PORT->dram->async_config2)); - tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg)); - tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL; - writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg)); - writeb(0x04, &(ICOM_PORT->dram->FlagFillIdleTimer)); /* 0.5 seconds */ - writeb(0xFF, &(ICOM_PORT->dram->ier)); /* enable modem signal interrupts */ - - /* reset processor */ - writeb(CMD_RESTART, &ICOM_PORT->dram->CmdReg); - - for (index = 0; index < 10; index++) { - if (readb(&ICOM_PORT->dram->CmdReg) == 0x00) { - break; - } - } - - /* Enable Transmitter and Reciever */ - offset = - (unsigned long) &ICOM_PORT->statStg->rcv[0] - - (unsigned long) ICOM_PORT->statStg; - writel(ICOM_PORT->statStg_pci + offset, - &ICOM_PORT->dram->RcvStatusAddr); - ICOM_PORT->next_rcv = 0; - ICOM_PORT->put_length = 0; - *ICOM_PORT->xmitRestart = 0; - writel(ICOM_PORT->xmitRestart_pci, - &ICOM_PORT->dram->XmitStatusAddr); - trace(ICOM_PORT, "XR_ENAB", 0); - writeb(CMD_XMIT_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *icom_type(struct uart_port *port) -{ - return "icom"; -} - -static void icom_release_port(struct uart_port *port) -{ -} - -static int icom_request_port(struct uart_port *port) -{ - return 0; -} - -static void icom_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_ICOM; -} - -static struct uart_ops icom_ops = { - .tx_empty = icom_tx_empty, - .set_mctrl = icom_set_mctrl, - .get_mctrl = icom_get_mctrl, - .stop_tx = icom_stop_tx, - .start_tx = icom_start_tx, - .send_xchar = icom_send_xchar, - .stop_rx = icom_stop_rx, - .enable_ms = icom_enable_ms, - .break_ctl = icom_break, - .startup = icom_open, - .shutdown = icom_close, - .set_termios = icom_set_termios, - .type = icom_type, - .release_port = icom_release_port, - .request_port = icom_request_port, - .config_port = icom_config_port, -}; - -#define ICOM_CONSOLE NULL - -static struct uart_driver icom_uart_driver = { - .owner = THIS_MODULE, - .driver_name = ICOM_DRIVER_NAME, - .dev_name = "ttyA", - .major = ICOM_MAJOR, - .minor = ICOM_MINOR_START, - .nr = NR_PORTS, - .cons = ICOM_CONSOLE, -}; - -static int __devinit icom_init_ports(struct icom_adapter *icom_adapter) -{ - u32 subsystem_id = icom_adapter->subsystem_id; - int i; - struct icom_port *icom_port; - - if (icom_adapter->version == ADAPTER_V1) { - icom_adapter->numb_ports = 2; - - for (i = 0; i < 2; i++) { - icom_port = &icom_adapter->port_info[i]; - icom_port->port = i; - icom_port->status = ICOM_PORT_ACTIVE; - icom_port->imbed_modem = ICOM_UNKNOWN; - } - } else { - if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) { - icom_adapter->numb_ports = 4; - - for (i = 0; i < 4; i++) { - icom_port = &icom_adapter->port_info[i]; - - icom_port->port = i; - icom_port->status = ICOM_PORT_ACTIVE; - icom_port->imbed_modem = ICOM_IMBED_MODEM; - } - } else { - icom_adapter->numb_ports = 4; - - icom_adapter->port_info[0].port = 0; - icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE; - - if (subsystem_id == - PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM) { - icom_adapter->port_info[0].imbed_modem = ICOM_IMBED_MODEM; - } else { - icom_adapter->port_info[0].imbed_modem = ICOM_RVX; - } - - icom_adapter->port_info[1].status = ICOM_PORT_OFF; - - icom_adapter->port_info[2].port = 2; - icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE; - icom_adapter->port_info[2].imbed_modem = ICOM_RVX; - icom_adapter->port_info[3].status = ICOM_PORT_OFF; - } - } - - return 0; -} - -static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num) -{ - if (icom_adapter->version == ADAPTER_V1) { - icom_port->global_reg = icom_adapter->base_addr + 0x4000; - icom_port->int_reg = icom_adapter->base_addr + - 0x4004 + 2 - 2 * port_num; - } else { - icom_port->global_reg = icom_adapter->base_addr + 0x8000; - if (icom_port->port < 2) - icom_port->int_reg = icom_adapter->base_addr + - 0x8004 + 2 - 2 * icom_port->port; - else - icom_port->int_reg = icom_adapter->base_addr + - 0x8024 + 2 - 2 * (icom_port->port - 2); - } -} -static int __devinit icom_load_ports(struct icom_adapter *icom_adapter) -{ - struct icom_port *icom_port; - int port_num; - - for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) { - - icom_port = &icom_adapter->port_info[port_num]; - - if (icom_port->status == ICOM_PORT_ACTIVE) { - icom_port_active(icom_port, icom_adapter, port_num); - icom_port->dram = icom_adapter->base_addr + - 0x2000 * icom_port->port; - - icom_port->adapter = icom_adapter; - - /* get port memory */ - if (get_port_memory(icom_port) != 0) { - dev_err(&icom_port->adapter->pci_dev->dev, - "Memory allocation for port FAILED\n"); - } - } - } - return 0; -} - -static int __devinit icom_alloc_adapter(struct icom_adapter - **icom_adapter_ref) -{ - int adapter_count = 0; - struct icom_adapter *icom_adapter; - struct icom_adapter *cur_adapter_entry; - struct list_head *tmp; - - icom_adapter = (struct icom_adapter *) - kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); - - if (!icom_adapter) { - return -ENOMEM; - } - - list_for_each(tmp, &icom_adapter_head) { - cur_adapter_entry = - list_entry(tmp, struct icom_adapter, - icom_adapter_entry); - if (cur_adapter_entry->index != adapter_count) { - break; - } - adapter_count++; - } - - icom_adapter->index = adapter_count; - list_add_tail(&icom_adapter->icom_adapter_entry, tmp); - - *icom_adapter_ref = icom_adapter; - return 0; -} - -static void icom_free_adapter(struct icom_adapter *icom_adapter) -{ - list_del(&icom_adapter->icom_adapter_entry); - kfree(icom_adapter); -} - -static void icom_remove_adapter(struct icom_adapter *icom_adapter) -{ - struct icom_port *icom_port; - int index; - - for (index = 0; index < icom_adapter->numb_ports; index++) { - icom_port = &icom_adapter->port_info[index]; - - if (icom_port->status == ICOM_PORT_ACTIVE) { - dev_info(&icom_adapter->pci_dev->dev, - "Device removed\n"); - - uart_remove_one_port(&icom_uart_driver, - &icom_port->uart_port); - - /* be sure that DTR and RTS are dropped */ - writeb(0x00, &icom_port->dram->osr); - - /* Wait 0.1 Sec for simple Init to complete */ - msleep(100); - - /* Stop proccessor */ - stop_processor(icom_port); - - free_port_memory(icom_port); - } - } - - free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter); - iounmap(icom_adapter->base_addr); - pci_release_regions(icom_adapter->pci_dev); - icom_free_adapter(icom_adapter); -} - -static void icom_kref_release(struct kref *kref) -{ - struct icom_adapter *icom_adapter; - - icom_adapter = to_icom_adapter(kref); - icom_remove_adapter(icom_adapter); -} - -static int __devinit icom_probe(struct pci_dev *dev, - const struct pci_device_id *ent) -{ - int index; - unsigned int command_reg; - int retval; - struct icom_adapter *icom_adapter; - struct icom_port *icom_port; - - retval = pci_enable_device(dev); - if (retval) { - dev_err(&dev->dev, "Device enable FAILED\n"); - return retval; - } - - if ( (retval = pci_request_regions(dev, "icom"))) { - dev_err(&dev->dev, "pci_request_regions FAILED\n"); - pci_disable_device(dev); - return retval; - } - - pci_set_master(dev); - - if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { - dev_err(&dev->dev, "PCI Config read FAILED\n"); - return retval; - } - - pci_write_config_dword(dev, PCI_COMMAND, - command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER - | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); - - if (ent->driver_data == ADAPTER_V1) { - pci_write_config_dword(dev, 0x44, 0x8300830A); - } else { - pci_write_config_dword(dev, 0x44, 0x42004200); - pci_write_config_dword(dev, 0x48, 0x42004200); - } - - - retval = icom_alloc_adapter(&icom_adapter); - if (retval) { - dev_err(&dev->dev, "icom_alloc_adapter FAILED\n"); - retval = -EIO; - goto probe_exit0; - } - - icom_adapter->base_addr_pci = pci_resource_start(dev, 0); - icom_adapter->pci_dev = dev; - icom_adapter->version = ent->driver_data; - icom_adapter->subsystem_id = ent->subdevice; - - - retval = icom_init_ports(icom_adapter); - if (retval) { - dev_err(&dev->dev, "Port configuration failed\n"); - goto probe_exit1; - } - - icom_adapter->base_addr = pci_ioremap_bar(dev, 0); - - if (!icom_adapter->base_addr) - goto probe_exit1; - - /* save off irq and request irq line */ - if ( (retval = request_irq(dev->irq, icom_interrupt, - IRQF_DISABLED | IRQF_SHARED, ICOM_DRIVER_NAME, - (void *) icom_adapter))) { - goto probe_exit2; - } - - retval = icom_load_ports(icom_adapter); - - for (index = 0; index < icom_adapter->numb_ports; index++) { - icom_port = &icom_adapter->port_info[index]; - - if (icom_port->status == ICOM_PORT_ACTIVE) { - icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq; - icom_port->uart_port.type = PORT_ICOM; - icom_port->uart_port.iotype = UPIO_MEM; - icom_port->uart_port.membase = - (char *) icom_adapter->base_addr_pci; - icom_port->uart_port.fifosize = 16; - icom_port->uart_port.ops = &icom_ops; - icom_port->uart_port.line = - icom_port->port + icom_adapter->index * 4; - if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) { - icom_port->status = ICOM_PORT_OFF; - dev_err(&dev->dev, "Device add failed\n"); - } else - dev_info(&dev->dev, "Device added\n"); - } - } - - kref_init(&icom_adapter->kref); - return 0; - -probe_exit2: - iounmap(icom_adapter->base_addr); -probe_exit1: - icom_free_adapter(icom_adapter); - -probe_exit0: - pci_release_regions(dev); - pci_disable_device(dev); - - return retval; -} - -static void __devexit icom_remove(struct pci_dev *dev) -{ - struct icom_adapter *icom_adapter; - struct list_head *tmp; - - list_for_each(tmp, &icom_adapter_head) { - icom_adapter = list_entry(tmp, struct icom_adapter, - icom_adapter_entry); - if (icom_adapter->pci_dev == dev) { - kref_put(&icom_adapter->kref, icom_kref_release); - return; - } - } - - dev_err(&dev->dev, "Unable to find device to remove\n"); -} - -static struct pci_driver icom_pci_driver = { - .name = ICOM_DRIVER_NAME, - .id_table = icom_pci_table, - .probe = icom_probe, - .remove = __devexit_p(icom_remove), -}; - -static int __init icom_init(void) -{ - int ret; - - spin_lock_init(&icom_lock); - - ret = uart_register_driver(&icom_uart_driver); - if (ret) - return ret; - - ret = pci_register_driver(&icom_pci_driver); - - if (ret < 0) - uart_unregister_driver(&icom_uart_driver); - - return ret; -} - -static void __exit icom_exit(void) -{ - pci_unregister_driver(&icom_pci_driver); - uart_unregister_driver(&icom_uart_driver); -} - -module_init(icom_init); -module_exit(icom_exit); - -MODULE_AUTHOR("Michael Anderson "); -MODULE_DESCRIPTION("IBM iSeries Serial IOA driver"); -MODULE_SUPPORTED_DEVICE - ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("icom_call_setup.bin"); -MODULE_FIRMWARE("icom_res_dce.bin"); -MODULE_FIRMWARE("icom_asc.bin"); diff --git a/drivers/serial/icom.h b/drivers/serial/icom.h deleted file mode 100644 index c8029e0025c9..000000000000 --- a/drivers/serial/icom.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - * icom.h - * - * Copyright (C) 2001 Michael Anderson, IBM Corporation - * - * Serial device driver include file. - * - * 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 - -#define BAUD_TABLE_LIMIT ((sizeof(icom_acfg_baud)/sizeof(int)) - 1) -static int icom_acfg_baud[] = { - 300, - 600, - 900, - 1200, - 1800, - 2400, - 3600, - 4800, - 7200, - 9600, - 14400, - 19200, - 28800, - 38400, - 57600, - 76800, - 115200, - 153600, - 230400, - 307200, - 460800, -}; - -struct icom_regs { - u32 control; /* Adapter Control Register */ - u32 interrupt; /* Adapter Interrupt Register */ - u32 int_mask; /* Adapter Interrupt Mask Reg */ - u32 int_pri; /* Adapter Interrupt Priority r */ - u32 int_reg_b; /* Adapter non-masked Interrupt */ - u32 resvd01; - u32 resvd02; - u32 resvd03; - u32 control_2; /* Adapter Control Register 2 */ - u32 interrupt_2; /* Adapter Interrupt Register 2 */ - u32 int_mask_2; /* Adapter Interrupt Mask 2 */ - u32 int_pri_2; /* Adapter Interrupt Prior 2 */ - u32 int_reg_2b; /* Adapter non-masked 2 */ -}; - -struct func_dram { - u32 reserved[108]; /* 0-1B0 reserved by personality code */ - u32 RcvStatusAddr; /* 1B0-1B3 Status Address for Next rcv */ - u8 RcvStnAddr; /* 1B4 Receive Station Addr */ - u8 IdleState; /* 1B5 Idle State */ - u8 IdleMonitor; /* 1B6 Idle Monitor */ - u8 FlagFillIdleTimer; /* 1B7 Flag Fill Idle Timer */ - u32 XmitStatusAddr; /* 1B8-1BB Transmit Status Address */ - u8 StartXmitCmd; /* 1BC Start Xmit Command */ - u8 HDLCConfigReg; /* 1BD Reserved */ - u8 CauseCode; /* 1BE Cause code for fatal error */ - u8 xchar; /* 1BF High priority send */ - u32 reserved3; /* 1C0-1C3 Reserved */ - u8 PrevCmdReg; /* 1C4 Reserved */ - u8 CmdReg; /* 1C5 Command Register */ - u8 async_config2; /* 1C6 Async Config Byte 2 */ - u8 async_config3; /* 1C7 Async Config Byte 3 */ - u8 dce_resvd[20]; /* 1C8-1DB DCE Rsvd */ - u8 dce_resvd21; /* 1DC DCE Rsvd (21st byte */ - u8 misc_flags; /* 1DD misc flags */ -#define V2_HARDWARE 0x40 -#define ICOM_HDW_ACTIVE 0x01 - u8 call_length; /* 1DE Phone #/CFI buff ln */ - u8 call_length2; /* 1DF Upper byte (unused) */ - u32 call_addr; /* 1E0-1E3 Phn #/CFI buff addr */ - u16 timer_value; /* 1E4-1E5 general timer value */ - u8 timer_command; /* 1E6 general timer cmd */ - u8 dce_command; /* 1E7 dce command reg */ - u8 dce_cmd_status; /* 1E8 dce command stat */ - u8 x21_r1_ioff; /* 1E9 dce ready counter */ - u8 x21_r0_ioff; /* 1EA dce not ready ctr */ - u8 x21_ralt_ioff; /* 1EB dce CNR counter */ - u8 x21_r1_ion; /* 1EC dce ready I on ctr */ - u8 rsvd_ier; /* 1ED Rsvd for IER (if ne */ - u8 ier; /* 1EE Interrupt Enable */ - u8 isr; /* 1EF Input Signal Reg */ - u8 osr; /* 1F0 Output Signal Reg */ - u8 reset; /* 1F1 Reset/Reload Reg */ - u8 disable; /* 1F2 Disable Reg */ - u8 sync; /* 1F3 Sync Reg */ - u8 error_stat; /* 1F4 Error Status */ - u8 cable_id; /* 1F5 Cable ID */ - u8 cs_length; /* 1F6 CS Load Length */ - u8 mac_length; /* 1F7 Mac Load Length */ - u32 cs_load_addr; /* 1F8-1FB Call Load PCI Addr */ - u32 mac_load_addr; /* 1FC-1FF Mac Load PCI Addr */ -}; - -/* - * adapter defines and structures - */ -#define ICOM_CONTROL_START_A 0x00000008 -#define ICOM_CONTROL_STOP_A 0x00000004 -#define ICOM_CONTROL_START_B 0x00000002 -#define ICOM_CONTROL_STOP_B 0x00000001 -#define ICOM_CONTROL_START_C 0x00000008 -#define ICOM_CONTROL_STOP_C 0x00000004 -#define ICOM_CONTROL_START_D 0x00000002 -#define ICOM_CONTROL_STOP_D 0x00000001 -#define ICOM_IRAM_OFFSET 0x1000 -#define ICOM_IRAM_SIZE 0x0C00 -#define ICOM_DCE_IRAM_OFFSET 0x0A00 -#define ICOM_CABLE_ID_VALID 0x01 -#define ICOM_CABLE_ID_MASK 0xF0 -#define ICOM_DISABLE 0x80 -#define CMD_XMIT_RCV_ENABLE 0xC0 -#define CMD_XMIT_ENABLE 0x40 -#define CMD_RCV_DISABLE 0x00 -#define CMD_RCV_ENABLE 0x80 -#define CMD_RESTART 0x01 -#define CMD_HOLD_XMIT 0x02 -#define CMD_SND_BREAK 0x04 -#define RS232_CABLE 0x06 -#define V24_CABLE 0x0E -#define V35_CABLE 0x0C -#define V36_CABLE 0x02 -#define NO_CABLE 0x00 -#define START_DOWNLOAD 0x80 -#define ICOM_INT_MASK_PRC_A 0x00003FFF -#define ICOM_INT_MASK_PRC_B 0x3FFF0000 -#define ICOM_INT_MASK_PRC_C 0x00003FFF -#define ICOM_INT_MASK_PRC_D 0x3FFF0000 -#define INT_RCV_COMPLETED 0x1000 -#define INT_XMIT_COMPLETED 0x2000 -#define INT_IDLE_DETECT 0x0800 -#define INT_RCV_DISABLED 0x0400 -#define INT_XMIT_DISABLED 0x0200 -#define INT_RCV_XMIT_SHUTDOWN 0x0100 -#define INT_FATAL_ERROR 0x0080 -#define INT_CABLE_PULL 0x0020 -#define INT_SIGNAL_CHANGE 0x0010 -#define HDLC_PPP_PURE_ASYNC 0x02 -#define HDLC_FF_FILL 0x00 -#define HDLC_HDW_FLOW 0x01 -#define START_XMIT 0x80 -#define ICOM_ACFG_DRIVE1 0x20 -#define ICOM_ACFG_NO_PARITY 0x00 -#define ICOM_ACFG_PARITY_ENAB 0x02 -#define ICOM_ACFG_PARITY_ODD 0x01 -#define ICOM_ACFG_8BPC 0x00 -#define ICOM_ACFG_7BPC 0x04 -#define ICOM_ACFG_6BPC 0x08 -#define ICOM_ACFG_5BPC 0x0C -#define ICOM_ACFG_1STOP_BIT 0x00 -#define ICOM_ACFG_2STOP_BIT 0x10 -#define ICOM_DTR 0x80 -#define ICOM_RTS 0x40 -#define ICOM_RI 0x08 -#define ICOM_DSR 0x80 -#define ICOM_DCD 0x20 -#define ICOM_CTS 0x40 - -#define NUM_XBUFFS 1 -#define NUM_RBUFFS 2 -#define RCV_BUFF_SZ 0x0200 -#define XMIT_BUFF_SZ 0x1000 -struct statusArea { - /**********************************************/ - /* Transmit Status Area */ - /**********************************************/ - struct xmit_status_area{ - u32 leNext; /* Next entry in Little Endian on Adapter */ - u32 leNextASD; - u32 leBuffer; /* Buffer for entry in LE for Adapter */ - u16 leLengthASD; - u16 leOffsetASD; - u16 leLength; /* Length of data in segment */ - u16 flags; -#define SA_FLAGS_DONE 0x0080 /* Done with Segment */ -#define SA_FLAGS_CONTINUED 0x8000 /* More Segments */ -#define SA_FLAGS_IDLE 0x4000 /* Mark IDLE after frm */ -#define SA_FLAGS_READY_TO_XMIT 0x0800 -#define SA_FLAGS_STAT_MASK 0x007F - } xmit[NUM_XBUFFS]; - - /**********************************************/ - /* Receive Status Area */ - /**********************************************/ - struct { - u32 leNext; /* Next entry in Little Endian on Adapter */ - u32 leNextASD; - u32 leBuffer; /* Buffer for entry in LE for Adapter */ - u16 WorkingLength; /* size of segment */ - u16 reserv01; - u16 leLength; /* Length of data in segment */ - u16 flags; -#define SA_FL_RCV_DONE 0x0010 /* Data ready */ -#define SA_FLAGS_OVERRUN 0x0040 -#define SA_FLAGS_PARITY_ERROR 0x0080 -#define SA_FLAGS_FRAME_ERROR 0x0001 -#define SA_FLAGS_FRAME_TRUNC 0x0002 -#define SA_FLAGS_BREAK_DET 0x0004 /* set conditionally by device driver, not hardware */ -#define SA_FLAGS_RCV_MASK 0xFFE6 - } rcv[NUM_RBUFFS]; -}; - -struct icom_adapter; - - -#define ICOM_MAJOR 243 -#define ICOM_MINOR_START 0 - -struct icom_port { - struct uart_port uart_port; - u8 imbed_modem; -#define ICOM_UNKNOWN 1 -#define ICOM_RVX 2 -#define ICOM_IMBED_MODEM 3 - unsigned char cable_id; - unsigned char read_status_mask; - unsigned char ignore_status_mask; - void __iomem * int_reg; - struct icom_regs __iomem *global_reg; - struct func_dram __iomem *dram; - int port; - struct statusArea *statStg; - dma_addr_t statStg_pci; - u32 *xmitRestart; - dma_addr_t xmitRestart_pci; - unsigned char *xmit_buf; - dma_addr_t xmit_buf_pci; - unsigned char *recv_buf; - dma_addr_t recv_buf_pci; - int next_rcv; - int put_length; - int status; -#define ICOM_PORT_ACTIVE 1 /* Port exists. */ -#define ICOM_PORT_OFF 0 /* Port does not exist. */ - int load_in_progress; - struct icom_adapter *adapter; -}; - -struct icom_adapter { - void __iomem * base_addr; - unsigned long base_addr_pci; - struct pci_dev *pci_dev; - struct icom_port port_info[4]; - int index; - int version; -#define ADAPTER_V1 0x0001 -#define ADAPTER_V2 0x0002 - u32 subsystem_id; -#define FOUR_PORT_MODEL 0x0252 -#define V2_TWO_PORTS_RVX 0x021A -#define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM 0x0251 - int numb_ports; - struct list_head icom_adapter_entry; - struct kref kref; -}; - -/* prototype */ -extern void iCom_sercons_init(void); - -struct lookup_proc_table { - u32 __iomem *global_control_reg; - unsigned long processor_id; -}; - -struct lookup_int_table { - u32 __iomem *global_int_mask; - unsigned long processor_id; -}; diff --git a/drivers/serial/ifx6x60.c b/drivers/serial/ifx6x60.c deleted file mode 100644 index ab93763862d5..000000000000 --- a/drivers/serial/ifx6x60.c +++ /dev/null @@ -1,1406 +0,0 @@ -/**************************************************************************** - * - * Driver for the IFX 6x60 spi modem. - * - * Copyright (C) 2008 Option International - * Copyright (C) 2008 Filip Aben - * Denis Joseph Barrow - * Jan Dumon - * - * Copyright (C) 2009, 2010 Intel Corp - * Russ Gorby - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA - * - * Driver modified by Intel from Option gtm501l_spi.c - * - * Notes - * o The driver currently assumes a single device only. If you need to - * change this then look for saved_ifx_dev and add a device lookup - * o The driver is intended to be big-endian safe but has never been - * tested that way (no suitable hardware). There are a couple of FIXME - * notes by areas that may need addressing - * o Some of the GPIO naming/setup assumptions may need revisiting if - * you need to use this driver for another platform. - * - *****************************************************************************/ -#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 - -#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_TTY_ID 0 -#define IFX_SPI_TIMEOUT_SEC 2 -#define IFX_SPI_HEADER_0 (-1) -#define IFX_SPI_HEADER_F (-2) - -/* forward reference */ -static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev); - -/* local variables */ -static int spi_b16 = 1; /* 8 or 16 bit word length */ -static struct tty_driver *tty_drv; -static struct ifx_spi_device *saved_ifx_dev; -static struct lock_class_key ifx_spi_key; - -/* GPIO/GPE settings */ - -/** - * mrdy_set_high - set MRDY GPIO - * @ifx: device we are controlling - * - */ -static inline void mrdy_set_high(struct ifx_spi_device *ifx) -{ - gpio_set_value(ifx->gpio.mrdy, 1); -} - -/** - * mrdy_set_low - clear MRDY GPIO - * @ifx: device we are controlling - * - */ -static inline void mrdy_set_low(struct ifx_spi_device *ifx) -{ - gpio_set_value(ifx->gpio.mrdy, 0); -} - -/** - * ifx_spi_power_state_set - * @ifx_dev: our SPI device - * @val: bits to set - * - * Set bit in power status and signal power system if status becomes non-0 - */ -static void -ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&ifx_dev->power_lock, flags); - - /* - * if power status is already non-0, just update, else - * tell power system - */ - if (!ifx_dev->power_status) - pm_runtime_get(&ifx_dev->spi_dev->dev); - ifx_dev->power_status |= val; - - spin_unlock_irqrestore(&ifx_dev->power_lock, flags); -} - -/** - * ifx_spi_power_state_clear - clear power bit - * @ifx_dev: our SPI device - * @val: bits to clear - * - * clear bit in power status and signal power system if status becomes 0 - */ -static void -ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&ifx_dev->power_lock, flags); - - if (ifx_dev->power_status) { - ifx_dev->power_status &= ~val; - if (!ifx_dev->power_status) - pm_runtime_put(&ifx_dev->spi_dev->dev); - } - - spin_unlock_irqrestore(&ifx_dev->power_lock, flags); -} - -/** - * swap_buf - * @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) -{ - int n; - - len = ((len + 1) >> 1); - if ((void *)&buf[len] > end) { - pr_err("swap_buf: swap exceeds boundary (%p > %p)!", - &buf[len], end); - return; - } - for (n = 0; n < len; n++) { - *buf = cpu_to_be16(*buf); - buf++; - } -} - -/** - * mrdy_assert - assert MRDY line - * @ifx_dev: our SPI device - * - * Assert mrdy and set timer to wait for SRDY interrupt, if SRDY is low - * now. - * - * FIXME: Can SRDY even go high as we are running this code ? - */ -static void mrdy_assert(struct ifx_spi_device *ifx_dev) -{ - int val = gpio_get_value(ifx_dev->gpio.srdy); - 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); - - } - } - ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_DATA_PENDING); - mrdy_set_high(ifx_dev); -} - -/** - * ifx_spi_hangup - hang up an IFX device - * @ifx_dev: our SPI device - * - * Hang up the tty attached to the IFX device if one is currently - * open. If not take no action - */ -static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) -{ - struct tty_port *pport = &ifx_dev->tty_port; - struct tty_struct *tty = tty_port_tty_get(pport); - if (tty) { - tty_hangup(tty); - tty_kref_put(tty); - } -} - -/** - * ifx_spi_timeout - SPI timeout - * @arg: our SPI device - * - * The SPI has timed out: hang up the tty. Users will then see a hangup - * and error events. - */ -static void ifx_spi_timeout(unsigned long arg) -{ - struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; - - dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); - ifx_spi_ttyhangup(ifx_dev); - mrdy_set_low(ifx_dev); - clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); -} - -/* char/tty operations */ - -/** - * ifx_spi_tiocmget - get modem lines - * @tty: our tty device - * @filp: file handle issuing the request - * - * Map the signal state into Linux modem flags and report the value - * in Linux terms - */ -static int ifx_spi_tiocmget(struct tty_struct *tty, struct file *filp) -{ - unsigned int value; - struct ifx_spi_device *ifx_dev = tty->driver_data; - - value = - (test_bit(IFX_SPI_RTS, &ifx_dev->signal_state) ? TIOCM_RTS : 0) | - (test_bit(IFX_SPI_DTR, &ifx_dev->signal_state) ? TIOCM_DTR : 0) | - (test_bit(IFX_SPI_CTS, &ifx_dev->signal_state) ? TIOCM_CTS : 0) | - (test_bit(IFX_SPI_DSR, &ifx_dev->signal_state) ? TIOCM_DSR : 0) | - (test_bit(IFX_SPI_DCD, &ifx_dev->signal_state) ? TIOCM_CAR : 0) | - (test_bit(IFX_SPI_RI, &ifx_dev->signal_state) ? TIOCM_RNG : 0); - return value; -} - -/** - * ifx_spi_tiocmset - set modem bits - * @tty: the tty structure - * @filp: file handle issuing the request - * @set: bits to set - * @clear: bits to clear - * - * The IFX6x60 only supports DTR and RTS. Set them accordingly - * and flag that an update to the modem is needed. - * - * FIXME: do we need to kick the tranfers when we do this ? - */ -static int ifx_spi_tiocmset(struct tty_struct *tty, struct file *filp, - unsigned int set, unsigned int clear) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - - if (set & TIOCM_RTS) - set_bit(IFX_SPI_RTS, &ifx_dev->signal_state); - if (set & TIOCM_DTR) - set_bit(IFX_SPI_DTR, &ifx_dev->signal_state); - if (clear & TIOCM_RTS) - clear_bit(IFX_SPI_RTS, &ifx_dev->signal_state); - if (clear & TIOCM_DTR) - clear_bit(IFX_SPI_DTR, &ifx_dev->signal_state); - - set_bit(IFX_SPI_UPDATE, &ifx_dev->signal_state); - return 0; -} - -/** - * ifx_spi_open - called on tty open - * @tty: our tty device - * @filp: file handle being associated with the tty - * - * Open the tty interface. We let the tty_port layer do all the work - * for us. - * - * FIXME: Remove single device assumption and saved_ifx_dev - */ -static int ifx_spi_open(struct tty_struct *tty, struct file *filp) -{ - return tty_port_open(&saved_ifx_dev->tty_port, tty, filp); -} - -/** - * ifx_spi_close - called when our tty closes - * @tty: the tty being closed - * @filp: the file handle being closed - * - * Perform the close of the tty. We use the tty_port layer to do all - * our hard work. - */ -static void ifx_spi_close(struct tty_struct *tty, struct file *filp) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - tty_port_close(&ifx_dev->tty_port, tty, filp); - /* FIXME: should we do an ifx_spi_reset here ? */ -} - -/** - * ifx_decode_spi_header - decode received header - * @buffer: the received data - * @length: decoded length - * @more: decoded more flag - * @received_cts: status of cts we received - * - * Note how received_cts is handled -- if header is all F it is left - * the same as it was, if header is all 0 it is set to 0 otherwise it is - * taken from the incoming header. - * - * FIXME: endianness - */ -static int ifx_spi_decode_spi_header(unsigned char *buffer, int *length, - unsigned char *more, unsigned char *received_cts) -{ - u16 h1; - u16 h2; - u16 *in_buffer = (u16 *)buffer; - - h1 = *in_buffer; - h2 = *(in_buffer+1); - - if (h1 == 0 && h2 == 0) { - *received_cts = 0; - return IFX_SPI_HEADER_0; - } else if (h1 == 0xffff && h2 == 0xffff) { - /* spi_slave_cts remains as it was */ - return IFX_SPI_HEADER_F; - } - - *length = h1 & 0xfff; /* upper bits of byte are flags */ - *more = (buffer[1] >> IFX_SPI_MORE_BIT) & 1; - *received_cts = (buffer[3] >> IFX_SPI_CTS_BIT) & 1; - return 0; -} - -/** - * ifx_setup_spi_header - set header fields - * @txbuffer: pointer to start of SPI buffer - * @tx_count: bytes - * @more: indicate if more to follow - * - * Format up an SPI header for a transfer - * - * FIXME: endianness? - */ -static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, - unsigned char more) -{ - *(u16 *)(txbuffer) = tx_count; - *(u16 *)(txbuffer+2) = IFX_SPI_PAYLOAD_SIZE; - txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK; -} - -/** - * ifx_spi_wakeup_serial - SPI space made - * @port_data: our SPI device - * - * We have emptied the FIFO enough that we want to get more data - * queued into it. Poke the line discipline via tty_wakeup so that - * it will feed us more bits - */ -static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) -{ - struct tty_struct *tty; - - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_wakeup(tty); - tty_kref_put(tty); -} - -/** - * ifx_spi_prepare_tx_buffer - prepare transmit frame - * @ifx_dev: our SPI device - * - * The transmit buffr needs a header and various other bits of - * information followed by as much data as we can pull from the FIFO - * and transfer. This function formats up a suitable buffer in the - * ifx_dev->tx_buffer - * - * FIXME: performance - should we wake the tty when the queue is half - * empty ? - */ -static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) -{ - int temp_count; - int queue_length; - int tx_count; - unsigned char *tx_buffer; - - tx_buffer = ifx_dev->tx_buffer; - memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE); - - /* make room for required SPI header */ - tx_buffer += IFX_SPI_HEADER_OVERHEAD; - tx_count = IFX_SPI_HEADER_OVERHEAD; - - /* clear to signal no more data if this turns out to be the - * last buffer sent in a sequence */ - ifx_dev->spi_more = 0; - - /* if modem cts is set, just send empty buffer */ - if (!ifx_dev->spi_slave_cts) { - /* see if there's tx data */ - queue_length = kfifo_len(&ifx_dev->tx_fifo); - if (queue_length != 0) { - /* data to mux -- see if there's room for it */ - temp_count = min(queue_length, IFX_SPI_PAYLOAD_SIZE); - temp_count = kfifo_out_locked(&ifx_dev->tx_fifo, - tx_buffer, temp_count, - &ifx_dev->fifo_lock); - - /* update buffer pointer and data count in message */ - tx_buffer += temp_count; - tx_count += temp_count; - if (temp_count == queue_length) - /* poke port to get more data */ - ifx_spi_wakeup_serial(ifx_dev); - else /* more data in port, use next SPI message */ - ifx_dev->spi_more = 1; - } - } - /* have data and info for header -- set up SPI header in buffer */ - /* spi header needs payload size, not entire buffer size */ - ifx_spi_setup_spi_header(ifx_dev->tx_buffer, - 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->tx_buffer[IFX_SPI_TRANSFER_SIZE]); - return tx_count; -} - -/** - * ifx_spi_write - line discipline write - * @tty: our tty device - * @buf: pointer to buffer to write (kernel space) - * @count: size of buffer - * - * Write the characters we have been given into the FIFO. If the device - * is not active then activate it, when the SRDY line is asserted back - * this will commence I/O - */ -static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, - int count) -{ - 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); - return tx_count; -} - -/** - * ifx_spi_chars_in_buffer - line discipline helper - * @tty: our tty device - * - * Report how much data we can accept before we drop bytes. As we use - * a simple FIFO this is nice and easy. - */ -static int ifx_spi_write_room(struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - return IFX_SPI_FIFO_SIZE - kfifo_len(&ifx_dev->tx_fifo); -} - -/** - * ifx_spi_chars_in_buffer - line discipline helper - * @tty: our tty device - * - * Report how many characters we have buffered. In our case this is the - * number of bytes sitting in our transmit FIFO. - */ -static int ifx_spi_chars_in_buffer(struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - return kfifo_len(&ifx_dev->tx_fifo); -} - -/** - * ifx_port_hangup - * @port: our tty port - * - * tty port hang up. Called when tty_hangup processing is invoked either - * by loss of carrier, or by software (eg vhangup). Serialized against - * activate/shutdown by the tty layer. - */ -static void ifx_spi_hangup(struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = tty->driver_data; - tty_port_hangup(&ifx_dev->tty_port); -} - -/** - * ifx_port_activate - * @port: our tty port - * - * tty port activate method - called for first open. Serialized - * with hangup and shutdown by the tty layer. - */ -static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) -{ - struct ifx_spi_device *ifx_dev = - container_of(port, struct ifx_spi_device, tty_port); - - /* clear any old data; can't do this in 'close' */ - kfifo_reset(&ifx_dev->tx_fifo); - - /* put port data into this tty */ - tty->driver_data = ifx_dev; - - /* allows flip string push from int context */ - tty->low_latency = 1; - - return 0; -} - -/** - * ifx_port_shutdown - * @port: our tty port - * - * tty port shutdown method - called for last port close. Serialized - * with hangup and activate by the tty layer. - */ -static void ifx_port_shutdown(struct tty_port *port) -{ - struct ifx_spi_device *ifx_dev = - container_of(port, struct ifx_spi_device, tty_port); - - mrdy_set_low(ifx_dev); - clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); - tasklet_kill(&ifx_dev->io_work_tasklet); -} - -static const struct tty_port_operations ifx_tty_port_ops = { - .activate = ifx_port_activate, - .shutdown = ifx_port_shutdown, -}; - -static const struct tty_operations ifx_spi_serial_ops = { - .open = ifx_spi_open, - .close = ifx_spi_close, - .write = ifx_spi_write, - .hangup = ifx_spi_hangup, - .write_room = ifx_spi_write_room, - .chars_in_buffer = ifx_spi_chars_in_buffer, - .tiocmget = ifx_spi_tiocmget, - .tiocmset = ifx_spi_tiocmset, -}; - -/** - * ifx_spi_insert_fip_string - queue received data - * @ifx_ser: our SPI device - * @chars: buffer we have received - * @size: number of chars reeived - * - * Queue bytes to the tty assuming the tty side is currently open. If - * not the discard the data. - */ -static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, - unsigned char *chars, size_t size) -{ - struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port); - if (!tty) - return; - tty_insert_flip_string(tty, chars, size); - tty_flip_buffer_push(tty); - tty_kref_put(tty); -} - -/** - * ifx_spi_complete - SPI transfer completed - * @ctx: our SPI device - * - * An SPI transfer has completed. Process any received data and kick off - * any further transmits we can commence. - */ -static void ifx_spi_complete(void *ctx) -{ - struct ifx_spi_device *ifx_dev = ctx; - struct tty_struct *tty; - struct tty_ldisc *ldisc = NULL; - int length; - int actual_length; - unsigned char more; - unsigned char cts; - int local_write_pending = 0; - int queue_length; - int srdy; - int decode_result; - - mrdy_set_low(ifx_dev); - - 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->rx_buffer[IFX_SPI_HEADER_OVERHEAD]); - decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer, - &length, &more, &cts); - if (decode_result == IFX_SPI_HEADER_0) { - dev_dbg(&ifx_dev->spi_dev->dev, - "ignore input: invalid header 0"); - ifx_dev->spi_slave_cts = 0; - goto complete_exit; - } else if (decode_result == IFX_SPI_HEADER_F) { - dev_dbg(&ifx_dev->spi_dev->dev, - "ignore input: invalid header F"); - goto complete_exit; - } - - ifx_dev->spi_slave_cts = cts; - - actual_length = min((unsigned int)length, - ifx_dev->spi_msg.actual_length); - swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), - actual_length, - &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]); - ifx_spi_insert_flip_string( - ifx_dev, - ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD, - (size_t)actual_length); - } else { - dev_dbg(&ifx_dev->spi_dev->dev, "SPI transfer error %d", - ifx_dev->spi_msg.status); - } - -complete_exit: - if (ifx_dev->write_pending) { - ifx_dev->write_pending = 0; - local_write_pending = 1; - } - - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags)); - - queue_length = kfifo_len(&ifx_dev->tx_fifo); - srdy = gpio_get_value(ifx_dev->gpio.srdy); - if (!srdy) - ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY); - - /* schedule output if there is more to do */ - if (test_and_clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags)) - tasklet_schedule(&ifx_dev->io_work_tasklet); - else { - if (more || ifx_dev->spi_more || queue_length > 0 || - local_write_pending) { - if (ifx_dev->spi_slave_cts) { - if (more) - mrdy_assert(ifx_dev); - } else - mrdy_assert(ifx_dev); - } else { - /* - * poke line discipline driver if any for more data - * may or may not get more data to write - * for now, say not busy - */ - ifx_spi_power_state_clear(ifx_dev, - IFX_SPI_POWER_DATA_PENDING); - tty = tty_port_tty_get(&ifx_dev->tty_port); - if (tty) { - ldisc = tty_ldisc_ref(tty); - if (ldisc) { - ldisc->ops->write_wakeup(tty); - tty_ldisc_deref(ldisc); - } - tty_kref_put(tty); - } - } - } -} - -/** - * ifx_spio_io - I/O tasklet - * @data: our SPI device - * - * Queue data for transmission if possible and then kick off the - * transfer. - */ -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 (ifx_dev->gpio.unack_srdy_int_nb > 0) - ifx_dev->gpio.unack_srdy_int_nb--; - - ifx_spi_prepare_tx_buffer(ifx_dev); - - spi_message_init(&ifx_dev->spi_msg); - INIT_LIST_HEAD(&ifx_dev->spi_msg.queue); - - ifx_dev->spi_msg.context = ifx_dev; - ifx_dev->spi_msg.complete = ifx_spi_complete; - - /* set up our spi transfer */ - /* note len is BYTES, not transfers */ - ifx_dev->spi_xfer.len = IFX_SPI_TRANSFER_SIZE; - ifx_dev->spi_xfer.cs_change = 0; - ifx_dev->spi_xfer.speed_hz = 12500000; - /* ifx_dev->spi_xfer.speed_hz = 390625; */ - ifx_dev->spi_xfer.bits_per_word = spi_b16 ? 16 : 8; - - ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer; - ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer; - - /* - * setup dma pointers - */ - if (ifx_dev->is_6160) { - ifx_dev->spi_msg.is_dma_mapped = 1; - ifx_dev->tx_dma = ifx_dev->tx_bus; - ifx_dev->rx_dma = ifx_dev->rx_bus; - ifx_dev->spi_xfer.tx_dma = ifx_dev->tx_dma; - ifx_dev->spi_xfer.rx_dma = ifx_dev->rx_dma; - } else { - ifx_dev->spi_msg.is_dma_mapped = 0; - ifx_dev->tx_dma = (dma_addr_t)0; - ifx_dev->rx_dma = (dma_addr_t)0; - ifx_dev->spi_xfer.tx_dma = (dma_addr_t)0; - ifx_dev->spi_xfer.rx_dma = (dma_addr_t)0; - } - - spi_message_add_tail(&ifx_dev->spi_xfer, &ifx_dev->spi_msg); - - /* Assert MRDY. This may have already been done by the write - * routine. - */ - mrdy_assert(ifx_dev); - - retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg); - if (retval) { - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, - &ifx_dev->flags); - tasklet_schedule(&ifx_dev->io_work_tasklet); - return; - } - } else - ifx_dev->write_pending = 1; -} - -/** - * ifx_spi_free_port - free up the tty side - * @ifx_dev: IFX device going away - * - * Unregister and free up a port when the device goes away - */ -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); - kfifo_free(&ifx_dev->tx_fifo); -} - -/** - * ifx_spi_create_port - create a new port - * @ifx_dev: our spi device - * - * Allocate and initialise the tty port that goes with this interface - * and add it to the tty layer so that it can be opened. - */ -static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) -{ - int ret = 0; - struct tty_port *pport = &ifx_dev->tty_port; - - spin_lock_init(&ifx_dev->fifo_lock); - lockdep_set_class_and_subclass(&ifx_dev->fifo_lock, - &ifx_spi_key, 0); - - if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE, GFP_KERNEL)) { - ret = -ENOMEM; - goto error_ret; - } - - pport->ops = &ifx_tty_port_ops; - tty_port_init(pport); - ifx_dev->minor = IFX_SPI_TTY_ID; - ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor, - &ifx_dev->spi_dev->dev); - if (IS_ERR(ifx_dev->tty_dev)) { - dev_dbg(&ifx_dev->spi_dev->dev, - "%s: registering tty device failed", __func__); - ret = PTR_ERR(ifx_dev->tty_dev); - goto error_ret; - } - return 0; - -error_ret: - ifx_spi_free_port(ifx_dev); - return ret; -} - -/** - * ifx_spi_handle_srdy - handle SRDY - * @ifx_dev: device asserting SRDY - * - * Check our device state and see what we need to kick off when SRDY - * is asserted. This usually means killing the timer and firing off the - * I/O processing. - */ -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); - clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); - } - - ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_SRDY); - - if (!test_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) - tasklet_schedule(&ifx_dev->io_work_tasklet); - else - set_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); -} - -/** - * ifx_spi_srdy_interrupt - SRDY asserted - * @irq: our IRQ number - * @dev: our ifx device - * - * The modem asserted SRDY. Handle the srdy event - */ -static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev) -{ - struct ifx_spi_device *ifx_dev = dev; - ifx_dev->gpio.unack_srdy_int_nb++; - ifx_spi_handle_srdy(ifx_dev); - return IRQ_HANDLED; -} - -/** - * ifx_spi_reset_interrupt - Modem has changed reset state - * @irq: interrupt number - * @dev: our device pointer - * - * The modem has either entered or left reset state. Check the GPIO - * line to see which. - * - * FIXME: review locking on MR_INPROGRESS versus - * parallel unsolicited reset/solicited reset - */ -static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) -{ - struct ifx_spi_device *ifx_dev = dev; - int val = gpio_get_value(ifx_dev->gpio.reset_out); - int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state); - - if (val == 0) { - /* entered reset */ - set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); - if (!solreset) { - /* unsolicited reset */ - ifx_spi_ttyhangup(ifx_dev); - } - } else { - /* exited reset */ - clear_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); - if (solreset) { - set_bit(MR_COMPLETE, &ifx_dev->mdm_reset_state); - wake_up(&ifx_dev->mdm_reset_wait); - } - } - return IRQ_HANDLED; -} - -/** - * ifx_spi_free_device - free device - * @ifx_dev: device to free - * - * Free the IFX device - */ -static void ifx_spi_free_device(struct ifx_spi_device *ifx_dev) -{ - ifx_spi_free_port(ifx_dev); - dma_free_coherent(&ifx_dev->spi_dev->dev, - IFX_SPI_TRANSFER_SIZE, - ifx_dev->tx_buffer, - ifx_dev->tx_bus); - dma_free_coherent(&ifx_dev->spi_dev->dev, - IFX_SPI_TRANSFER_SIZE, - ifx_dev->rx_buffer, - ifx_dev->rx_bus); -} - -/** - * ifx_spi_reset - reset modem - * @ifx_dev: modem to reset - * - * Perform a reset on the modem - */ -static int ifx_spi_reset(struct ifx_spi_device *ifx_dev) -{ - int ret; - /* - * set up modem power, reset - * - * delays are required on some platforms for the modem - * to reset properly - */ - set_bit(MR_START, &ifx_dev->mdm_reset_state); - gpio_set_value(ifx_dev->gpio.po, 0); - gpio_set_value(ifx_dev->gpio.reset, 0); - msleep(25); - gpio_set_value(ifx_dev->gpio.reset, 1); - msleep(1); - gpio_set_value(ifx_dev->gpio.po, 1); - msleep(1); - gpio_set_value(ifx_dev->gpio.po, 0); - ret = wait_event_timeout(ifx_dev->mdm_reset_wait, - test_bit(MR_COMPLETE, - &ifx_dev->mdm_reset_state), - IFX_RESET_TIMEOUT); - if (!ret) - dev_warn(&ifx_dev->spi_dev->dev, "Modem reset timeout: (state:%lx)", - ifx_dev->mdm_reset_state); - - ifx_dev->mdm_reset_state = 0; - return ret; -} - -/** - * ifx_spi_spi_probe - probe callback - * @spi: our possible matching SPI device - * - * Probe for a 6x60 modem on SPI bus. Perform any needed device and - * GPIO setup. - * - * FIXME: - * - Support for multiple devices - * - Split out MID specific GPIO handling eventually - */ - -static int ifx_spi_spi_probe(struct spi_device *spi) -{ - int ret; - int srdy; - struct ifx_modem_platform_data *pl_data = NULL; - struct ifx_spi_device *ifx_dev; - - if (saved_ifx_dev) { - dev_dbg(&spi->dev, "ignoring subsequent detection"); - return -ENODEV; - } - - /* initialize structure to hold our device variables */ - ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL); - if (!ifx_dev) { - dev_err(&spi->dev, "spi device allocation failed"); - return -ENOMEM; - } - saved_ifx_dev = ifx_dev; - ifx_dev->spi_dev = spi; - clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); - spin_lock_init(&ifx_dev->write_lock); - spin_lock_init(&ifx_dev->power_lock); - ifx_dev->power_status = 0; - init_timer(&ifx_dev->spi_timer); - ifx_dev->spi_timer.function = ifx_spi_timeout; - ifx_dev->spi_timer.data = (unsigned long)ifx_dev; - ifx_dev->is_6160 = pl_data->is_6160; - - /* ensure SPI protocol flags are initialized to enable transfer */ - ifx_dev->spi_more = 0; - ifx_dev->spi_slave_cts = 0; - - /*initialize transfer and dma buffers */ - ifx_dev->tx_buffer = dma_alloc_coherent(&ifx_dev->spi_dev->dev, - IFX_SPI_TRANSFER_SIZE, - &ifx_dev->tx_bus, - GFP_KERNEL); - if (!ifx_dev->tx_buffer) { - dev_err(&spi->dev, "DMA-TX buffer allocation failed"); - ret = -ENOMEM; - goto error_ret; - } - ifx_dev->rx_buffer = dma_alloc_coherent(&ifx_dev->spi_dev->dev, - IFX_SPI_TRANSFER_SIZE, - &ifx_dev->rx_bus, - GFP_KERNEL); - if (!ifx_dev->rx_buffer) { - dev_err(&spi->dev, "DMA-RX buffer allocation failed"); - ret = -ENOMEM; - goto error_ret; - } - - /* initialize waitq for modem reset */ - init_waitqueue_head(&ifx_dev->mdm_reset_wait); - - spi_set_drvdata(spi, ifx_dev); - tasklet_init(&ifx_dev->io_work_tasklet, ifx_spi_io, - (unsigned long)ifx_dev); - - set_bit(IFX_SPI_STATE_PRESENT, &ifx_dev->flags); - - /* create our tty port */ - ret = ifx_spi_create_port(ifx_dev); - if (ret != 0) { - dev_err(&spi->dev, "create default tty port failed"); - goto error_ret; - } - - pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; - if (pl_data) { - ifx_dev->gpio.reset = pl_data->rst_pmu; - ifx_dev->gpio.po = pl_data->pwr_on; - ifx_dev->gpio.mrdy = pl_data->mrdy; - ifx_dev->gpio.srdy = pl_data->srdy; - ifx_dev->gpio.reset_out = pl_data->rst_out; - } else { - dev_err(&spi->dev, "missing platform data!"); - ret = -ENODEV; - goto error_ret; - } - - dev_info(&spi->dev, "gpios %d, %d, %d, %d, %d", - ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy, - ifx_dev->gpio.srdy, ifx_dev->gpio.reset_out); - - /* Configure gpios */ - ret = gpio_request(ifx_dev->gpio.reset, "ifxModem"); - if (ret < 0) { - dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET)", - ifx_dev->gpio.reset); - goto error_ret; - } - ret += gpio_direction_output(ifx_dev->gpio.reset, 0); - ret += gpio_export(ifx_dev->gpio.reset, 1); - if (ret) { - dev_err(&spi->dev, "Unable to configure GPIO%d (RESET)", - ifx_dev->gpio.reset); - ret = -EBUSY; - goto error_ret2; - } - - ret = gpio_request(ifx_dev->gpio.po, "ifxModem"); - ret += gpio_direction_output(ifx_dev->gpio.po, 0); - ret += gpio_export(ifx_dev->gpio.po, 1); - if (ret) { - dev_err(&spi->dev, "Unable to configure GPIO%d (ON)", - ifx_dev->gpio.po); - ret = -EBUSY; - goto error_ret3; - } - - ret = gpio_request(ifx_dev->gpio.mrdy, "ifxModem"); - if (ret < 0) { - dev_err(&spi->dev, "Unable to allocate GPIO%d (MRDY)", - ifx_dev->gpio.mrdy); - goto error_ret3; - } - ret += gpio_export(ifx_dev->gpio.mrdy, 1); - ret += gpio_direction_output(ifx_dev->gpio.mrdy, 0); - if (ret) { - dev_err(&spi->dev, "Unable to configure GPIO%d (MRDY)", - ifx_dev->gpio.mrdy); - ret = -EBUSY; - goto error_ret4; - } - - ret = gpio_request(ifx_dev->gpio.srdy, "ifxModem"); - if (ret < 0) { - dev_err(&spi->dev, "Unable to allocate GPIO%d (SRDY)", - ifx_dev->gpio.srdy); - ret = -EBUSY; - goto error_ret4; - } - ret += gpio_export(ifx_dev->gpio.srdy, 1); - ret += gpio_direction_input(ifx_dev->gpio.srdy); - if (ret) { - dev_err(&spi->dev, "Unable to configure GPIO%d (SRDY)", - ifx_dev->gpio.srdy); - ret = -EBUSY; - goto error_ret5; - } - - ret = gpio_request(ifx_dev->gpio.reset_out, "ifxModem"); - if (ret < 0) { - dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET_OUT)", - ifx_dev->gpio.reset_out); - goto error_ret5; - } - ret += gpio_export(ifx_dev->gpio.reset_out, 1); - ret += gpio_direction_input(ifx_dev->gpio.reset_out); - if (ret) { - dev_err(&spi->dev, "Unable to configure GPIO%d (RESET_OUT)", - ifx_dev->gpio.reset_out); - ret = -EBUSY; - goto error_ret6; - } - - ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out), - ifx_spi_reset_interrupt, - IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME, - (void *)ifx_dev); - if (ret) { - dev_err(&spi->dev, "Unable to get irq %x\n", - gpio_to_irq(ifx_dev->gpio.reset_out)); - goto error_ret6; - } - - ret = ifx_spi_reset(ifx_dev); - - ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy), - ifx_spi_srdy_interrupt, - IRQF_TRIGGER_RISING, DRVNAME, - (void *)ifx_dev); - if (ret) { - dev_err(&spi->dev, "Unable to get irq %x", - gpio_to_irq(ifx_dev->gpio.srdy)); - goto error_ret7; - } - - /* set pm runtime power state and register with power system */ - pm_runtime_set_active(&spi->dev); - pm_runtime_enable(&spi->dev); - - /* handle case that modem is already signaling SRDY */ - /* no outgoing tty open at this point, this just satisfies the - * modem's read and should reset communication properly - */ - srdy = gpio_get_value(ifx_dev->gpio.srdy); - - if (srdy) { - mrdy_assert(ifx_dev); - ifx_spi_handle_srdy(ifx_dev); - } else - mrdy_set_low(ifx_dev); - return 0; - -error_ret7: - free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); -error_ret6: - gpio_free(ifx_dev->gpio.srdy); -error_ret5: - gpio_free(ifx_dev->gpio.mrdy); -error_ret4: - gpio_free(ifx_dev->gpio.reset); -error_ret3: - gpio_free(ifx_dev->gpio.po); -error_ret2: - gpio_free(ifx_dev->gpio.reset_out); -error_ret: - ifx_spi_free_device(ifx_dev); - saved_ifx_dev = NULL; - return ret; -} - -/** - * ifx_spi_spi_remove - SPI device was removed - * @spi: SPI device - * - * FIXME: We should be shutting the device down here not in - * the module unload path. - */ - -static int ifx_spi_spi_remove(struct spi_device *spi) -{ - struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); - /* stop activity */ - tasklet_kill(&ifx_dev->io_work_tasklet); - /* free irq */ - free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); - free_irq(gpio_to_irq(ifx_dev->gpio.srdy), (void *)ifx_dev); - - gpio_free(ifx_dev->gpio.srdy); - gpio_free(ifx_dev->gpio.mrdy); - gpio_free(ifx_dev->gpio.reset); - gpio_free(ifx_dev->gpio.po); - gpio_free(ifx_dev->gpio.reset_out); - - /* free allocations */ - ifx_spi_free_device(ifx_dev); - - saved_ifx_dev = NULL; - return 0; -} - -/** - * ifx_spi_spi_shutdown - called on SPI shutdown - * @spi: SPI device - * - * No action needs to be taken here - */ - -static void ifx_spi_spi_shutdown(struct spi_device *spi) -{ -} - -/* - * various suspends and resumes have nothing to do - * no hardware to save state for - */ - -/** - * ifx_spi_spi_suspend - suspend SPI on system suspend - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg) -{ - return 0; -} - -/** - * ifx_spi_spi_resume - resume SPI side on system resume - * @dev: device being suspended - * - * Suspend the SPI side. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_spi_resume(struct spi_device *spi) -{ - return 0; -} - -/** - * ifx_spi_pm_suspend - suspend modem on system suspend - * @dev: device being suspended - * - * Suspend the modem. No action needed on Intel MID platforms, may - * need extending for other systems. - */ -static int ifx_spi_pm_suspend(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_resume - resume modem on system resume - * @dev: device being suspended - * - * Allow the modem to resume. No action needed. - * - * FIXME: do we need to reset anything here ? - */ -static int ifx_spi_pm_resume(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_runtime_resume - suspend modem - * @dev: device being suspended - * - * Allow the modem to resume. No action needed. - */ -static int ifx_spi_pm_runtime_resume(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_runtime_suspend - suspend modem - * @dev: device being suspended - * - * Allow the modem to suspend and thus suspend to continue up the - * device tree. - */ -static int ifx_spi_pm_runtime_suspend(struct device *dev) -{ - return 0; -} - -/** - * ifx_spi_pm_runtime_idle - check if modem idle - * @dev: our device - * - * Check conditions and queue runtime suspend if idle. - */ -static int ifx_spi_pm_runtime_idle(struct device *dev) -{ - struct spi_device *spi = to_spi_device(dev); - struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); - - if (!ifx_dev->power_status) - pm_runtime_suspend(dev); - - return 0; -} - -static const struct dev_pm_ops ifx_spi_pm = { - .resume = ifx_spi_pm_resume, - .suspend = ifx_spi_pm_suspend, - .runtime_resume = ifx_spi_pm_runtime_resume, - .runtime_suspend = ifx_spi_pm_runtime_suspend, - .runtime_idle = ifx_spi_pm_runtime_idle -}; - -static const struct spi_device_id ifx_id_table[] = { - {"ifx6160", 0}, - {"ifx6260", 0}, - { } -}; -MODULE_DEVICE_TABLE(spi, ifx_id_table); - -/* spi operations */ -static const struct spi_driver ifx_spi_driver_6160 = { - .driver = { - .name = "ifx6160", - .bus = &spi_bus_type, - .pm = &ifx_spi_pm, - .owner = THIS_MODULE}, - .probe = ifx_spi_spi_probe, - .shutdown = ifx_spi_spi_shutdown, - .remove = __devexit_p(ifx_spi_spi_remove), - .suspend = ifx_spi_spi_suspend, - .resume = ifx_spi_spi_resume, - .id_table = ifx_id_table -}; - -/** - * ifx_spi_exit - module exit - * - * Unload the module. - */ - -static void __exit ifx_spi_exit(void) -{ - /* unregister */ - tty_unregister_driver(tty_drv); - spi_unregister_driver((void *)&ifx_spi_driver_6160); -} - -/** - * ifx_spi_init - module entry point - * - * Initialise the SPI and tty interfaces for the IFX SPI driver - * We need to initialize upper-edge spi driver after the tty - * driver because otherwise the spi probe will race - */ - -static int __init ifx_spi_init(void) -{ - int result; - - tty_drv = alloc_tty_driver(1); - if (!tty_drv) { - pr_err("%s: alloc_tty_driver failed", DRVNAME); - return -ENOMEM; - } - - tty_drv->magic = TTY_DRIVER_MAGIC; - tty_drv->owner = THIS_MODULE; - tty_drv->driver_name = DRVNAME; - tty_drv->name = TTYNAME; - tty_drv->minor_start = IFX_SPI_TTY_ID; - tty_drv->num = 1; - tty_drv->type = TTY_DRIVER_TYPE_SERIAL; - tty_drv->subtype = SERIAL_TYPE_NORMAL; - tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - tty_drv->init_termios = tty_std_termios; - - tty_set_operations(tty_drv, &ifx_spi_serial_ops); - - result = tty_register_driver(tty_drv); - if (result) { - pr_err("%s: tty_register_driver failed(%d)", - DRVNAME, result); - put_tty_driver(tty_drv); - return result; - } - - result = spi_register_driver((void *)&ifx_spi_driver_6160); - if (result) { - pr_err("%s: spi_register_driver failed(%d)", - DRVNAME, result); - tty_unregister_driver(tty_drv); - } - return result; -} - -module_init(ifx_spi_init); -module_exit(ifx_spi_exit); - -MODULE_AUTHOR("Intel"); -MODULE_DESCRIPTION("IFX6x60 spi driver"); -MODULE_LICENSE("GPL"); -MODULE_INFO(Version, "0.1-IFX6x60"); diff --git a/drivers/serial/ifx6x60.h b/drivers/serial/ifx6x60.h deleted file mode 100644 index deb7b8d977dc..000000000000 --- a/drivers/serial/ifx6x60.h +++ /dev/null @@ -1,129 +0,0 @@ -/**************************************************************************** - * - * Driver for the IFX spi modem. - * - * Copyright (C) 2009, 2010 Intel Corp - * Jim Stanley - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA - * - * - * - *****************************************************************************/ -#ifndef _IFX6X60_H -#define _IFX6X60_H - -#define DRVNAME "ifx6x60" -#define TTYNAME "ttyIFX" - -/* #define IFX_THROTTLE_CODE */ - -#define IFX_SPI_MAX_MINORS 1 -#define IFX_SPI_TRANSFER_SIZE 2048 -#define IFX_SPI_FIFO_SIZE 4096 - -#define IFX_SPI_HEADER_OVERHEAD 4 -#define IFX_RESET_TIMEOUT msecs_to_jiffies(50) - -/* device flags bitfield definitions */ -#define IFX_SPI_STATE_PRESENT 0 -#define IFX_SPI_STATE_IO_IN_PROGRESS 1 -#define IFX_SPI_STATE_IO_READY 2 -#define IFX_SPI_STATE_TIMER_PENDING 3 - -/* flow control bitfields */ -#define IFX_SPI_DCD 0 -#define IFX_SPI_CTS 1 -#define IFX_SPI_DSR 2 -#define IFX_SPI_RI 3 -#define IFX_SPI_DTR 4 -#define IFX_SPI_RTS 5 -#define IFX_SPI_TX_FC 6 -#define IFX_SPI_RX_FC 7 -#define IFX_SPI_UPDATE 8 - -#define IFX_SPI_PAYLOAD_SIZE (IFX_SPI_TRANSFER_SIZE - \ - IFX_SPI_HEADER_OVERHEAD) - -#define IFX_SPI_IRQ_TYPE DETECT_EDGE_RISING -#define IFX_SPI_GPIO_TARGET 0 -#define IFX_SPI_GPIO0 0x105 - -#define IFX_SPI_STATUS_TIMEOUT (2000*HZ) - -/* values for bits in power status byte */ -#define IFX_SPI_POWER_DATA_PENDING 1 -#define IFX_SPI_POWER_SRDY 2 - -struct ifx_spi_device { - /* Our SPI device */ - struct spi_device *spi_dev; - - /* Port specific data */ - struct kfifo tx_fifo; - spinlock_t fifo_lock; - unsigned long signal_state; - - /* TTY Layer logic */ - struct tty_port tty_port; - struct device *tty_dev; - int minor; - - /* Low level I/O work */ - struct tasklet_struct io_work_tasklet; - unsigned long flags; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - - int is_6160; /* Modem type */ - - spinlock_t write_lock; - int write_pending; - spinlock_t power_lock; - unsigned char power_status; - - unsigned char *rx_buffer; - unsigned char *tx_buffer; - dma_addr_t rx_bus; - dma_addr_t tx_bus; - unsigned char spi_more; - unsigned char spi_slave_cts; - - struct timer_list spi_timer; - - struct spi_message spi_msg; - struct spi_transfer spi_xfer; - - struct { - /* gpio lines */ - unsigned short srdy; /* slave-ready gpio */ - unsigned short mrdy; /* master-ready gpio */ - unsigned short reset; /* modem-reset gpio */ - unsigned short po; /* modem-on gpio */ - unsigned short reset_out; /* modem-in-reset gpio */ - /* state/stats */ - int unack_srdy_int_nb; - } gpio; - - /* modem reset */ - unsigned long mdm_reset_state; -#define MR_START 0 -#define MR_INPROGRESS 1 -#define MR_COMPLETE 2 - wait_queue_head_t mdm_reset_wait; -}; - -#endif /* _IFX6X60_H */ diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c deleted file mode 100644 index dfcf4b1878aa..000000000000 --- a/drivers/serial/imx.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * linux/drivers/serial/imx.c - * - * Driver for Motorola IMX serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Author: Sascha Hauer - * Copyright (C) 2004 Pengutronix - * - * Copyright (C) 2009 emlix GmbH - * Author: Fabian Godehardt (added IrDA support for iMX) - * - * 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 - * - * [29-Mar-2005] Mike Lee - * Added hardware handshake - */ - -#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Register definitions */ -#define URXD0 0x0 /* Receiver Register */ -#define URTX0 0x40 /* Transmitter Register */ -#define UCR1 0x80 /* Control Register 1 */ -#define UCR2 0x84 /* Control Register 2 */ -#define UCR3 0x88 /* Control Register 3 */ -#define UCR4 0x8c /* Control Register 4 */ -#define UFCR 0x90 /* FIFO Control Register */ -#define USR1 0x94 /* Status Register 1 */ -#define USR2 0x98 /* Status Register 2 */ -#define UESC 0x9c /* Escape Character Register */ -#define UTIM 0xa0 /* Escape Timer Register */ -#define UBIR 0xa4 /* BRM Incremental Register */ -#define UBMR 0xa8 /* BRM Modulator Register */ -#define UBRC 0xac /* Baud Rate Count Register */ -#define MX2_ONEMS 0xb0 /* One Millisecond register */ -#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */ - -/* UART Control Register Bit Fields.*/ -#define URXD_CHARRDY (1<<15) -#define URXD_ERR (1<<14) -#define URXD_OVRRUN (1<<13) -#define URXD_FRMERR (1<<12) -#define URXD_BRK (1<<11) -#define URXD_PRERR (1<<10) -#define UCR1_ADEN (1<<15) /* Auto dectect interrupt */ -#define UCR1_ADBR (1<<14) /* Auto detect baud rate */ -#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ -#define UCR1_IDEN (1<<12) /* Idle condition interrupt */ -#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ -#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ -#define UCR1_IREN (1<<7) /* Infrared interface enable */ -#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ -#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ -#define UCR1_SNDBRK (1<<4) /* Send break */ -#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ -#define MX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, mx1 only */ -#define UCR1_DOZE (1<<1) /* Doze */ -#define UCR1_UARTEN (1<<0) /* UART enabled */ -#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ -#define UCR2_IRTS (1<<14) /* Ignore RTS pin */ -#define UCR2_CTSC (1<<13) /* CTS pin control */ -#define UCR2_CTS (1<<12) /* Clear to send */ -#define UCR2_ESCEN (1<<11) /* Escape enable */ -#define UCR2_PREN (1<<8) /* Parity enable */ -#define UCR2_PROE (1<<7) /* Parity odd/even */ -#define UCR2_STPB (1<<6) /* Stop */ -#define UCR2_WS (1<<5) /* Word size */ -#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ -#define UCR2_TXEN (1<<2) /* Transmitter enabled */ -#define UCR2_RXEN (1<<1) /* Receiver enabled */ -#define UCR2_SRST (1<<0) /* SW reset */ -#define UCR3_DTREN (1<<13) /* DTR interrupt enable */ -#define UCR3_PARERREN (1<<12) /* Parity enable */ -#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */ -#define UCR3_DSR (1<<10) /* Data set ready */ -#define UCR3_DCD (1<<9) /* Data carrier detect */ -#define UCR3_RI (1<<8) /* Ring indicator */ -#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */ -#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ -#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ -#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ -#define MX1_UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */ -#define MX1_UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */ -#define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ -#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ -#define UCR3_BPEN (1<<0) /* Preset registers enable */ -#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ -#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ -#define UCR4_INVR (1<<9) /* Inverted infrared reception */ -#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ -#define UCR4_WKEN (1<<7) /* Wake interrupt enable */ -#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ -#define UCR4_IRSC (1<<5) /* IR special case */ -#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ -#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ -#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ -#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ -#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ -#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ -#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7) -#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ -#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ -#define USR1_RTSS (1<<14) /* RTS pin status */ -#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */ -#define USR1_RTSD (1<<12) /* RTS delta */ -#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ -#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ -#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ -#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ -#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ -#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ -#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */ -#define USR2_ADET (1<<15) /* Auto baud rate detect complete */ -#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ -#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ -#define USR2_IDLE (1<<12) /* Idle condition */ -#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ -#define USR2_WAKE (1<<7) /* Wake */ -#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ -#define USR2_TXDC (1<<3) /* Transmitter complete */ -#define USR2_BRCD (1<<2) /* Break condition */ -#define USR2_ORE (1<<1) /* Overrun error */ -#define USR2_RDR (1<<0) /* Recv data ready */ -#define UTS_FRCPERR (1<<13) /* Force parity error */ -#define UTS_LOOP (1<<12) /* Loop tx and rx */ -#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ -#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ -#define UTS_TXFULL (1<<4) /* TxFIFO full */ -#define UTS_RXFULL (1<<3) /* RxFIFO full */ -#define UTS_SOFTRST (1<<0) /* Software reset */ - -/* We've been assigned a range on the "Low-density serial ports" major */ -#define SERIAL_IMX_MAJOR 207 -#define MINOR_START 16 -#define DEV_NAME "ttymxc" -#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS - -/* - * This determines how often we check the modem status signals - * for any change. They generally aren't connected to an IRQ - * so we have to poll them. We also check immediately before - * filling the TX fifo incase CTS has been dropped. - */ -#define MCTRL_TIMEOUT (250*HZ/1000) - -#define DRIVER_NAME "IMX-uart" - -#define UART_NR 8 - -struct imx_port { - struct uart_port port; - struct timer_list timer; - unsigned int old_status; - int txirq,rxirq,rtsirq; - unsigned int have_rtscts:1; - unsigned int use_irda:1; - unsigned int irda_inv_rx:1; - unsigned int irda_inv_tx:1; - unsigned short trcv_delay; /* transceiver delay */ - struct clk *clk; -}; - -#ifdef CONFIG_IRDA -#define USE_IRDA(sport) ((sport)->use_irda) -#else -#define USE_IRDA(sport) (0) -#endif - -/* - * Handle any change of modem status signal since we were last called. - */ -static void imx_mctrl_check(struct imx_port *sport) -{ - unsigned int status, changed; - - status = sport->port.ops->get_mctrl(&sport->port); - changed = status ^ sport->old_status; - - if (changed == 0) - return; - - sport->old_status = status; - - if (changed & TIOCM_RI) - sport->port.icount.rng++; - if (changed & TIOCM_DSR) - sport->port.icount.dsr++; - if (changed & TIOCM_CAR) - uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); - if (changed & TIOCM_CTS) - uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - - wake_up_interruptible(&sport->port.state->port.delta_msr_wait); -} - -/* - * This is our per-port timeout handler, for checking the - * modem status signals. - */ -static void imx_timeout(unsigned long data) -{ - struct imx_port *sport = (struct imx_port *)data; - unsigned long flags; - - if (sport->port.state) { - spin_lock_irqsave(&sport->port.lock, flags); - imx_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); - - mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); - } -} - -/* - * interrupts disabled on entry - */ -static void imx_stop_tx(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; - - if (USE_IRDA(sport)) { - /* half duplex - wait for end of transmission */ - int n = 256; - while ((--n > 0) && - !(readl(sport->port.membase + USR2) & USR2_TXDC)) { - udelay(5); - barrier(); - } - /* - * irda transceiver - wait a bit more to avoid - * cutoff, hardware dependent - */ - udelay(sport->trcv_delay); - - /* - * half duplex - reactivate receive mode, - * flush receive pipe echo crap - */ - if (readl(sport->port.membase + USR2) & USR2_TXDC) { - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN); - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR4); - temp &= ~(UCR4_TCEN); - writel(temp, sport->port.membase + UCR4); - - while (readl(sport->port.membase + URXD0) & - URXD_CHARRDY) - barrier(); - - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN; - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_DREN; - writel(temp, sport->port.membase + UCR4); - } - return; - } - - temp = readl(sport->port.membase + UCR1); - writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); -} - -/* - * interrupts disabled on entry - */ -static void imx_stop_rx(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; - - temp = readl(sport->port.membase + UCR2); - writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2); -} - -/* - * Set the modem control timer to fire immediately. - */ -static void imx_enable_ms(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - - mod_timer(&sport->timer, jiffies); -} - -static inline void imx_transmit_buffer(struct imx_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - - while (!uart_circ_empty(xmit) && - !(readl(sport->port.membase + UTS) & UTS_TXFULL)) { - /* send xmit->buf[xmit->tail] - * out the port here */ - writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - - if (uart_circ_empty(xmit)) - imx_stop_tx(&sport->port); -} - -/* - * interrupts disabled on entry - */ -static void imx_start_tx(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; - - if (USE_IRDA(sport)) { - /* half duplex in IrDA mode; have to disable receive mode */ - temp = readl(sport->port.membase + UCR4); - temp &= ~(UCR4_DREN); - writel(temp, sport->port.membase + UCR4); - - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_RRDYEN); - writel(temp, sport->port.membase + UCR1); - } - - temp = readl(sport->port.membase + UCR1); - writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); - - if (USE_IRDA(sport)) { - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_TRDYEN; - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_TCEN; - writel(temp, sport->port.membase + UCR4); - } - - if (readl(sport->port.membase + UTS) & UTS_TXEMPTY) - imx_transmit_buffer(sport); -} - -static irqreturn_t imx_rtsint(int irq, void *dev_id) -{ - struct imx_port *sport = dev_id; - unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; - unsigned long flags; - - spin_lock_irqsave(&sport->port.lock, flags); - - writel(USR1_RTSD, sport->port.membase + USR1); - uart_handle_cts_change(&sport->port, !!val); - wake_up_interruptible(&sport->port.state->port.delta_msr_wait); - - spin_unlock_irqrestore(&sport->port.lock, flags); - return IRQ_HANDLED; -} - -static irqreturn_t imx_txint(int irq, void *dev_id) -{ - struct imx_port *sport = dev_id; - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned long flags; - - spin_lock_irqsave(&sport->port.lock,flags); - if (sport->port.x_char) - { - /* Send next char */ - writel(sport->port.x_char, sport->port.membase + URTX0); - goto out; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - imx_stop_tx(&sport->port); - goto out; - } - - imx_transmit_buffer(sport); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - -out: - spin_unlock_irqrestore(&sport->port.lock,flags); - return IRQ_HANDLED; -} - -static irqreturn_t imx_rxint(int irq, void *dev_id) -{ - struct imx_port *sport = dev_id; - unsigned int rx,flg,ignored = 0; - struct tty_struct *tty = sport->port.state->port.tty; - unsigned long flags, temp; - - spin_lock_irqsave(&sport->port.lock,flags); - - while (readl(sport->port.membase + USR2) & USR2_RDR) { - flg = TTY_NORMAL; - sport->port.icount.rx++; - - rx = readl(sport->port.membase + URXD0); - - temp = readl(sport->port.membase + USR2); - if (temp & USR2_BRCD) { - writel(USR2_BRCD, sport->port.membase + USR2); - if (uart_handle_break(&sport->port)) - continue; - } - - if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) - continue; - - if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) { - if (rx & URXD_PRERR) - sport->port.icount.parity++; - else if (rx & URXD_FRMERR) - sport->port.icount.frame++; - if (rx & URXD_OVRRUN) - sport->port.icount.overrun++; - - if (rx & sport->port.ignore_status_mask) { - if (++ignored > 100) - goto out; - continue; - } - - rx &= sport->port.read_status_mask; - - if (rx & URXD_PRERR) - flg = TTY_PARITY; - else if (rx & URXD_FRMERR) - flg = TTY_FRAME; - if (rx & URXD_OVRRUN) - flg = TTY_OVERRUN; - -#ifdef SUPPORT_SYSRQ - sport->port.sysrq = 0; -#endif - } - - tty_insert_flip_char(tty, rx, flg); - } - -out: - spin_unlock_irqrestore(&sport->port.lock,flags); - tty_flip_buffer_push(tty); - return IRQ_HANDLED; -} - -static irqreturn_t imx_int(int irq, void *dev_id) -{ - struct imx_port *sport = dev_id; - unsigned int sts; - - sts = readl(sport->port.membase + USR1); - - if (sts & USR1_RRDY) - imx_rxint(irq, dev_id); - - if (sts & USR1_TRDY && - readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) - imx_txint(irq, dev_id); - - if (sts & USR1_RTSD) - imx_rtsint(irq, dev_id); - - return IRQ_HANDLED; -} - -/* - * Return TIOCSER_TEMT when transmitter is not busy. - */ -static unsigned int imx_tx_empty(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - - return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; -} - -/* - * We have a modem side uart, so the meanings of RTS and CTS are inverted. - */ -static unsigned int imx_get_mctrl(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned int tmp = TIOCM_DSR | TIOCM_CAR; - - if (readl(sport->port.membase + USR1) & USR1_RTSS) - tmp |= TIOCM_CTS; - - if (readl(sport->port.membase + UCR2) & UCR2_CTS) - tmp |= TIOCM_RTS; - - return tmp; -} - -static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; - - temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; - - if (mctrl & TIOCM_RTS) - temp |= UCR2_CTS; - - writel(temp, sport->port.membase + UCR2); -} - -/* - * Interrupts always disabled. - */ -static void imx_break_ctl(struct uart_port *port, int break_state) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long flags, temp; - - spin_lock_irqsave(&sport->port.lock, flags); - - temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK; - - if ( break_state != 0 ) - temp |= UCR1_SNDBRK; - - writel(temp, sport->port.membase + UCR1); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -#define TXTL 2 /* reset default */ -#define RXTL 1 /* reset default */ - -static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) -{ - unsigned int val; - unsigned int ufcr_rfdiv; - - /* set receiver / transmitter trigger level. - * RFDIV is set such way to satisfy requested uartclk value - */ - val = TXTL << 10 | RXTL; - ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2) - / sport->port.uartclk; - - if(!ufcr_rfdiv) - ufcr_rfdiv = 1; - - val |= UFCR_RFDIV_REG(ufcr_rfdiv); - - writel(val, sport->port.membase + UFCR); - - return 0; -} - -/* half the RX buffer size */ -#define CTSTL 16 - -static int imx_startup(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - int retval; - unsigned long flags, temp; - - imx_setup_ufcr(sport, 0); - - /* disable the DREN bit (Data Ready interrupt enable) before - * requesting IRQs - */ - temp = readl(sport->port.membase + UCR4); - - if (USE_IRDA(sport)) - temp |= UCR4_IRSC; - - /* set the trigger level for CTS */ - temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); - temp |= CTSTL<< UCR4_CTSTL_SHF; - - writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); - - if (USE_IRDA(sport)) { - /* reset fifo's and state machines */ - int i = 100; - temp = readl(sport->port.membase + UCR2); - temp &= ~UCR2_SRST; - writel(temp, sport->port.membase + UCR2); - while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && - (--i > 0)) { - udelay(1); - } - } - - /* - * Allocate the IRQ(s) i.MX1 has three interrupts whereas later - * chips only have one interrupt. - */ - if (sport->txirq > 0) { - retval = request_irq(sport->rxirq, imx_rxint, 0, - DRIVER_NAME, sport); - if (retval) - goto error_out1; - - retval = request_irq(sport->txirq, imx_txint, 0, - DRIVER_NAME, sport); - if (retval) - goto error_out2; - - /* do not use RTS IRQ on IrDA */ - if (!USE_IRDA(sport)) { - retval = request_irq(sport->rtsirq, imx_rtsint, - (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : - IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING, - DRIVER_NAME, sport); - if (retval) - goto error_out3; - } - } else { - retval = request_irq(sport->port.irq, imx_int, 0, - DRIVER_NAME, sport); - if (retval) { - free_irq(sport->port.irq, sport); - goto error_out1; - } - } - - /* - * Finally, clear and enable interrupts - */ - writel(USR1_RTSD, sport->port.membase + USR1); - - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; - - if (USE_IRDA(sport)) { - temp |= UCR1_IREN; - temp &= ~(UCR1_RTSDEN); - } - - writel(temp, sport->port.membase + UCR1); - - temp = readl(sport->port.membase + UCR2); - temp |= (UCR2_RXEN | UCR2_TXEN); - writel(temp, sport->port.membase + UCR2); - - if (USE_IRDA(sport)) { - /* clear RX-FIFO */ - int i = 64; - while ((--i > 0) && - (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) { - barrier(); - } - } - - if (!cpu_is_mx1()) { - temp = readl(sport->port.membase + UCR3); - temp |= MX2_UCR3_RXDMUXSEL; - writel(temp, sport->port.membase + UCR3); - } - - if (USE_IRDA(sport)) { - temp = readl(sport->port.membase + UCR4); - if (sport->irda_inv_rx) - temp |= UCR4_INVR; - else - temp &= ~(UCR4_INVR); - writel(temp | UCR4_DREN, sport->port.membase + UCR4); - - temp = readl(sport->port.membase + UCR3); - if (sport->irda_inv_tx) - temp |= UCR3_INVT; - else - temp &= ~(UCR3_INVT); - writel(temp, sport->port.membase + UCR3); - } - - /* - * Enable modem status interrupts - */ - spin_lock_irqsave(&sport->port.lock,flags); - imx_enable_ms(&sport->port); - spin_unlock_irqrestore(&sport->port.lock,flags); - - if (USE_IRDA(sport)) { - struct imxuart_platform_data *pdata; - pdata = sport->port.dev->platform_data; - sport->irda_inv_rx = pdata->irda_inv_rx; - sport->irda_inv_tx = pdata->irda_inv_tx; - sport->trcv_delay = pdata->transceiver_delay; - if (pdata->irda_enable) - pdata->irda_enable(1); - } - - return 0; - -error_out3: - if (sport->txirq) - free_irq(sport->txirq, sport); -error_out2: - if (sport->rxirq) - free_irq(sport->rxirq, sport); -error_out1: - return retval; -} - -static void imx_shutdown(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long temp; - - temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_TXEN); - writel(temp, sport->port.membase + UCR2); - - if (USE_IRDA(sport)) { - struct imxuart_platform_data *pdata; - pdata = sport->port.dev->platform_data; - if (pdata->irda_enable) - pdata->irda_enable(0); - } - - /* - * Stop our timer. - */ - del_timer_sync(&sport->timer); - - /* - * Free the interrupts - */ - if (sport->txirq > 0) { - if (!USE_IRDA(sport)) - free_irq(sport->rtsirq, sport); - free_irq(sport->txirq, sport); - free_irq(sport->rxirq, sport); - } else - free_irq(sport->port.irq, sport); - - /* - * Disable all interrupts, port and break condition. - */ - - temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); - if (USE_IRDA(sport)) - temp &= ~(UCR1_IREN); - - writel(temp, sport->port.membase + UCR1); -} - -static void -imx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned long flags; - unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; - unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - unsigned int div, ufcr; - unsigned long num, denom; - uint64_t tdiv64; - - /* - * If we don't support modem control lines, don't allow - * these to be set. - */ - if (0) { - termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); - termios->c_cflag |= CLOCAL; - } - - /* - * We only support CS7 and CS8. - */ - while ((termios->c_cflag & CSIZE) != CS7 && - (termios->c_cflag & CSIZE) != CS8) { - termios->c_cflag &= ~CSIZE; - termios->c_cflag |= old_csize; - old_csize = CS8; - } - - if ((termios->c_cflag & CSIZE) == CS8) - ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; - else - ucr2 = UCR2_SRST | UCR2_IRTS; - - if (termios->c_cflag & CRTSCTS) { - if( sport->have_rtscts ) { - ucr2 &= ~UCR2_IRTS; - ucr2 |= UCR2_CTSC; - } else { - termios->c_cflag &= ~CRTSCTS; - } - } - - if (termios->c_cflag & CSTOPB) - ucr2 |= UCR2_STPB; - if (termios->c_cflag & PARENB) { - ucr2 |= UCR2_PREN; - if (termios->c_cflag & PARODD) - ucr2 |= UCR2_PROE; - } - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); - quot = uart_get_divisor(port, baud); - - spin_lock_irqsave(&sport->port.lock, flags); - - sport->port.read_status_mask = 0; - if (termios->c_iflag & INPCK) - sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR); - if (termios->c_iflag & (BRKINT | PARMRK)) - sport->port.read_status_mask |= URXD_BRK; - - /* - * Characters to ignore - */ - sport->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= URXD_PRERR; - if (termios->c_iflag & IGNBRK) { - sport->port.ignore_status_mask |= URXD_BRK; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= URXD_OVRRUN; - } - - del_timer_sync(&sport->timer); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * disable interrupts and drain transmitter - */ - old_ucr1 = readl(sport->port.membase + UCR1); - writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), - sport->port.membase + UCR1); - - while ( !(readl(sport->port.membase + USR2) & USR2_TXDC)) - barrier(); - - /* then, disable everything */ - old_txrxen = readl(sport->port.membase + UCR2); - writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN), - sport->port.membase + UCR2); - old_txrxen &= (UCR2_TXEN | UCR2_RXEN); - - if (USE_IRDA(sport)) { - /* - * use maximum available submodule frequency to - * avoid missing short pulses due to low sampling rate - */ - div = 1; - } else { - div = sport->port.uartclk / (baud * 16); - if (div > 7) - div = 7; - if (!div) - div = 1; - } - - rational_best_approximation(16 * div * baud, sport->port.uartclk, - 1 << 16, 1 << 16, &num, &denom); - - tdiv64 = sport->port.uartclk; - tdiv64 *= num; - do_div(tdiv64, denom * 16 * div); - tty_termios_encode_baud_rate(termios, - (speed_t)tdiv64, (speed_t)tdiv64); - - num -= 1; - denom -= 1; - - ufcr = readl(sport->port.membase + UFCR); - ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); - writel(ufcr, sport->port.membase + UFCR); - - writel(num, sport->port.membase + UBIR); - writel(denom, sport->port.membase + UBMR); - - if (!cpu_is_mx1()) - writel(sport->port.uartclk / div / 1000, - sport->port.membase + MX2_ONEMS); - - writel(old_ucr1, sport->port.membase + UCR1); - - /* set the parity, stop bits and data size */ - writel(ucr2 | old_txrxen, sport->port.membase + UCR2); - - if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) - imx_enable_ms(&sport->port); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static const char *imx_type(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - - return sport->port.type == PORT_IMX ? "IMX" : NULL; -} - -/* - * Release the memory region(s) being used by 'port'. - */ -static void imx_release_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *mmres; - - mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(mmres->start, mmres->end - mmres->start + 1); -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int imx_request_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *mmres; - void *ret; - - mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mmres) - return -ENODEV; - - ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, - "imx-uart"); - - return ret ? 0 : -EBUSY; -} - -/* - * Configure/autoconfigure the port. - */ -static void imx_config_port(struct uart_port *port, int flags) -{ - struct imx_port *sport = (struct imx_port *)port; - - if (flags & UART_CONFIG_TYPE && - imx_request_port(&sport->port) == 0) - sport->port.type = PORT_IMX; -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - * The only change we allow are to the flags and type, and - * even then only between PORT_IMX and PORT_UNKNOWN - */ -static int -imx_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct imx_port *sport = (struct imx_port *)port; - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX) - ret = -EINVAL; - if (sport->port.irq != ser->irq) - ret = -EINVAL; - if (ser->io_type != UPIO_MEM) - ret = -EINVAL; - if (sport->port.uartclk / 16 != ser->baud_base) - ret = -EINVAL; - if ((void *)sport->port.mapbase != ser->iomem_base) - ret = -EINVAL; - if (sport->port.iobase != ser->port) - ret = -EINVAL; - if (ser->hub6 != 0) - ret = -EINVAL; - return ret; -} - -static struct uart_ops imx_pops = { - .tx_empty = imx_tx_empty, - .set_mctrl = imx_set_mctrl, - .get_mctrl = imx_get_mctrl, - .stop_tx = imx_stop_tx, - .start_tx = imx_start_tx, - .stop_rx = imx_stop_rx, - .enable_ms = imx_enable_ms, - .break_ctl = imx_break_ctl, - .startup = imx_startup, - .shutdown = imx_shutdown, - .set_termios = imx_set_termios, - .type = imx_type, - .release_port = imx_release_port, - .request_port = imx_request_port, - .config_port = imx_config_port, - .verify_port = imx_verify_port, -}; - -static struct imx_port *imx_ports[UART_NR]; - -#ifdef CONFIG_SERIAL_IMX_CONSOLE -static void imx_console_putchar(struct uart_port *port, int ch) -{ - struct imx_port *sport = (struct imx_port *)port; - - while (readl(sport->port.membase + UTS) & UTS_TXFULL) - barrier(); - - writel(ch, sport->port.membase + URTX0); -} - -/* - * Interrupts are disabled on entering - */ -static void -imx_console_write(struct console *co, const char *s, unsigned int count) -{ - struct imx_port *sport = imx_ports[co->index]; - unsigned int old_ucr1, old_ucr2, ucr1; - - /* - * First, save UCR1/2 and then disable interrupts - */ - ucr1 = old_ucr1 = readl(sport->port.membase + UCR1); - old_ucr2 = readl(sport->port.membase + UCR2); - - if (cpu_is_mx1()) - ucr1 |= MX1_UCR1_UARTCLKEN; - ucr1 |= UCR1_UARTEN; - ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN); - - writel(ucr1, sport->port.membase + UCR1); - - writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); - - uart_console_write(&sport->port, s, count, imx_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore UCR1/2 - */ - while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); - - writel(old_ucr1, sport->port.membase + UCR1); - writel(old_ucr2, sport->port.membase + UCR2); -} - -/* - * If the port was already initialised (eg, by a boot loader), - * try to determine the current setup. - */ -static void __init -imx_console_get_options(struct imx_port *sport, int *baud, - int *parity, int *bits) -{ - - if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) { - /* ok, the port was enabled */ - unsigned int ucr2, ubir,ubmr, uartclk; - unsigned int baud_raw; - unsigned int ucfr_rfdiv; - - ucr2 = readl(sport->port.membase + UCR2); - - *parity = 'n'; - if (ucr2 & UCR2_PREN) { - if (ucr2 & UCR2_PROE) - *parity = 'o'; - else - *parity = 'e'; - } - - if (ucr2 & UCR2_WS) - *bits = 8; - else - *bits = 7; - - ubir = readl(sport->port.membase + UBIR) & 0xffff; - ubmr = readl(sport->port.membase + UBMR) & 0xffff; - - ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7; - if (ucfr_rfdiv == 6) - ucfr_rfdiv = 7; - else - ucfr_rfdiv = 6 - ucfr_rfdiv; - - uartclk = clk_get_rate(sport->clk); - uartclk /= ucfr_rfdiv; - - { /* - * The next code provides exact computation of - * baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1)) - * without need of float support or long long division, - * which would be required to prevent 32bit arithmetic overflow - */ - unsigned int mul = ubir + 1; - unsigned int div = 16 * (ubmr + 1); - unsigned int rem = uartclk % div; - - baud_raw = (uartclk / div) * mul; - baud_raw += (rem * mul + div / 2) / div; - *baud = (baud_raw + 50) / 100 * 100; - } - - if(*baud != baud_raw) - printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n", - baud_raw, *baud); - } -} - -static int __init -imx_console_setup(struct console *co, char *options) -{ - struct imx_port *sport; - int baud = 9600; - 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. - */ - if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports)) - co->index = 0; - sport = imx_ports[co->index]; - if(sport == NULL) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - imx_console_get_options(sport, &baud, &parity, &bits); - - imx_setup_ufcr(sport, 0); - - return uart_set_options(&sport->port, co, baud, parity, bits, flow); -} - -static struct uart_driver imx_reg; -static struct console imx_console = { - .name = DEV_NAME, - .write = imx_console_write, - .device = uart_console_device, - .setup = imx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &imx_reg, -}; - -#define IMX_CONSOLE &imx_console -#else -#define IMX_CONSOLE NULL -#endif - -static struct uart_driver imx_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = DEV_NAME, - .major = SERIAL_IMX_MAJOR, - .minor = MINOR_START, - .nr = ARRAY_SIZE(imx_ports), - .cons = IMX_CONSOLE, -}; - -static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) -{ - struct imx_port *sport = platform_get_drvdata(dev); - - if (sport) - uart_suspend_port(&imx_reg, &sport->port); - - return 0; -} - -static int serial_imx_resume(struct platform_device *dev) -{ - struct imx_port *sport = platform_get_drvdata(dev); - - if (sport) - uart_resume_port(&imx_reg, &sport->port); - - return 0; -} - -static int serial_imx_probe(struct platform_device *pdev) -{ - struct imx_port *sport; - struct imxuart_platform_data *pdata; - void __iomem *base; - int ret = 0; - struct resource *res; - - sport = kzalloc(sizeof(*sport), GFP_KERNEL); - if (!sport) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto free; - } - - base = ioremap(res->start, PAGE_SIZE); - if (!base) { - ret = -ENOMEM; - goto free; - } - - sport->port.dev = &pdev->dev; - sport->port.mapbase = res->start; - sport->port.membase = base; - sport->port.type = PORT_IMX, - sport->port.iotype = UPIO_MEM; - sport->port.irq = platform_get_irq(pdev, 0); - sport->rxirq = platform_get_irq(pdev, 0); - sport->txirq = platform_get_irq(pdev, 1); - sport->rtsirq = platform_get_irq(pdev, 2); - sport->port.fifosize = 32; - sport->port.ops = &imx_pops; - sport->port.flags = UPF_BOOT_AUTOCONF; - sport->port.line = pdev->id; - init_timer(&sport->timer); - sport->timer.function = imx_timeout; - sport->timer.data = (unsigned long)sport; - - sport->clk = clk_get(&pdev->dev, "uart"); - if (IS_ERR(sport->clk)) { - ret = PTR_ERR(sport->clk); - goto unmap; - } - clk_enable(sport->clk); - - sport->port.uartclk = clk_get_rate(sport->clk); - - imx_ports[pdev->id] = sport; - - pdata = pdev->dev.platform_data; - if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) - sport->have_rtscts = 1; - -#ifdef CONFIG_IRDA - if (pdata && (pdata->flags & IMXUART_IRDA)) - sport->use_irda = 1; -#endif - - if (pdata && pdata->init) { - ret = pdata->init(pdev); - if (ret) - goto clkput; - } - - ret = uart_add_one_port(&imx_reg, &sport->port); - if (ret) - goto deinit; - platform_set_drvdata(pdev, &sport->port); - - return 0; -deinit: - if (pdata && pdata->exit) - pdata->exit(pdev); -clkput: - clk_put(sport->clk); - clk_disable(sport->clk); -unmap: - iounmap(sport->port.membase); -free: - kfree(sport); - - return ret; -} - -static int serial_imx_remove(struct platform_device *pdev) -{ - struct imxuart_platform_data *pdata; - struct imx_port *sport = platform_get_drvdata(pdev); - - pdata = pdev->dev.platform_data; - - platform_set_drvdata(pdev, NULL); - - if (sport) { - uart_remove_one_port(&imx_reg, &sport->port); - clk_put(sport->clk); - } - - clk_disable(sport->clk); - - if (pdata && pdata->exit) - pdata->exit(pdev); - - iounmap(sport->port.membase); - kfree(sport); - - return 0; -} - -static struct platform_driver serial_imx_driver = { - .probe = serial_imx_probe, - .remove = serial_imx_remove, - - .suspend = serial_imx_suspend, - .resume = serial_imx_resume, - .driver = { - .name = "imx-uart", - .owner = THIS_MODULE, - }, -}; - -static int __init imx_serial_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: IMX driver\n"); - - ret = uart_register_driver(&imx_reg); - if (ret) - return ret; - - ret = platform_driver_register(&serial_imx_driver); - if (ret != 0) - uart_unregister_driver(&imx_reg); - - return 0; -} - -static void __exit imx_serial_exit(void) -{ - platform_driver_unregister(&serial_imx_driver); - uart_unregister_driver(&imx_reg); -} - -module_init(imx_serial_init); -module_exit(imx_serial_exit); - -MODULE_AUTHOR("Sascha Hauer"); -MODULE_DESCRIPTION("IMX generic serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-uart"); diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c deleted file mode 100644 index ee43efc7bdcc..000000000000 --- a/drivers/serial/ioc3_serial.c +++ /dev/null @@ -1,2199 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved. - */ - -/* - * This file contains a module version of the ioc3 serial driver. This - * includes all the support functions needed (support functions, etc.) - * and the serial driver itself. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Interesting things about the ioc3 - */ - -#define LOGICAL_PORTS 2 /* rs232(0) and rs422(1) */ -#define PORTS_PER_CARD 2 -#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS) -#define MAX_CARDS 8 -#define MAX_LOGICAL_PORTS (LOGICAL_PORTS_PER_CARD * MAX_CARDS) - -/* determine given the sio_ir what port it applies to */ -#define GET_PORT_FROM_SIO_IR(_x) (_x & SIO_IR_SA) ? 0 : 1 - - -/* - * we have 2 logical ports (rs232, rs422) for each physical port - * evens are rs232, odds are rs422 - */ -#define GET_PHYSICAL_PORT(_x) ((_x) >> 1) -#define GET_LOGICAL_PORT(_x) ((_x) & 1) -#define IS_PHYSICAL_PORT(_x) !((_x) & 1) -#define IS_RS232(_x) !((_x) & 1) - -static unsigned int Num_of_ioc3_cards; -static unsigned int Submodule_slot; - -/* defining this will get you LOTS of great debug info */ -//#define DEBUG_INTERRUPTS -#define DPRINT_CONFIG(_x...) ; -//#define DPRINT_CONFIG(_x...) printk _x -#define NOT_PROGRESS() ; -//#define NOT_PROGRESS() printk("%s : fails %d\n", __func__, __LINE__) - -/* number of characters we want to transmit to the lower level at a time */ -#define MAX_CHARS 256 -#define FIFO_SIZE (MAX_CHARS-1) /* it's a uchar */ - -/* Device name we're using */ -#define DEVICE_NAME "ttySIOC" -#define DEVICE_MAJOR 204 -#define DEVICE_MINOR 116 - -/* flags for next_char_state */ -#define NCS_BREAK 0x1 -#define NCS_PARITY 0x2 -#define NCS_FRAMING 0x4 -#define NCS_OVERRUN 0x8 - -/* cause we need SOME parameters ... */ -#define MIN_BAUD_SUPPORTED 1200 -#define MAX_BAUD_SUPPORTED 115200 - -/* protocol types supported */ -#define PROTO_RS232 0 -#define PROTO_RS422 1 - -/* Notification types */ -#define N_DATA_READY 0x01 -#define N_OUTPUT_LOWAT 0x02 -#define N_BREAK 0x04 -#define N_PARITY_ERROR 0x08 -#define N_FRAMING_ERROR 0x10 -#define N_OVERRUN_ERROR 0x20 -#define N_DDCD 0x40 -#define N_DCTS 0x80 - -#define N_ALL_INPUT (N_DATA_READY | N_BREAK \ - | N_PARITY_ERROR | N_FRAMING_ERROR \ - | N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define N_ALL_OUTPUT N_OUTPUT_LOWAT - -#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR \ - | N_OVERRUN_ERROR) - -#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK \ - | N_PARITY_ERROR | N_FRAMING_ERROR \ - | N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define SER_CLK_SPEED(prediv) ((22000000 << 1) / prediv) -#define SER_DIVISOR(x, clk) (((clk) + (x) * 8) / ((x) * 16)) -#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) - -/* Some masks */ -#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ - | UART_LCR_WLEN7 | UART_LCR_WLEN8) -#define LCR_MASK_STOP_BITS (UART_LCR_STOP) - -#define PENDING(_a, _p) (readl(&(_p)->vma->sio_ir) & (_a)->ic_enable) - -#define RING_BUF_SIZE 4096 -#define BUF_SIZE_BIT SBBR_L_SIZE -#define PROD_CONS_MASK PROD_CONS_PTR_4K - -#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) - -/* driver specific - one per card */ -struct ioc3_card { - struct { - /* uart ports are allocated here */ - struct uart_port icp_uart_port[LOGICAL_PORTS]; - /* the ioc3_port used for this port */ - struct ioc3_port *icp_port; - } ic_port[PORTS_PER_CARD]; - /* currently enabled interrupts */ - uint32_t ic_enable; -}; - -/* Local port info for each IOC3 serial port */ -struct ioc3_port { - /* handy reference material */ - struct uart_port *ip_port; - struct ioc3_card *ip_card; - struct ioc3_driver_data *ip_idd; - struct ioc3_submodule *ip_is; - - /* pci mem addresses for this port */ - struct ioc3_serialregs __iomem *ip_serial_regs; - struct ioc3_uartregs __iomem *ip_uart_regs; - - /* Ring buffer page for this port */ - dma_addr_t ip_dma_ringbuf; - /* vaddr of ring buffer */ - struct ring_buffer *ip_cpu_ringbuf; - - /* Rings for this port */ - struct ring *ip_inring; - struct ring *ip_outring; - - /* Hook to port specific values */ - struct port_hooks *ip_hooks; - - spinlock_t ip_lock; - - /* Various rx/tx parameters */ - int ip_baud; - int ip_tx_lowat; - int ip_rx_timeout; - - /* Copy of notification bits */ - int ip_notify; - - /* Shadow copies of various registers so we don't need to PIO - * read them constantly - */ - uint32_t ip_sscr; - uint32_t ip_tx_prod; - uint32_t ip_rx_cons; - unsigned char ip_flags; -}; - -/* tx low water mark. We need to notify the driver whenever tx is getting - * close to empty so it can refill the tx buffer and keep things going. - * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll - * have no trouble getting in more chars in time (I certainly hope so). - */ -#define TX_LOWAT_LATENCY 1000 -#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY) -#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) - -/* Flags per port */ -#define INPUT_HIGH 0x01 - /* used to signify that we have turned off the rx_high - * temporarily - we need to drain the fifo and don't - * want to get blasted with interrupts. - */ -#define DCD_ON 0x02 - /* DCD state is on */ -#define LOWAT_WRITTEN 0x04 -#define READ_ABORTED 0x08 - /* the read was aborted - used to avaoid infinate looping - * in the interrupt handler - */ -#define INPUT_ENABLE 0x10 - -/* Since each port has different register offsets and bitmasks - * for everything, we'll store those that we need in tables so we - * don't have to be constantly checking the port we are dealing with. - */ -struct port_hooks { - uint32_t intr_delta_dcd; - uint32_t intr_delta_cts; - uint32_t intr_tx_mt; - uint32_t intr_rx_timer; - uint32_t intr_rx_high; - uint32_t intr_tx_explicit; - uint32_t intr_clear; - uint32_t intr_all; - char rs422_select_pin; -}; - -static struct port_hooks hooks_array[PORTS_PER_CARD] = { - /* values for port A */ - { - .intr_delta_dcd = SIO_IR_SA_DELTA_DCD, - .intr_delta_cts = SIO_IR_SA_DELTA_CTS, - .intr_tx_mt = SIO_IR_SA_TX_MT, - .intr_rx_timer = SIO_IR_SA_RX_TIMER, - .intr_rx_high = SIO_IR_SA_RX_HIGH, - .intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT, - .intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL - | SIO_IR_SA_RX_HIGH - | SIO_IR_SA_RX_TIMER - | SIO_IR_SA_DELTA_DCD - | SIO_IR_SA_DELTA_CTS - | SIO_IR_SA_INT - | SIO_IR_SA_TX_EXPLICIT - | SIO_IR_SA_MEMERR), - .intr_all = SIO_IR_SA, - .rs422_select_pin = GPPR_UARTA_MODESEL_PIN, - }, - - /* values for port B */ - { - .intr_delta_dcd = SIO_IR_SB_DELTA_DCD, - .intr_delta_cts = SIO_IR_SB_DELTA_CTS, - .intr_tx_mt = SIO_IR_SB_TX_MT, - .intr_rx_timer = SIO_IR_SB_RX_TIMER, - .intr_rx_high = SIO_IR_SB_RX_HIGH, - .intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT, - .intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL - | SIO_IR_SB_RX_HIGH - | SIO_IR_SB_RX_TIMER - | SIO_IR_SB_DELTA_DCD - | SIO_IR_SB_DELTA_CTS - | SIO_IR_SB_INT - | SIO_IR_SB_TX_EXPLICIT - | SIO_IR_SB_MEMERR), - .intr_all = SIO_IR_SB, - .rs422_select_pin = GPPR_UARTB_MODESEL_PIN, - } -}; - -struct ring_entry { - union { - struct { - uint32_t alldata; - uint32_t allsc; - } all; - struct { - char data[4]; /* data bytes */ - char sc[4]; /* status/control */ - } s; - } u; -}; - -/* Test the valid bits in any of the 4 sc chars using "allsc" member */ -#define RING_ANY_VALID \ - ((uint32_t)(RXSB_MODEM_VALID | RXSB_DATA_VALID) * 0x01010101) - -#define ring_sc u.s.sc -#define ring_data u.s.data -#define ring_allsc u.all.allsc - -/* Number of entries per ring buffer. */ -#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) - -/* An individual ring */ -struct ring { - struct ring_entry entries[ENTRIES_PER_RING]; -}; - -/* The whole enchilada */ -struct ring_buffer { - struct ring TX_A; - struct ring RX_A; - struct ring TX_B; - struct ring RX_B; -}; - -/* Get a ring from a port struct */ -#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) - -/* for Infinite loop detection */ -#define MAXITER 10000000 - - -/** - * set_baud - Baud rate setting code - * @port: port to set - * @baud: baud rate to use - */ -static int set_baud(struct ioc3_port *port, int baud) -{ - int divisor; - int actual_baud; - int diff; - int lcr, prediv; - struct ioc3_uartregs __iomem *uart; - - for (prediv = 6; prediv < 64; prediv++) { - divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv)); - if (!divisor) - continue; /* invalid divisor */ - actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv)); - - diff = actual_baud - baud; - if (diff < 0) - diff = -diff; - - /* if we're within 1% we've found a match */ - if (diff * 100 <= actual_baud) - break; - } - - /* if the above loop completed, we didn't match - * the baud rate. give up. - */ - if (prediv == 64) { - NOT_PROGRESS(); - return 1; - } - - uart = port->ip_uart_regs; - lcr = readb(&uart->iu_lcr); - - writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr); - writeb((unsigned char)divisor, &uart->iu_dll); - writeb((unsigned char)(divisor >> 8), &uart->iu_dlm); - writeb((unsigned char)prediv, &uart->iu_scr); - writeb((unsigned char)lcr, &uart->iu_lcr); - - return 0; -} - -/** - * get_ioc3_port - given a uart port, return the control structure - * @the_port: uart port to find - */ -static struct ioc3_port *get_ioc3_port(struct uart_port *the_port) -{ - struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev); - struct ioc3_card *card_ptr = idd->data[Submodule_slot]; - int ii, jj; - - if (!card_ptr) { - NOT_PROGRESS(); - return NULL; - } - for (ii = 0; ii < PORTS_PER_CARD; ii++) { - for (jj = 0; jj < LOGICAL_PORTS; jj++) { - if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj]) - return card_ptr->ic_port[ii].icp_port; - } - } - NOT_PROGRESS(); - return NULL; -} - -/** - * port_init - Initialize the sio and ioc3 hardware for a given port - * called per port from attach... - * @port: port to initialize - */ -static int inline port_init(struct ioc3_port *port) -{ - uint32_t sio_cr; - struct port_hooks *hooks = port->ip_hooks; - struct ioc3_uartregs __iomem *uart; - int reset_loop_counter = 0xfffff; - struct ioc3_driver_data *idd = port->ip_idd; - - /* Idle the IOC3 serial interface */ - writel(SSCR_RESET, &port->ip_serial_regs->sscr); - - /* Wait until any pending bus activity for this port has ceased */ - do { - sio_cr = readl(&idd->vma->sio_cr); - if (reset_loop_counter-- <= 0) { - printk(KERN_WARNING - "IOC3 unable to come out of reset" - " scr 0x%x\n", sio_cr); - return -1; - } - } while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) && - (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA) - || sio_cr == SIO_CR_ARB_DIAG_TXB - || sio_cr == SIO_CR_ARB_DIAG_RXA - || sio_cr == SIO_CR_ARB_DIAG_RXB)); - - /* Finish reset sequence */ - writel(0, &port->ip_serial_regs->sscr); - - /* Once RESET is done, reload cached tx_prod and rx_cons values - * and set rings to empty by making prod == cons - */ - port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); - port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); - - /* Disable interrupts for this 16550 */ - uart = port->ip_uart_regs; - writeb(0, &uart->iu_lcr); - writeb(0, &uart->iu_ier); - - /* Set the default baud */ - set_baud(port, port->ip_baud); - - /* Set line control to 8 bits no parity */ - writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Enable the FIFOs */ - writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr); - /* then reset 16550 FIFOs */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - &uart->iu_fcr); - - /* Clear modem control register */ - writeb(0, &uart->iu_mcr); - - /* Clear deltas in modem status register */ - writel(0, &port->ip_serial_regs->shadow); - - /* Only do this once per port pair */ - if (port->ip_hooks == &hooks_array[0]) { - unsigned long ring_pci_addr; - uint32_t __iomem *sbbr_l, *sbbr_h; - - sbbr_l = &idd->vma->sbbr_l; - sbbr_h = &idd->vma->sbbr_h; - ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; - DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n", - __func__, (void *)ring_pci_addr)); - - writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h); - writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l); - } - - /* Set the receive timeout value to 10 msec */ - writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr); - - /* Set rx threshold, enable DMA */ - /* Set high water mark at 3/4 of full ring */ - port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); - - /* uart experiences pauses at high baud rate reducing actual - * throughput by 10% or so unless we enable high speed polling - * XXX when this hardware bug is resolved we should revert to - * normal polling speed - */ - port->ip_sscr |= SSCR_HIGH_SPD; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Disable and clear all serial related interrupt bits */ - port->ip_card->ic_enable &= ~hooks->intr_clear; - ioc3_disable(port->ip_is, idd, hooks->intr_clear); - ioc3_ack(port->ip_is, idd, hooks->intr_clear); - return 0; -} - -/** - * enable_intrs - enable interrupts - * @port: port to enable - * @mask: mask to use - */ -static void enable_intrs(struct ioc3_port *port, uint32_t mask) -{ - if ((port->ip_card->ic_enable & mask) != mask) { - port->ip_card->ic_enable |= mask; - ioc3_enable(port->ip_is, port->ip_idd, mask); - } -} - -/** - * local_open - local open a port - * @port: port to open - */ -static inline int local_open(struct ioc3_port *port) -{ - int spiniter = 0; - - port->ip_flags = INPUT_ENABLE; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr | SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) { - NOT_PROGRESS(); - return -1; - } - } - } - - /* Reset the input fifo. If the uart received chars while the port - * was closed and DMA is not enabled, the uart may have a bunch of - * chars hanging around in its rx fifo which will not be discarded - * by rclr in the upper layer. We must get rid of them here. - */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, - &port->ip_uart_regs->iu_fcr); - - writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Re-enable DMA, set default threshold to intr whenever there is - * data available. - */ - port->ip_sscr &= ~SSCR_RX_THRESHOLD; - port->ip_sscr |= 1; /* default threshold */ - - /* Plug in the new sscr. This implicitly clears the DMA_PAUSE - * flag if it was set above - */ - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - port->ip_tx_lowat = 1; - return 0; -} - -/** - * set_rx_timeout - Set rx timeout and threshold values. - * @port: port to use - * @timeout: timeout value in ticks - */ -static inline int set_rx_timeout(struct ioc3_port *port, int timeout) -{ - int threshold; - - port->ip_rx_timeout = timeout; - - /* Timeout is in ticks. Let's figure out how many chars we - * can receive at the current baud rate in that interval - * and set the rx threshold to that amount. There are 4 chars - * per ring entry, so we'll divide the number of chars that will - * arrive in timeout by 4. - * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. - */ - threshold = timeout * port->ip_baud / 4000; - if (threshold == 0) - threshold = 1; /* otherwise we'll intr all the time! */ - - if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD) - return 1; - - port->ip_sscr &= ~SSCR_RX_THRESHOLD; - port->ip_sscr |= threshold; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Now set the rx timeout to the given value - * again timeout * SRTR_HZ / HZ - */ - timeout = timeout * SRTR_HZ / 100; - if (timeout > SRTR_CNT) - timeout = SRTR_CNT; - writel(timeout, &port->ip_serial_regs->srtr); - return 0; -} - -/** - * config_port - config the hardware - * @port: port to config - * @baud: baud rate for the port - * @byte_size: data size - * @stop_bits: number of stop bits - * @parenb: parity enable ? - * @parodd: odd parity ? - */ -static inline int -config_port(struct ioc3_port *port, - int baud, int byte_size, int stop_bits, int parenb, int parodd) -{ - char lcr, sizebits; - int spiniter = 0; - - DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d " - "parodd %d\n", - __func__, ((struct uart_port *)port->ip_port)->line, - baud, byte_size, stop_bits, parenb, parodd)); - - if (set_baud(port, baud)) - return 1; - - switch (byte_size) { - case 5: - sizebits = UART_LCR_WLEN5; - break; - case 6: - sizebits = UART_LCR_WLEN6; - break; - case 7: - sizebits = UART_LCR_WLEN7; - break; - case 8: - sizebits = UART_LCR_WLEN8; - break; - default: - return 1; - } - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr | SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - - /* Clear relevant fields in lcr */ - lcr = readb(&port->ip_uart_regs->iu_lcr); - lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | - UART_LCR_PARITY | LCR_MASK_STOP_BITS); - - /* Set byte size in lcr */ - lcr |= sizebits; - - /* Set parity */ - if (parenb) { - lcr |= UART_LCR_PARITY; - if (!parodd) - lcr |= UART_LCR_EPAR; - } - - /* Set stop bits */ - if (stop_bits) - lcr |= UART_LCR_STOP /* 2 stop bits */ ; - - writeb(lcr, &port->ip_uart_regs->iu_lcr); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - port->ip_baud = baud; - - /* When we get within this number of ring entries of filling the - * entire ring on tx, place an EXPLICIT intr to generate a lowat - * notification when output has drained. - */ - port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; - if (port->ip_tx_lowat == 0) - port->ip_tx_lowat = 1; - - set_rx_timeout(port, 2); - return 0; -} - -/** - * do_write - Write bytes to the port. Returns the number of bytes - * actually written. Called from transmit_chars - * @port: port to use - * @buf: the stuff to write - * @len: how many bytes in 'buf' - */ -static inline int do_write(struct ioc3_port *port, char *buf, int len) -{ - int prod_ptr, cons_ptr, total = 0; - struct ring *outring; - struct ring_entry *entry; - struct port_hooks *hooks = port->ip_hooks; - - BUG_ON(!(len >= 0)); - - prod_ptr = port->ip_tx_prod; - cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - outring = port->ip_outring; - - /* Maintain a 1-entry red-zone. The ring buffer is full when - * (cons - prod) % ring_size is 1. Rather than do this subtraction - * in the body of the loop, I'll do it now. - */ - cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; - - /* Stuff the bytes into the output */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - int xx; - - /* Get 4 bytes (one ring entry) at a time */ - entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); - - /* Invalidate all entries */ - entry->ring_allsc = 0; - - /* Copy in some bytes */ - for (xx = 0; (xx < 4) && (len > 0); xx++) { - entry->ring_data[xx] = *buf++; - entry->ring_sc[xx] = TXCB_VALID; - len--; - total++; - } - - /* If we are within some small threshold of filling up the - * entire ring buffer, we must place an EXPLICIT intr here - * to generate a lowat interrupt in case we subsequently - * really do fill up the ring and the caller goes to sleep. - * No need to place more than one though. - */ - if (!(port->ip_flags & LOWAT_WRITTEN) && - ((cons_ptr - prod_ptr) & PROD_CONS_MASK) - <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) { - port->ip_flags |= LOWAT_WRITTEN; - entry->ring_sc[0] |= TXCB_INT_WHEN_DONE; - } - - /* Go on to next entry */ - prod_ptr += sizeof(struct ring_entry); - prod_ptr &= PROD_CONS_MASK; - } - - /* If we sent something, start DMA if necessary */ - if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) { - port->ip_sscr |= SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - - /* Store the new producer pointer. If tx is disabled, we stuff the - * data into the ring buffer, but we don't actually start tx. - */ - if (!uart_tx_stopped(port->ip_port)) { - writel(prod_ptr, &port->ip_serial_regs->stpir); - - /* If we are now transmitting, enable tx_mt interrupt so we - * can disable DMA if necessary when the tx finishes. - */ - if (total > 0) - enable_intrs(port, hooks->intr_tx_mt); - } - port->ip_tx_prod = prod_ptr; - - return total; -} - -/** - * disable_intrs - disable interrupts - * @port: port to enable - * @mask: mask to use - */ -static inline void disable_intrs(struct ioc3_port *port, uint32_t mask) -{ - if (port->ip_card->ic_enable & mask) { - ioc3_disable(port->ip_is, port->ip_idd, mask); - port->ip_card->ic_enable &= ~mask; - } -} - -/** - * set_notification - Modify event notification - * @port: port to use - * @mask: events mask - * @set_on: set ? - */ -static int set_notification(struct ioc3_port *port, int mask, int set_on) -{ - struct port_hooks *hooks = port->ip_hooks; - uint32_t intrbits, sscrbits; - - BUG_ON(!mask); - - intrbits = sscrbits = 0; - - if (mask & N_DATA_READY) - intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); - if (mask & N_OUTPUT_LOWAT) - intrbits |= hooks->intr_tx_explicit; - if (mask & N_DDCD) { - intrbits |= hooks->intr_delta_dcd; - sscrbits |= SSCR_RX_RING_DCD; - } - if (mask & N_DCTS) - intrbits |= hooks->intr_delta_cts; - - if (set_on) { - enable_intrs(port, intrbits); - port->ip_notify |= mask; - port->ip_sscr |= sscrbits; - } else { - disable_intrs(port, intrbits); - port->ip_notify &= ~mask; - port->ip_sscr &= ~sscrbits; - } - - /* We require DMA if either DATA_READY or DDCD notification is - * currently requested. If neither of these is requested and - * there is currently no tx in progress, DMA may be disabled. - */ - if (port->ip_notify & (N_DATA_READY | N_DDCD)) - port->ip_sscr |= SSCR_DMA_EN; - else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt)) - port->ip_sscr &= ~SSCR_DMA_EN; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - return 0; -} - -/** - * set_mcr - set the master control reg - * @the_port: port to use - * @mask1: mcr mask - * @mask2: shadow mask - */ -static inline int set_mcr(struct uart_port *the_port, - int mask1, int mask2) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - uint32_t shadow; - int spiniter = 0; - char mcr; - - if (!port) - return -1; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr | SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - shadow = readl(&port->ip_serial_regs->shadow); - mcr = (shadow & 0xff000000) >> 24; - - /* Set new value */ - mcr |= mask1; - shadow |= mask2; - writeb(mcr, &port->ip_uart_regs->iu_mcr); - writel(shadow, &port->ip_serial_regs->shadow); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - return 0; -} - -/** - * ioc3_set_proto - set the protocol for the port - * @port: port to use - * @proto: protocol to use - */ -static int ioc3_set_proto(struct ioc3_port *port, int proto) -{ - struct port_hooks *hooks = port->ip_hooks; - - switch (proto) { - default: - case PROTO_RS232: - /* Clear the appropriate GIO pin */ - DPRINT_CONFIG(("%s: rs232\n", __func__)); - writel(0, (&port->ip_idd->vma->gppr[0] - + hooks->rs422_select_pin)); - break; - - case PROTO_RS422: - /* Set the appropriate GIO pin */ - DPRINT_CONFIG(("%s: rs422\n", __func__)); - writel(1, (&port->ip_idd->vma->gppr[0] - + hooks->rs422_select_pin)); - break; - } - return 0; -} - -/** - * transmit_chars - upper level write, called with the_port->lock - * @the_port: port to write - */ -static void transmit_chars(struct uart_port *the_port) -{ - int xmit_count, tail, head; - int result; - char *start; - struct tty_struct *tty; - struct ioc3_port *port = get_ioc3_port(the_port); - struct uart_state *state; - - if (!the_port) - return; - if (!port) - return; - - state = the_port->state; - tty = state->port.tty; - - if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { - /* Nothing to do or hw stopped */ - set_notification(port, N_ALL_OUTPUT, 0); - return; - } - - head = state->xmit.head; - tail = state->xmit.tail; - start = (char *)&state->xmit.buf[tail]; - - /* write out all the data or until the end of the buffer */ - xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); - if (xmit_count > 0) { - result = do_write(port, start, xmit_count); - if (result > 0) { - /* booking */ - xmit_count -= result; - the_port->icount.tx += result; - /* advance the pointers */ - tail += result; - tail &= UART_XMIT_SIZE - 1; - state->xmit.tail = tail; - start = (char *)&state->xmit.buf[tail]; - } - } - if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(the_port); - - if (uart_circ_empty(&state->xmit)) { - set_notification(port, N_OUTPUT_LOWAT, 0); - } else { - set_notification(port, N_OUTPUT_LOWAT, 1); - } -} - -/** - * ioc3_change_speed - change the speed of the port - * @the_port: port to change - * @new_termios: new termios settings - * @old_termios: old termios settings - */ -static void -ioc3_change_speed(struct uart_port *the_port, - struct ktermios *new_termios, struct ktermios *old_termios) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - unsigned int cflag, iflag; - int baud; - int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; - struct uart_state *state = the_port->state; - - cflag = new_termios->c_cflag; - iflag = new_termios->c_iflag; - - switch (cflag & CSIZE) { - case CS5: - new_data = 5; - break; - case CS6: - new_data = 6; - break; - case CS7: - new_data = 7; - break; - case CS8: - new_data = 8; - break; - default: - /* cuz we always need a default ... */ - new_data = 5; - break; - } - if (cflag & CSTOPB) { - new_stop = 1; - } - if (cflag & PARENB) { - new_parity_enable = 1; - if (cflag & PARODD) - new_parity = 1; - } - baud = uart_get_baud_rate(the_port, new_termios, old_termios, - MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); - DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud, - the_port->line)); - - if (!the_port->fifosize) - the_port->fifosize = FIFO_SIZE; - uart_update_timeout(the_port, cflag, baud); - - the_port->ignore_status_mask = N_ALL_INPUT; - - state->port.tty->low_latency = 1; - - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~(N_PARITY_ERROR - | N_FRAMING_ERROR); - if (iflag & IGNBRK) { - the_port->ignore_status_mask &= ~N_BREAK; - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; - } - if (!(cflag & CREAD)) { - /* ignore everything */ - the_port->ignore_status_mask &= ~N_DATA_READY; - } - - if (cflag & CRTSCTS) { - /* enable hardware flow control */ - port->ip_sscr |= SSCR_HFC_EN; - } - else { - /* disable hardware flow control */ - port->ip_sscr &= ~SSCR_HFC_EN; - } - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Set the configuration and proper notification call */ - DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o " - "config_port(baud %d data %d stop %d penable %d " - " parity %d), notification 0x%x\n", - __func__, (void *)port, the_port->line, cflag, baud, - new_data, new_stop, new_parity_enable, new_parity, - the_port->ignore_status_mask)); - - if ((config_port(port, baud, /* baud */ - new_data, /* byte size */ - new_stop, /* stop bits */ - new_parity_enable, /* set parity */ - new_parity)) >= 0) { /* parity 1==odd */ - set_notification(port, the_port->ignore_status_mask, 1); - } -} - -/** - * ic3_startup_local - Start up the serial port - returns >= 0 if no errors - * @the_port: Port to operate on - */ -static inline int ic3_startup_local(struct uart_port *the_port) -{ - struct ioc3_port *port; - - if (!the_port) { - NOT_PROGRESS(); - return -1; - } - - port = get_ioc3_port(the_port); - if (!port) { - NOT_PROGRESS(); - return -1; - } - - local_open(port); - - /* set the protocol */ - ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 : - PROTO_RS422); - return 0; -} - -/* - * ioc3_cb_output_lowat - called when the output low water mark is hit - * @port: port to output - */ -static void ioc3_cb_output_lowat(struct ioc3_port *port) -{ - unsigned long pflags; - - /* the_port->lock is set on the call here */ - if (port->ip_port) { - spin_lock_irqsave(&port->ip_port->lock, pflags); - transmit_chars(port->ip_port); - spin_unlock_irqrestore(&port->ip_port->lock, pflags); - } -} - -/* - * ioc3_cb_post_ncs - called for some basic errors - * @port: port to use - * @ncs: event - */ -static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs) -{ - struct uart_icount *icount; - - icount = &the_port->icount; - - if (ncs & NCS_BREAK) - icount->brk++; - if (ncs & NCS_FRAMING) - icount->frame++; - if (ncs & NCS_OVERRUN) - icount->overrun++; - if (ncs & NCS_PARITY) - icount->parity++; -} - -/** - * do_read - Read in bytes from the port. Return the number of bytes - * actually read. - * @the_port: port to use - * @buf: place to put the stuff we read - * @len: how big 'buf' is - */ - -static inline int do_read(struct uart_port *the_port, char *buf, int len) -{ - int prod_ptr, cons_ptr, total; - struct ioc3_port *port = get_ioc3_port(the_port); - struct ring *inring; - struct ring_entry *entry; - struct port_hooks *hooks = port->ip_hooks; - int byte_num; - char *sc; - int loop_counter; - - BUG_ON(!(len >= 0)); - BUG_ON(!port); - - /* There is a nasty timing issue in the IOC3. When the rx_timer - * expires or the rx_high condition arises, we take an interrupt. - * At some point while servicing the interrupt, we read bytes from - * the ring buffer and re-arm the rx_timer. However the rx_timer is - * not started until the first byte is received *after* it is armed, - * and any bytes pending in the rx construction buffers are not drained - * to memory until either there are 4 bytes available or the rx_timer - * expires. This leads to a potential situation where data is left - * in the construction buffers forever - 1 to 3 bytes were received - * after the interrupt was generated but before the rx_timer was - * re-armed. At that point as long as no subsequent bytes are received - * the timer will never be started and the bytes will remain in the - * construction buffer forever. The solution is to execute a DRAIN - * command after rearming the timer. This way any bytes received before - * the DRAIN will be drained to memory, and any bytes received after - * the DRAIN will start the TIMER and be drained when it expires. - * Luckily, this only needs to be done when the DMA buffer is empty - * since there is no requirement that this function return all - * available data as long as it returns some. - */ - /* Re-arm the timer */ - - writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); - - prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - cons_ptr = port->ip_rx_cons; - - if (prod_ptr == cons_ptr) { - int reset_dma = 0; - - /* Input buffer appears empty, do a flush. */ - - /* DMA must be enabled for this to work. */ - if (!(port->ip_sscr & SSCR_DMA_EN)) { - port->ip_sscr |= SSCR_DMA_EN; - reset_dma = 1; - } - - /* Potential race condition: we must reload the srpir after - * issuing the drain command, otherwise we could think the rx - * buffer is empty, then take a very long interrupt, and when - * we come back it's full and we wait forever for the drain to - * complete. - */ - writel(port->ip_sscr | SSCR_RX_DRAIN, - &port->ip_serial_regs->sscr); - prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - - /* We must not wait for the DRAIN to complete unless there are - * at least 8 bytes (2 ring entries) available to receive the - * data otherwise the DRAIN will never complete and we'll - * deadlock here. - * In fact, to make things easier, I'll just ignore the flush if - * there is any data at all now available. - */ - if (prod_ptr == cons_ptr) { - loop_counter = 0; - while (readl(&port->ip_serial_regs->sscr) & - SSCR_RX_DRAIN) { - loop_counter++; - if (loop_counter > MAXITER) - return -1; - } - - /* SIGH. We have to reload the prod_ptr *again* since - * the drain may have caused it to change - */ - prod_ptr = readl(&port->ip_serial_regs->srpir) - & PROD_CONS_MASK; - } - if (reset_dma) { - port->ip_sscr &= ~SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - } - inring = port->ip_inring; - port->ip_flags &= ~READ_ABORTED; - - total = 0; - loop_counter = 0xfffff; /* to avoid hangs */ - - /* Grab bytes from the hardware */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - entry = (struct ring_entry *)((caddr_t) inring + cons_ptr); - - if (loop_counter-- <= 0) { - printk(KERN_WARNING "IOC3 serial: " - "possible hang condition/" - "port stuck on read (line %d).\n", - the_port->line); - break; - } - - /* According to the producer pointer, this ring entry - * must contain some data. But if the PIO happened faster - * than the DMA, the data may not be available yet, so let's - * wait until it arrives. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - /* Indicate the read is aborted so we don't disable - * the interrupt thinking that the consumer is - * congested. - */ - port->ip_flags |= READ_ABORTED; - len = 0; - break; - } - - /* Load the bytes/status out of the ring entry */ - for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { - sc = &(entry->ring_sc[byte_num]); - - /* Check for change in modem state or overrun */ - if ((*sc & RXSB_MODEM_VALID) - && (port->ip_notify & N_DDCD)) { - /* Notify upper layer if DCD dropped */ - if ((port->ip_flags & DCD_ON) - && !(*sc & RXSB_DCD)) { - /* If we have already copied some data, - * return it. We'll pick up the carrier - * drop on the next pass. That way we - * don't throw away the data that has - * already been copied back to - * the caller's buffer. - */ - if (total > 0) { - len = 0; - break; - } - port->ip_flags &= ~DCD_ON; - - /* Turn off this notification so the - * carrier drop protocol won't see it - * again when it does a read. - */ - *sc &= ~RXSB_MODEM_VALID; - - /* To keep things consistent, we need - * to update the consumer pointer so - * the next reader won't come in and - * try to read the same ring entries - * again. This must be done here before - * the dcd change. - */ - - if ((entry->ring_allsc & RING_ANY_VALID) - == 0) { - cons_ptr += (int)sizeof - (struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - writel(cons_ptr, - &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* Notify upper layer of carrier drop */ - if ((port->ip_notify & N_DDCD) - && port->ip_port) { - uart_handle_dcd_change - (port->ip_port, 0); - wake_up_interruptible - (&the_port->state-> - port.delta_msr_wait); - } - - /* If we had any data to return, we - * would have returned it above. - */ - return 0; - } - } - if (*sc & RXSB_MODEM_VALID) { - /* Notify that an input overrun occurred */ - if ((*sc & RXSB_OVERRUN) - && (port->ip_notify & N_OVERRUN_ERROR)) { - ioc3_cb_post_ncs(the_port, NCS_OVERRUN); - } - /* Don't look at this byte again */ - *sc &= ~RXSB_MODEM_VALID; - } - - /* Check for valid data or RX errors */ - if ((*sc & RXSB_DATA_VALID) && - ((*sc & (RXSB_PAR_ERR - | RXSB_FRAME_ERR | RXSB_BREAK)) - && (port->ip_notify & (N_PARITY_ERROR - | N_FRAMING_ERROR - | N_BREAK)))) { - /* There is an error condition on the next byte. - * If we have already transferred some bytes, - * we'll stop here. Otherwise if this is the - * first byte to be read, we'll just transfer - * it alone after notifying the - * upper layer of its status. - */ - if (total > 0) { - len = 0; - break; - } else { - if ((*sc & RXSB_PAR_ERR) && - (port-> - ip_notify & N_PARITY_ERROR)) { - ioc3_cb_post_ncs(the_port, - NCS_PARITY); - } - if ((*sc & RXSB_FRAME_ERR) && - (port-> - ip_notify & N_FRAMING_ERROR)) { - ioc3_cb_post_ncs(the_port, - NCS_FRAMING); - } - if ((*sc & RXSB_BREAK) - && (port->ip_notify & N_BREAK)) { - ioc3_cb_post_ncs - (the_port, NCS_BREAK); - } - len = 1; - } - } - if (*sc & RXSB_DATA_VALID) { - *sc &= ~RXSB_DATA_VALID; - *buf = entry->ring_data[byte_num]; - buf++; - len--; - total++; - } - } - - /* If we used up this entry entirely, go on to the next one, - * otherwise we must have run out of buffer space, so - * leave the consumer pointer here for the next read in case - * there are still unread bytes in this entry. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - cons_ptr += (int)sizeof(struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - } - - /* Update consumer pointer and re-arm rx timer interrupt */ - writel(cons_ptr, &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* If we have now dipped below the rx high water mark and we have - * rx_high interrupt turned off, we can now turn it back on again. - */ - if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) - & PROD_CONS_MASK) < - ((port-> - ip_sscr & - SSCR_RX_THRESHOLD) - << PROD_CONS_PTR_OFF))) { - port->ip_flags &= ~INPUT_HIGH; - enable_intrs(port, hooks->intr_rx_high); - } - return total; -} - -/** - * receive_chars - upper level read. - * @the_port: port to read from - */ -static int receive_chars(struct uart_port *the_port) -{ - struct tty_struct *tty; - unsigned char ch[MAX_CHARS]; - int read_count = 0, read_room, flip = 0; - struct uart_state *state = the_port->state; - struct ioc3_port *port = get_ioc3_port(the_port); - unsigned long pflags; - - /* Make sure all the pointers are "good" ones */ - if (!state) - return 0; - if (!state->port.tty) - return 0; - - if (!(port->ip_flags & INPUT_ENABLE)) - return 0; - - spin_lock_irqsave(&the_port->lock, pflags); - tty = state->port.tty; - - read_count = do_read(the_port, ch, MAX_CHARS); - if (read_count > 0) { - flip = 1; - read_room = tty_insert_flip_string(tty, ch, read_count); - the_port->icount.rx += read_count; - } - spin_unlock_irqrestore(&the_port->lock, pflags); - - if (flip) - tty_flip_buffer_push(tty); - - return read_count; -} - -/** - * ioc3uart_intr_one - lowest level (per port) interrupt handler. - * @is : submodule - * @idd: driver data - * @pending: interrupts to handle - */ - -static int inline -ioc3uart_intr_one(struct ioc3_submodule *is, - struct ioc3_driver_data *idd, - unsigned int pending) -{ - int port_num = GET_PORT_FROM_SIO_IR(pending); - struct port_hooks *hooks; - unsigned int rx_high_rd_aborted = 0; - unsigned long flags; - struct uart_port *the_port; - struct ioc3_port *port; - int loop_counter; - struct ioc3_card *card_ptr; - unsigned int sio_ir; - - card_ptr = idd->data[is->id]; - port = card_ptr->ic_port[port_num].icp_port; - hooks = port->ip_hooks; - - /* Possible race condition here: The tx_mt interrupt bit may be - * cleared without the intervention of the interrupt handler, - * e.g. by a write. If the top level interrupt handler reads a - * tx_mt, then some other processor does a write, starting up - * output, then we come in here, see the tx_mt and stop DMA, the - * output started by the other processor will hang. Thus we can - * only rely on tx_mt being legitimate if it is read while the - * port lock is held. Therefore this bit must be ignored in the - * passed in interrupt mask which was read by the top level - * interrupt handler since the port lock was not held at the time - * it was read. We can only rely on this bit being accurate if it - * is read while the port lock is held. So we'll clear it for now, - * and reload it later once we have the port lock. - */ - - sio_ir = pending & ~(hooks->intr_tx_mt); - spin_lock_irqsave(&port->ip_lock, flags); - - loop_counter = MAXITER; /* to avoid hangs */ - - do { - uint32_t shadow; - - if (loop_counter-- <= 0) { - printk(KERN_WARNING "IOC3 serial: " - "possible hang condition/" - "port stuck on interrupt (line %d).\n", - ((struct uart_port *)port->ip_port)->line); - break; - } - /* Handle a DCD change */ - if (sio_ir & hooks->intr_delta_dcd) { - ioc3_ack(is, idd, hooks->intr_delta_dcd); - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DDCD) - && (shadow & SHADOW_DCD) - && (port->ip_port)) { - the_port = port->ip_port; - uart_handle_dcd_change(the_port, - shadow & SHADOW_DCD); - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } else if ((port->ip_notify & N_DDCD) - && !(shadow & SHADOW_DCD)) { - /* Flag delta DCD/no DCD */ - uart_handle_dcd_change(port->ip_port, - shadow & SHADOW_DCD); - port->ip_flags |= DCD_ON; - } - } - - /* Handle a CTS change */ - if (sio_ir & hooks->intr_delta_cts) { - ioc3_ack(is, idd, hooks->intr_delta_cts); - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DCTS) && (port->ip_port)) { - the_port = port->ip_port; - uart_handle_cts_change(the_port, shadow - & SHADOW_CTS); - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } - } - - /* rx timeout interrupt. Must be some data available. Put this - * before the check for rx_high since servicing this condition - * may cause that condition to clear. - */ - if (sio_ir & hooks->intr_rx_timer) { - ioc3_ack(is, idd, hooks->intr_rx_timer); - if ((port->ip_notify & N_DATA_READY) - && (port->ip_port)) { - receive_chars(port->ip_port); - } - } - - /* rx high interrupt. Must be after rx_timer. */ - else if (sio_ir & hooks->intr_rx_high) { - /* Data available, notify upper layer */ - if ((port->ip_notify & N_DATA_READY) && port->ip_port) { - receive_chars(port->ip_port); - } - - /* We can't ACK this interrupt. If receive_chars didn't - * cause the condition to clear, we'll have to disable - * the interrupt until the data is drained. - * If the read was aborted, don't disable the interrupt - * as this may cause us to hang indefinitely. An - * aborted read generally means that this interrupt - * hasn't been delivered to the cpu yet anyway, even - * though we see it as asserted when we read the sio_ir. - */ - if ((sio_ir = PENDING(card_ptr, idd)) - & hooks->intr_rx_high) { - if (port->ip_flags & READ_ABORTED) { - rx_high_rd_aborted++; - } - else { - card_ptr->ic_enable &= ~hooks->intr_rx_high; - port->ip_flags |= INPUT_HIGH; - } - } - } - - /* We got a low water interrupt: notify upper layer to - * send more data. Must come before tx_mt since servicing - * this condition may cause that condition to clear. - */ - if (sio_ir & hooks->intr_tx_explicit) { - port->ip_flags &= ~LOWAT_WRITTEN; - ioc3_ack(is, idd, hooks->intr_tx_explicit); - if (port->ip_notify & N_OUTPUT_LOWAT) - ioc3_cb_output_lowat(port); - } - - /* Handle tx_mt. Must come after tx_explicit. */ - else if (sio_ir & hooks->intr_tx_mt) { - /* If we are expecting a lowat notification - * and we get to this point it probably means that for - * some reason the tx_explicit didn't work as expected - * (that can legitimately happen if the output buffer is - * filled up in just the right way). - * So send the notification now. - */ - if (port->ip_notify & N_OUTPUT_LOWAT) { - ioc3_cb_output_lowat(port); - - /* We need to reload the sio_ir since the lowat - * call may have caused another write to occur, - * clearing the tx_mt condition. - */ - sio_ir = PENDING(card_ptr, idd); - } - - /* If the tx_mt condition still persists even after the - * lowat call, we've got some work to do. - */ - if (sio_ir & hooks->intr_tx_mt) { - /* If we are not currently expecting DMA input, - * and the transmitter has just gone idle, - * there is no longer any reason for DMA, so - * disable it. - */ - if (!(port->ip_notify - & (N_DATA_READY | N_DDCD))) { - BUG_ON(!(port->ip_sscr - & SSCR_DMA_EN)); - port->ip_sscr &= ~SSCR_DMA_EN; - writel(port->ip_sscr, - &port->ip_serial_regs->sscr); - } - /* Prevent infinite tx_mt interrupt */ - card_ptr->ic_enable &= ~hooks->intr_tx_mt; - } - } - sio_ir = PENDING(card_ptr, idd); - - /* if the read was aborted and only hooks->intr_rx_high, - * clear hooks->intr_rx_high, so we do not loop forever. - */ - - if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { - sio_ir &= ~hooks->intr_rx_high; - } - } while (sio_ir & hooks->intr_all); - - spin_unlock_irqrestore(&port->ip_lock, flags); - ioc3_enable(is, idd, card_ptr->ic_enable); - return 0; -} - -/** - * ioc3uart_intr - field all serial interrupts - * @is : submodule - * @idd: driver data - * @pending: interrupts to handle - * - */ - -static int ioc3uart_intr(struct ioc3_submodule *is, - struct ioc3_driver_data *idd, - unsigned int pending) -{ - int ret = 0; - - /* - * The upper level interrupt handler sends interrupts for both ports - * here. So we need to call for each port with its interrupts. - */ - - if (pending & SIO_IR_SA) - ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SA); - if (pending & SIO_IR_SB) - ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SB); - - return ret; -} - -/** - * ic3_type - * @port: Port to operate with (we ignore since we only have one port) - * - */ -static const char *ic3_type(struct uart_port *the_port) -{ - if (IS_RS232(the_port->line)) - return "SGI IOC3 Serial [rs232]"; - else - return "SGI IOC3 Serial [rs422]"; -} - -/** - * ic3_tx_empty - Is the transmitter empty? - * @port: Port to operate on - * - */ -static unsigned int ic3_tx_empty(struct uart_port *the_port) -{ - unsigned int ret = 0; - struct ioc3_port *port = get_ioc3_port(the_port); - - if (readl(&port->ip_serial_regs->shadow) & SHADOW_TEMT) - ret = TIOCSER_TEMT; - return ret; -} - -/** - * ic3_stop_tx - stop the transmitter - * @port: Port to operate on - * - */ -static void ic3_stop_tx(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - - if (port) - set_notification(port, N_OUTPUT_LOWAT, 0); -} - -/** - * ic3_stop_rx - stop the receiver - * @port: Port to operate on - * - */ -static void ic3_stop_rx(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - - if (port) - port->ip_flags &= ~INPUT_ENABLE; -} - -/** - * null_void_function - * @port: Port to operate on - * - */ -static void null_void_function(struct uart_port *the_port) -{ -} - -/** - * ic3_shutdown - shut down the port - free irq and disable - * @port: port to shut down - * - */ -static void ic3_shutdown(struct uart_port *the_port) -{ - unsigned long port_flags; - struct ioc3_port *port; - struct uart_state *state; - - port = get_ioc3_port(the_port); - if (!port) - return; - - state = the_port->state; - wake_up_interruptible(&state->port.delta_msr_wait); - - spin_lock_irqsave(&the_port->lock, port_flags); - set_notification(port, N_ALL, 0); - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic3_set_mctrl - set control lines (dtr, rts, etc) - * @port: Port to operate on - * @mctrl: Lines to set/unset - * - */ -static void ic3_set_mctrl(struct uart_port *the_port, unsigned int mctrl) -{ - unsigned char mcr = 0; - - 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; - - set_mcr(the_port, mcr, SHADOW_DTR); -} - -/** - * ic3_get_mctrl - get control line info - * @port: port to operate on - * - */ -static unsigned int ic3_get_mctrl(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - uint32_t shadow; - unsigned int ret = 0; - - if (!port) - return 0; - - shadow = readl(&port->ip_serial_regs->shadow); - if (shadow & SHADOW_DCD) - ret |= TIOCM_CD; - if (shadow & SHADOW_DR) - ret |= TIOCM_DSR; - if (shadow & SHADOW_CTS) - ret |= TIOCM_CTS; - return ret; -} - -/** - * ic3_start_tx - Start transmitter. Called with the_port->lock - * @port: Port to operate on - * - */ -static void ic3_start_tx(struct uart_port *the_port) -{ - struct ioc3_port *port = get_ioc3_port(the_port); - - if (port) { - set_notification(port, N_OUTPUT_LOWAT, 1); - enable_intrs(port, port->ip_hooks->intr_tx_mt); - } -} - -/** - * ic3_break_ctl - handle breaks - * @port: Port to operate on - * @break_state: Break state - * - */ -static void ic3_break_ctl(struct uart_port *the_port, int break_state) -{ -} - -/** - * ic3_startup - Start up the serial port - always return 0 (We're always on) - * @port: Port to operate on - * - */ -static int ic3_startup(struct uart_port *the_port) -{ - int retval; - struct ioc3_port *port; - struct ioc3_card *card_ptr; - unsigned long port_flags; - - if (!the_port) { - NOT_PROGRESS(); - return -ENODEV; - } - port = get_ioc3_port(the_port); - if (!port) { - NOT_PROGRESS(); - return -ENODEV; - } - card_ptr = port->ip_card; - port->ip_port = the_port; - - if (!card_ptr) { - NOT_PROGRESS(); - return -ENODEV; - } - - /* Start up the serial port */ - spin_lock_irqsave(&the_port->lock, port_flags); - retval = ic3_startup_local(the_port); - spin_unlock_irqrestore(&the_port->lock, port_flags); - return retval; -} - -/** - * ic3_set_termios - set termios stuff - * @port: port to operate on - * @termios: New settings - * @termios: Old - * - */ -static void -ic3_set_termios(struct uart_port *the_port, - struct ktermios *termios, struct ktermios *old_termios) -{ - unsigned long port_flags; - - spin_lock_irqsave(&the_port->lock, port_flags); - ioc3_change_speed(the_port, termios, old_termios); - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic3_request_port - allocate resources for port - no op.... - * @port: port to operate on - * - */ -static int ic3_request_port(struct uart_port *port) -{ - return 0; -} - -/* Associate the uart functions above - given to serial core */ -static struct uart_ops ioc3_ops = { - .tx_empty = ic3_tx_empty, - .set_mctrl = ic3_set_mctrl, - .get_mctrl = ic3_get_mctrl, - .stop_tx = ic3_stop_tx, - .start_tx = ic3_start_tx, - .stop_rx = ic3_stop_rx, - .enable_ms = null_void_function, - .break_ctl = ic3_break_ctl, - .startup = ic3_startup, - .shutdown = ic3_shutdown, - .set_termios = ic3_set_termios, - .type = ic3_type, - .release_port = null_void_function, - .request_port = ic3_request_port, -}; - -/* - * Boot-time initialization code - */ - -static struct uart_driver ioc3_uart = { - .owner = THIS_MODULE, - .driver_name = "ioc3_serial", - .dev_name = DEVICE_NAME, - .major = DEVICE_MAJOR, - .minor = DEVICE_MINOR, - .nr = MAX_LOGICAL_PORTS -}; - -/** - * ioc3_serial_core_attach - register with serial core - * This is done during pci probing - * @is: submodule struct for this - * @idd: handle for this card - */ -static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, - struct ioc3_driver_data *idd) -{ - struct ioc3_port *port; - struct uart_port *the_port; - struct ioc3_card *card_ptr = idd->data[is->id]; - int ii, phys_port; - struct pci_dev *pdev = idd->pdev; - - DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n", - __func__, pdev, (void *)card_ptr)); - - if (!card_ptr) - return -ENODEV; - - /* once around for each logical port on this card */ - for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { - phys_port = GET_PHYSICAL_PORT(ii); - the_port = &card_ptr->ic_port[phys_port]. - icp_uart_port[GET_LOGICAL_PORT(ii)]; - port = card_ptr->ic_port[phys_port].icp_port; - port->ip_port = the_port; - - DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n", - __func__, (void *)the_port, (void *)port, - phys_port, ii)); - - /* membase, iobase and mapbase just need to be non-0 */ - the_port->membase = (unsigned char __iomem *)1; - the_port->iobase = (pdev->bus->number << 16) | ii; - the_port->line = (Num_of_ioc3_cards << 2) | ii; - the_port->mapbase = 1; - the_port->type = PORT_16550A; - the_port->fifosize = FIFO_SIZE; - the_port->ops = &ioc3_ops; - the_port->irq = idd->irq_io; - the_port->dev = &pdev->dev; - - if (uart_add_one_port(&ioc3_uart, the_port) < 0) { - printk(KERN_WARNING - "%s: unable to add port %d bus %d\n", - __func__, the_port->line, pdev->bus->number); - } else { - DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n", - the_port->line, the_port->irq, pdev->bus->number)); - } - - /* all ports are rs232 for now */ - if (IS_PHYSICAL_PORT(ii)) - ioc3_set_proto(port, PROTO_RS232); - } - return 0; -} - -/** - * ioc3uart_remove - register detach function - * @is: submodule struct for this submodule - * @idd: ioc3 driver data for this submodule - */ - -static int ioc3uart_remove(struct ioc3_submodule *is, - struct ioc3_driver_data *idd) -{ - struct ioc3_card *card_ptr = idd->data[is->id]; - struct uart_port *the_port; - struct ioc3_port *port; - int ii; - - if (card_ptr) { - for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { - the_port = &card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. - icp_uart_port[GET_LOGICAL_PORT(ii)]; - if (the_port) - uart_remove_one_port(&ioc3_uart, the_port); - port = card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].icp_port; - if (port && IS_PHYSICAL_PORT(ii) - && (GET_PHYSICAL_PORT(ii) == 0)) { - pci_free_consistent(port->ip_idd->pdev, - TOTAL_RING_BUF_SIZE, - (void *)port->ip_cpu_ringbuf, - port->ip_dma_ringbuf); - kfree(port); - card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. - icp_port = NULL; - } - } - kfree(card_ptr); - idd->data[is->id] = NULL; - } - return 0; -} - -/** - * ioc3uart_probe - card probe function called from shim driver - * @is: submodule struct for this submodule - * @idd: ioc3 driver data for this card - */ - -static int __devinit -ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) -{ - struct pci_dev *pdev = idd->pdev; - struct ioc3_card *card_ptr; - int ret = 0; - struct ioc3_port *port; - struct ioc3_port *ports[PORTS_PER_CARD]; - int phys_port; - int cnt; - - DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd)); - - card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL); - if (!card_ptr) { - printk(KERN_WARNING "ioc3_attach_one" - ": unable to get memory for the IOC3\n"); - return -ENOMEM; - } - idd->data[is->id] = card_ptr; - Submodule_slot = is->id; - - writel(((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) | - ((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) | - (0xf << SIO_CR_CMD_PULSE_SHIFT), &idd->vma->sio_cr); - - pci_write_config_dword(pdev, PCI_LAT, 0xff00); - - /* Enable serial port mode select generic PIO pins as outputs */ - ioc3_gpcr_set(idd, GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL); - - /* Create port structures for each port */ - for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) { - port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL); - if (!port) { - printk(KERN_WARNING - "IOC3 serial memory not available for port\n"); - ret = -ENOMEM; - goto out4; - } - spin_lock_init(&port->ip_lock); - - /* we need to remember the previous ones, to point back to - * them farther down - setting up the ring buffers. - */ - ports[phys_port] = port; - - /* init to something useful */ - card_ptr->ic_port[phys_port].icp_port = port; - port->ip_is = is; - port->ip_idd = idd; - port->ip_baud = 9600; - port->ip_card = card_ptr; - port->ip_hooks = &hooks_array[phys_port]; - - /* Setup each port */ - if (phys_port == 0) { - port->ip_serial_regs = &idd->vma->port_a; - port->ip_uart_regs = &idd->vma->sregs.uarta; - - DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p " - "ip_uart_regs 0x%p\n", - __func__, - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* setup ring buffers */ - port->ip_cpu_ringbuf = pci_alloc_consistent(pdev, - TOTAL_RING_BUF_SIZE, &port->ip_dma_ringbuf); - - BUG_ON(!((((int64_t) port->ip_dma_ringbuf) & - (TOTAL_RING_BUF_SIZE - 1)) == 0)); - port->ip_inring = RING(port, RX_A); - port->ip_outring = RING(port, TX_A); - DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p " - "ip_dma_ringbuf 0x%p, ip_inring 0x%p " - "ip_outring 0x%p\n", - __func__, - (void *)port->ip_cpu_ringbuf, - (void *)port->ip_dma_ringbuf, - (void *)port->ip_inring, - (void *)port->ip_outring)); - } - else { - port->ip_serial_regs = &idd->vma->port_b; - port->ip_uart_regs = &idd->vma->sregs.uartb; - - DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p " - "ip_uart_regs 0x%p\n", - __func__, - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* share the ring buffers */ - port->ip_dma_ringbuf = - ports[phys_port - 1]->ip_dma_ringbuf; - port->ip_cpu_ringbuf = - ports[phys_port - 1]->ip_cpu_ringbuf; - port->ip_inring = RING(port, RX_B); - port->ip_outring = RING(port, TX_B); - DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p " - "ip_dma_ringbuf 0x%p, ip_inring 0x%p " - "ip_outring 0x%p\n", - __func__, - (void *)port->ip_cpu_ringbuf, - (void *)port->ip_dma_ringbuf, - (void *)port->ip_inring, - (void *)port->ip_outring)); - } - - DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p", - __func__, - phys_port, (void *)port, (void *)card_ptr)); - DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* Initialize the hardware for IOC3 */ - port_init(port); - - DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p " - "outring 0x%p\n", - __func__, - phys_port, (void *)port, - (void *)port->ip_inring, - (void *)port->ip_outring)); - - } - - /* register port with the serial core */ - - if ((ret = ioc3_serial_core_attach(is, idd))) - goto out4; - - Num_of_ioc3_cards++; - - return ret; - - /* error exits that give back resources */ -out4: - for (cnt = 0; cnt < phys_port; cnt++) - kfree(ports[cnt]); - - kfree(card_ptr); - return ret; -} - -static struct ioc3_submodule ioc3uart_ops = { - .name = "IOC3uart", - .probe = ioc3uart_probe, - .remove = ioc3uart_remove, - /* call .intr for both ports initially */ - .irq_mask = SIO_IR_SA | SIO_IR_SB, - .intr = ioc3uart_intr, - .owner = THIS_MODULE, -}; - -/** - * ioc3_detect - module init called, - */ -static int __init ioc3uart_init(void) -{ - int ret; - - /* register with serial core */ - if ((ret = uart_register_driver(&ioc3_uart)) < 0) { - printk(KERN_WARNING - "%s: Couldn't register IOC3 uart serial driver\n", - __func__); - return ret; - } - ret = ioc3_register_submodule(&ioc3uart_ops); - if (ret) - uart_unregister_driver(&ioc3_uart); - return ret; -} - -static void __exit ioc3uart_exit(void) -{ - ioc3_unregister_submodule(&ioc3uart_ops); - uart_unregister_driver(&ioc3_uart); -} - -module_init(ioc3uart_init); -module_exit(ioc3uart_exit); - -MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) "); -MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC3 card"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c deleted file mode 100644 index fcfe82653ac8..000000000000 --- a/drivers/serial/ioc4_serial.c +++ /dev/null @@ -1,2953 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003-2006 Silicon Graphics, Inc. All Rights Reserved. - */ - - -/* - * This file contains a module version of the ioc4 serial driver. This - * includes all the support functions needed (support functions, etc.) - * and the serial driver itself. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * interesting things about the ioc4 - */ - -#define IOC4_NUM_SERIAL_PORTS 4 /* max ports per card */ -#define IOC4_NUM_CARDS 8 /* max cards per partition */ - -#define GET_SIO_IR(_n) (_n == 0) ? (IOC4_SIO_IR_S0) : \ - (_n == 1) ? (IOC4_SIO_IR_S1) : \ - (_n == 2) ? (IOC4_SIO_IR_S2) : \ - (IOC4_SIO_IR_S3) - -#define GET_OTHER_IR(_n) (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \ - (_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \ - (_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \ - (IOC4_OTHER_IR_S3_MEMERR) - - -/* - * All IOC4 registers are 32 bits wide. - */ - -/* - * PCI Memory Space Map - */ -#define IOC4_PCI_ERR_ADDR_L 0x000 /* Low Error Address */ -#define IOC4_PCI_ERR_ADDR_VLD (0x1 << 0) -#define IOC4_PCI_ERR_ADDR_MST_ID_MSK (0xf << 1) -#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK (0xe << 1) -#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK (0x1 << 1) -#define IOC4_PCI_ERR_ADDR_MUL_ERR (0x1 << 5) -#define IOC4_PCI_ERR_ADDR_ADDR_MSK (0x3ffffff << 6) - -/* Interrupt types */ -#define IOC4_SIO_INTR_TYPE 0 -#define IOC4_OTHER_INTR_TYPE 1 -#define IOC4_NUM_INTR_TYPES 2 - -/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES */ -#define IOC4_SIO_IR_S0_TX_MT 0x00000001 /* Serial port 0 TX empty */ -#define IOC4_SIO_IR_S0_RX_FULL 0x00000002 /* Port 0 RX buf full */ -#define IOC4_SIO_IR_S0_RX_HIGH 0x00000004 /* Port 0 RX hiwat */ -#define IOC4_SIO_IR_S0_RX_TIMER 0x00000008 /* Port 0 RX timeout */ -#define IOC4_SIO_IR_S0_DELTA_DCD 0x00000010 /* Port 0 delta DCD */ -#define IOC4_SIO_IR_S0_DELTA_CTS 0x00000020 /* Port 0 delta CTS */ -#define IOC4_SIO_IR_S0_INT 0x00000040 /* Port 0 pass-thru intr */ -#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080 /* Port 0 explicit TX thru */ -#define IOC4_SIO_IR_S1_TX_MT 0x00000100 /* Serial port 1 */ -#define IOC4_SIO_IR_S1_RX_FULL 0x00000200 /* */ -#define IOC4_SIO_IR_S1_RX_HIGH 0x00000400 /* */ -#define IOC4_SIO_IR_S1_RX_TIMER 0x00000800 /* */ -#define IOC4_SIO_IR_S1_DELTA_DCD 0x00001000 /* */ -#define IOC4_SIO_IR_S1_DELTA_CTS 0x00002000 /* */ -#define IOC4_SIO_IR_S1_INT 0x00004000 /* */ -#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000 /* */ -#define IOC4_SIO_IR_S2_TX_MT 0x00010000 /* Serial port 2 */ -#define IOC4_SIO_IR_S2_RX_FULL 0x00020000 /* */ -#define IOC4_SIO_IR_S2_RX_HIGH 0x00040000 /* */ -#define IOC4_SIO_IR_S2_RX_TIMER 0x00080000 /* */ -#define IOC4_SIO_IR_S2_DELTA_DCD 0x00100000 /* */ -#define IOC4_SIO_IR_S2_DELTA_CTS 0x00200000 /* */ -#define IOC4_SIO_IR_S2_INT 0x00400000 /* */ -#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000 /* */ -#define IOC4_SIO_IR_S3_TX_MT 0x01000000 /* Serial port 3 */ -#define IOC4_SIO_IR_S3_RX_FULL 0x02000000 /* */ -#define IOC4_SIO_IR_S3_RX_HIGH 0x04000000 /* */ -#define IOC4_SIO_IR_S3_RX_TIMER 0x08000000 /* */ -#define IOC4_SIO_IR_S3_DELTA_DCD 0x10000000 /* */ -#define IOC4_SIO_IR_S3_DELTA_CTS 0x20000000 /* */ -#define IOC4_SIO_IR_S3_INT 0x40000000 /* */ -#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000 /* */ - -/* Per device interrupt masks */ -#define IOC4_SIO_IR_S0 (IOC4_SIO_IR_S0_TX_MT | \ - IOC4_SIO_IR_S0_RX_FULL | \ - IOC4_SIO_IR_S0_RX_HIGH | \ - IOC4_SIO_IR_S0_RX_TIMER | \ - IOC4_SIO_IR_S0_DELTA_DCD | \ - IOC4_SIO_IR_S0_DELTA_CTS | \ - IOC4_SIO_IR_S0_INT | \ - IOC4_SIO_IR_S0_TX_EXPLICIT) -#define IOC4_SIO_IR_S1 (IOC4_SIO_IR_S1_TX_MT | \ - IOC4_SIO_IR_S1_RX_FULL | \ - IOC4_SIO_IR_S1_RX_HIGH | \ - IOC4_SIO_IR_S1_RX_TIMER | \ - IOC4_SIO_IR_S1_DELTA_DCD | \ - IOC4_SIO_IR_S1_DELTA_CTS | \ - IOC4_SIO_IR_S1_INT | \ - IOC4_SIO_IR_S1_TX_EXPLICIT) -#define IOC4_SIO_IR_S2 (IOC4_SIO_IR_S2_TX_MT | \ - IOC4_SIO_IR_S2_RX_FULL | \ - IOC4_SIO_IR_S2_RX_HIGH | \ - IOC4_SIO_IR_S2_RX_TIMER | \ - IOC4_SIO_IR_S2_DELTA_DCD | \ - IOC4_SIO_IR_S2_DELTA_CTS | \ - IOC4_SIO_IR_S2_INT | \ - IOC4_SIO_IR_S2_TX_EXPLICIT) -#define IOC4_SIO_IR_S3 (IOC4_SIO_IR_S3_TX_MT | \ - IOC4_SIO_IR_S3_RX_FULL | \ - IOC4_SIO_IR_S3_RX_HIGH | \ - IOC4_SIO_IR_S3_RX_TIMER | \ - IOC4_SIO_IR_S3_DELTA_DCD | \ - IOC4_SIO_IR_S3_DELTA_CTS | \ - IOC4_SIO_IR_S3_INT | \ - IOC4_SIO_IR_S3_TX_EXPLICIT) - -/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES */ -#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */ -#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */ -#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */ -#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */ -#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */ -#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */ -#define IOC4_OTHER_IR_KBD_INT 0x00000040 /* Keyboard/mouse */ -#define IOC4_OTHER_IR_RESERVED 0x007fff80 /* Reserved */ -#define IOC4_OTHER_IR_RT_INT 0x00800000 /* INT_OUT section output */ -#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* Generic pins */ - -#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \ - IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR) - -/* Bitmasks for IOC4_SIO_CR */ -#define IOC4_SIO_CR_CMD_PULSE_SHIFT 0 /* byte bus strobe shift */ -#define IOC4_SIO_CR_ARB_DIAG_TX0 0x00000000 -#define IOC4_SIO_CR_ARB_DIAG_RX0 0x00000010 -#define IOC4_SIO_CR_ARB_DIAG_TX1 0x00000020 -#define IOC4_SIO_CR_ARB_DIAG_RX1 0x00000030 -#define IOC4_SIO_CR_ARB_DIAG_TX2 0x00000040 -#define IOC4_SIO_CR_ARB_DIAG_RX2 0x00000050 -#define IOC4_SIO_CR_ARB_DIAG_TX3 0x00000060 -#define IOC4_SIO_CR_ARB_DIAG_RX3 0x00000070 -#define IOC4_SIO_CR_SIO_DIAG_IDLE 0x00000080 /* 0 -> active request among - serial ports (ro) */ -/* Defs for some of the generic I/O pins */ -#define IOC4_GPCR_UART0_MODESEL 0x10 /* Pin is output to port 0 - mode sel */ -#define IOC4_GPCR_UART1_MODESEL 0x20 /* Pin is output to port 1 - mode sel */ -#define IOC4_GPCR_UART2_MODESEL 0x40 /* Pin is output to port 2 - mode sel */ -#define IOC4_GPCR_UART3_MODESEL 0x80 /* Pin is output to port 3 - mode sel */ - -#define IOC4_GPPR_UART0_MODESEL_PIN 4 /* GIO pin controlling - uart 0 mode select */ -#define IOC4_GPPR_UART1_MODESEL_PIN 5 /* GIO pin controlling - uart 1 mode select */ -#define IOC4_GPPR_UART2_MODESEL_PIN 6 /* GIO pin controlling - uart 2 mode select */ -#define IOC4_GPPR_UART3_MODESEL_PIN 7 /* GIO pin controlling - uart 3 mode select */ - -/* Bitmasks for serial RX status byte */ -#define IOC4_RXSB_OVERRUN 0x01 /* Char(s) lost */ -#define IOC4_RXSB_PAR_ERR 0x02 /* Parity error */ -#define IOC4_RXSB_FRAME_ERR 0x04 /* Framing error */ -#define IOC4_RXSB_BREAK 0x08 /* Break character */ -#define IOC4_RXSB_CTS 0x10 /* State of CTS */ -#define IOC4_RXSB_DCD 0x20 /* State of DCD */ -#define IOC4_RXSB_MODEM_VALID 0x40 /* DCD, CTS, and OVERRUN are valid */ -#define IOC4_RXSB_DATA_VALID 0x80 /* Data byte, FRAME_ERR PAR_ERR - * & BREAK valid */ - -/* Bitmasks for serial TX control byte */ -#define IOC4_TXCB_INT_WHEN_DONE 0x20 /* Interrupt after this byte is sent */ -#define IOC4_TXCB_INVALID 0x00 /* Byte is invalid */ -#define IOC4_TXCB_VALID 0x40 /* Byte is valid */ -#define IOC4_TXCB_MCR 0x80 /* Data<7:0> to modem control reg */ -#define IOC4_TXCB_DELAY 0xc0 /* Delay data<7:0> mSec */ - -/* Bitmasks for IOC4_SBBR_L */ -#define IOC4_SBBR_L_SIZE 0x00000001 /* 0 == 1KB rings, 1 == 4KB rings */ - -/* Bitmasks for IOC4_SSCR_<3:0> */ -#define IOC4_SSCR_RX_THRESHOLD 0x000001ff /* Hiwater mark */ -#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000 /* TX timer in progress */ -#define IOC4_SSCR_HFC_EN 0x00020000 /* Hardware flow control enabled */ -#define IOC4_SSCR_RX_RING_DCD 0x00040000 /* Post RX record on delta-DCD */ -#define IOC4_SSCR_RX_RING_CTS 0x00080000 /* Post RX record on delta-CTS */ -#define IOC4_SSCR_DIAG 0x00200000 /* Bypass clock divider for sim */ -#define IOC4_SSCR_RX_DRAIN 0x08000000 /* Drain RX buffer to memory */ -#define IOC4_SSCR_DMA_EN 0x10000000 /* Enable ring buffer DMA */ -#define IOC4_SSCR_DMA_PAUSE 0x20000000 /* Pause DMA */ -#define IOC4_SSCR_PAUSE_STATE 0x40000000 /* Sets when PAUSE takes effect */ -#define IOC4_SSCR_RESET 0x80000000 /* Reset DMA channels */ - -/* All producer/comsumer pointers are the same bitfield */ -#define IOC4_PROD_CONS_PTR_4K 0x00000ff8 /* For 4K buffers */ -#define IOC4_PROD_CONS_PTR_1K 0x000003f8 /* For 1K buffers */ -#define IOC4_PROD_CONS_PTR_OFF 3 - -/* Bitmasks for IOC4_SRCIR_<3:0> */ -#define IOC4_SRCIR_ARM 0x80000000 /* Arm RX timer */ - -/* Bitmasks for IOC4_SHADOW_<3:0> */ -#define IOC4_SHADOW_DR 0x00000001 /* Data ready */ -#define IOC4_SHADOW_OE 0x00000002 /* Overrun error */ -#define IOC4_SHADOW_PE 0x00000004 /* Parity error */ -#define IOC4_SHADOW_FE 0x00000008 /* Framing error */ -#define IOC4_SHADOW_BI 0x00000010 /* Break interrupt */ -#define IOC4_SHADOW_THRE 0x00000020 /* Xmit holding register empty */ -#define IOC4_SHADOW_TEMT 0x00000040 /* Xmit shift register empty */ -#define IOC4_SHADOW_RFCE 0x00000080 /* Char in RX fifo has an error */ -#define IOC4_SHADOW_DCTS 0x00010000 /* Delta clear to send */ -#define IOC4_SHADOW_DDCD 0x00080000 /* Delta data carrier detect */ -#define IOC4_SHADOW_CTS 0x00100000 /* Clear to send */ -#define IOC4_SHADOW_DCD 0x00800000 /* Data carrier detect */ -#define IOC4_SHADOW_DTR 0x01000000 /* Data terminal ready */ -#define IOC4_SHADOW_RTS 0x02000000 /* Request to send */ -#define IOC4_SHADOW_OUT1 0x04000000 /* 16550 OUT1 bit */ -#define IOC4_SHADOW_OUT2 0x08000000 /* 16550 OUT2 bit */ -#define IOC4_SHADOW_LOOP 0x10000000 /* Loopback enabled */ - -/* Bitmasks for IOC4_SRTR_<3:0> */ -#define IOC4_SRTR_CNT 0x00000fff /* Reload value for RX timer */ -#define IOC4_SRTR_CNT_VAL 0x0fff0000 /* Current value of RX timer */ -#define IOC4_SRTR_CNT_VAL_SHIFT 16 -#define IOC4_SRTR_HZ 16000 /* SRTR clock frequency */ - -/* Serial port register map used for DMA and PIO serial I/O */ -struct ioc4_serialregs { - uint32_t sscr; - uint32_t stpir; - uint32_t stcir; - uint32_t srpir; - uint32_t srcir; - uint32_t srtr; - uint32_t shadow; -}; - -/* IOC4 UART register map */ -struct ioc4_uartregs { - char i4u_lcr; - union { - char iir; /* read only */ - char fcr; /* write only */ - } u3; - union { - char ier; /* DLAB == 0 */ - char dlm; /* DLAB == 1 */ - } u2; - union { - char rbr; /* read only, DLAB == 0 */ - char thr; /* write only, DLAB == 0 */ - char dll; /* DLAB == 1 */ - } u1; - char i4u_scr; - char i4u_msr; - char i4u_lsr; - char i4u_mcr; -}; - -/* short names */ -#define i4u_dll u1.dll -#define i4u_ier u2.ier -#define i4u_dlm u2.dlm -#define i4u_fcr u3.fcr - -/* Serial port registers used for DMA serial I/O */ -struct ioc4_serial { - uint32_t sbbr01_l; - uint32_t sbbr01_h; - uint32_t sbbr23_l; - uint32_t sbbr23_h; - - struct ioc4_serialregs port_0; - struct ioc4_serialregs port_1; - struct ioc4_serialregs port_2; - struct ioc4_serialregs port_3; - struct ioc4_uartregs uart_0; - struct ioc4_uartregs uart_1; - struct ioc4_uartregs uart_2; - struct ioc4_uartregs uart_3; -} ioc4_serial; - -/* UART clock speed */ -#define IOC4_SER_XIN_CLK_66 66666667 -#define IOC4_SER_XIN_CLK_33 33333333 - -#define IOC4_W_IES 0 -#define IOC4_W_IEC 1 - -typedef void ioc4_intr_func_f(void *, uint32_t); -typedef ioc4_intr_func_f *ioc4_intr_func_t; - -static unsigned int Num_of_ioc4_cards; - -/* defining this will get you LOTS of great debug info */ -//#define DEBUG_INTERRUPTS -#define DPRINT_CONFIG(_x...) ; -//#define DPRINT_CONFIG(_x...) printk _x - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* number of characters we want to transmit to the lower level at a time */ -#define IOC4_MAX_CHARS 256 -#define IOC4_FIFO_CHARS 255 - -/* Device name we're using */ -#define DEVICE_NAME_RS232 "ttyIOC" -#define DEVICE_NAME_RS422 "ttyAIOC" -#define DEVICE_MAJOR 204 -#define DEVICE_MINOR_RS232 50 -#define DEVICE_MINOR_RS422 84 - - -/* register offsets */ -#define IOC4_SERIAL_OFFSET 0x300 - -/* flags for next_char_state */ -#define NCS_BREAK 0x1 -#define NCS_PARITY 0x2 -#define NCS_FRAMING 0x4 -#define NCS_OVERRUN 0x8 - -/* cause we need SOME parameters ... */ -#define MIN_BAUD_SUPPORTED 1200 -#define MAX_BAUD_SUPPORTED 115200 - -/* protocol types supported */ -#define PROTO_RS232 3 -#define PROTO_RS422 7 - -/* Notification types */ -#define N_DATA_READY 0x01 -#define N_OUTPUT_LOWAT 0x02 -#define N_BREAK 0x04 -#define N_PARITY_ERROR 0x08 -#define N_FRAMING_ERROR 0x10 -#define N_OVERRUN_ERROR 0x20 -#define N_DDCD 0x40 -#define N_DCTS 0x80 - -#define N_ALL_INPUT (N_DATA_READY | N_BREAK | \ - N_PARITY_ERROR | N_FRAMING_ERROR | \ - N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define N_ALL_OUTPUT N_OUTPUT_LOWAT - -#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR | N_OVERRUN_ERROR) - -#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK | \ - N_PARITY_ERROR | N_FRAMING_ERROR | \ - N_OVERRUN_ERROR | N_DDCD | N_DCTS) - -#define SER_DIVISOR(_x, clk) (((clk) + (_x) * 8) / ((_x) * 16)) -#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) - -/* Some masks */ -#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ - | UART_LCR_WLEN7 | UART_LCR_WLEN8) -#define LCR_MASK_STOP_BITS (UART_LCR_STOP) - -#define PENDING(_p) (readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb) -#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw) - -/* Default to 4k buffers */ -#ifdef IOC4_1K_BUFFERS -#define RING_BUF_SIZE 1024 -#define IOC4_BUF_SIZE_BIT 0 -#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K -#else -#define RING_BUF_SIZE 4096 -#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE -#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K -#endif - -#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) - -/* - * This is the entry saved by the driver - one per card - */ - -#define UART_PORT_MIN 0 -#define UART_PORT_RS232 UART_PORT_MIN -#define UART_PORT_RS422 1 -#define UART_PORT_COUNT 2 /* one for each mode */ - -struct ioc4_control { - int ic_irq; - struct { - /* uart ports are allocated here - 1 for rs232, 1 for rs422 */ - struct uart_port icp_uart_port[UART_PORT_COUNT]; - /* Handy reference material */ - struct ioc4_port *icp_port; - } ic_port[IOC4_NUM_SERIAL_PORTS]; - struct ioc4_soft *ic_soft; -}; - -/* - * per-IOC4 data structure - */ -#define MAX_IOC4_INTR_ENTS (8 * sizeof(uint32_t)) -struct ioc4_soft { - struct ioc4_misc_regs __iomem *is_ioc4_misc_addr; - struct ioc4_serial __iomem *is_ioc4_serial_addr; - - /* Each interrupt type has an entry in the array */ - struct ioc4_intr_type { - - /* - * Each in-use entry in this array contains at least - * one nonzero bit in sd_bits; no two entries in this - * array have overlapping sd_bits values. - */ - struct ioc4_intr_info { - uint32_t sd_bits; - ioc4_intr_func_f *sd_intr; - void *sd_info; - } is_intr_info[MAX_IOC4_INTR_ENTS]; - - /* Number of entries active in the above array */ - atomic_t is_num_intrs; - } is_intr_type[IOC4_NUM_INTR_TYPES]; - - /* is_ir_lock must be held while - * modifying sio_ie values, so - * we can be sure that sio_ie is - * not changing when we read it - * along with sio_ir. - */ - spinlock_t is_ir_lock; /* SIO_IE[SC] mod lock */ -}; - -/* Local port info for each IOC4 serial ports */ -struct ioc4_port { - struct uart_port *ip_port; /* current active port ptr */ - /* Ptrs for all ports */ - struct uart_port *ip_all_ports[UART_PORT_COUNT]; - /* Back ptrs for this port */ - struct ioc4_control *ip_control; - struct pci_dev *ip_pdev; - struct ioc4_soft *ip_ioc4_soft; - - /* pci mem addresses */ - struct ioc4_misc_regs __iomem *ip_mem; - struct ioc4_serial __iomem *ip_serial; - struct ioc4_serialregs __iomem *ip_serial_regs; - struct ioc4_uartregs __iomem *ip_uart_regs; - - /* Ring buffer page for this port */ - dma_addr_t ip_dma_ringbuf; - /* vaddr of ring buffer */ - struct ring_buffer *ip_cpu_ringbuf; - - /* Rings for this port */ - struct ring *ip_inring; - struct ring *ip_outring; - - /* Hook to port specific values */ - struct hooks *ip_hooks; - - spinlock_t ip_lock; - - /* Various rx/tx parameters */ - int ip_baud; - int ip_tx_lowat; - int ip_rx_timeout; - - /* Copy of notification bits */ - int ip_notify; - - /* Shadow copies of various registers so we don't need to PIO - * read them constantly - */ - uint32_t ip_ienb; /* Enabled interrupts */ - uint32_t ip_sscr; - uint32_t ip_tx_prod; - uint32_t ip_rx_cons; - int ip_pci_bus_speed; - unsigned char ip_flags; -}; - -/* tx low water mark. We need to notify the driver whenever tx is getting - * close to empty so it can refill the tx buffer and keep things going. - * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll - * have no trouble getting in more chars in time (I certainly hope so). - */ -#define TX_LOWAT_LATENCY 1000 -#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY) -#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) - -/* Flags per port */ -#define INPUT_HIGH 0x01 -#define DCD_ON 0x02 -#define LOWAT_WRITTEN 0x04 -#define READ_ABORTED 0x08 -#define PORT_ACTIVE 0x10 -#define PORT_INACTIVE 0 /* This is the value when "off" */ - - -/* Since each port has different register offsets and bitmasks - * for everything, we'll store those that we need in tables so we - * don't have to be constantly checking the port we are dealing with. - */ -struct hooks { - uint32_t intr_delta_dcd; - uint32_t intr_delta_cts; - uint32_t intr_tx_mt; - uint32_t intr_rx_timer; - uint32_t intr_rx_high; - uint32_t intr_tx_explicit; - uint32_t intr_dma_error; - uint32_t intr_clear; - uint32_t intr_all; - int rs422_select_pin; -}; - -static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = { - /* Values for port 0 */ - { - IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS, - IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER, - IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT, - IOC4_OTHER_IR_S0_MEMERR, - (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL | - IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER | - IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS | - IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT), - IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN, - }, - - /* Values for port 1 */ - { - IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS, - IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER, - IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT, - IOC4_OTHER_IR_S1_MEMERR, - (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL | - IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER | - IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS | - IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT), - IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN, - }, - - /* Values for port 2 */ - { - IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS, - IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER, - IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT, - IOC4_OTHER_IR_S2_MEMERR, - (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL | - IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER | - IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS | - IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT), - IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN, - }, - - /* Values for port 3 */ - { - IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS, - IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER, - IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT, - IOC4_OTHER_IR_S3_MEMERR, - (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL | - IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER | - IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS | - IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT), - IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN, - } -}; - -/* A ring buffer entry */ -struct ring_entry { - union { - struct { - uint32_t alldata; - uint32_t allsc; - } all; - struct { - char data[4]; /* data bytes */ - char sc[4]; /* status/control */ - } s; - } u; -}; - -/* Test the valid bits in any of the 4 sc chars using "allsc" member */ -#define RING_ANY_VALID \ - ((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101) - -#define ring_sc u.s.sc -#define ring_data u.s.data -#define ring_allsc u.all.allsc - -/* Number of entries per ring buffer. */ -#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) - -/* An individual ring */ -struct ring { - struct ring_entry entries[ENTRIES_PER_RING]; -}; - -/* The whole enchilada */ -struct ring_buffer { - struct ring TX_0_OR_2; - struct ring RX_0_OR_2; - struct ring TX_1_OR_3; - struct ring RX_1_OR_3; -}; - -/* Get a ring from a port struct */ -#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) - -/* Infinite loop detection. - */ -#define MAXITER 10000000 - -/* Prototypes */ -static void receive_chars(struct uart_port *); -static void handle_intr(void *arg, uint32_t sio_ir); - -/* - * port_is_active - determines if this port is currently active - * @port: ptr to soft struct for this port - * @uart_port: uart port to test for - */ -static inline int port_is_active(struct ioc4_port *port, - struct uart_port *uart_port) -{ - if (port) { - if ((port->ip_flags & PORT_ACTIVE) - && (port->ip_port == uart_port)) - return 1; - } - return 0; -} - - -/** - * write_ireg - write the interrupt regs - * @ioc4_soft: ptr to soft struct for this port - * @val: value to write - * @which: which register - * @type: which ireg set - */ -static inline void -write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type) -{ - struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr; - unsigned long flags; - - spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags); - - switch (type) { - case IOC4_SIO_INTR_TYPE: - switch (which) { - case IOC4_W_IES: - writel(val, &mem->sio_ies.raw); - break; - - case IOC4_W_IEC: - writel(val, &mem->sio_iec.raw); - break; - } - break; - - case IOC4_OTHER_INTR_TYPE: - switch (which) { - case IOC4_W_IES: - writel(val, &mem->other_ies.raw); - break; - - case IOC4_W_IEC: - writel(val, &mem->other_iec.raw); - break; - } - break; - - default: - break; - } - spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags); -} - -/** - * set_baud - Baud rate setting code - * @port: port to set - * @baud: baud rate to use - */ -static int set_baud(struct ioc4_port *port, int baud) -{ - int actual_baud; - int diff; - int lcr; - unsigned short divisor; - struct ioc4_uartregs __iomem *uart; - - divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed); - if (!divisor) - return 1; - actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed); - - diff = actual_baud - baud; - if (diff < 0) - diff = -diff; - - /* If we're within 1%, we've found a match */ - if (diff * 100 > actual_baud) - return 1; - - uart = port->ip_uart_regs; - lcr = readb(&uart->i4u_lcr); - writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr); - writeb((unsigned char)divisor, &uart->i4u_dll); - writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm); - writeb(lcr, &uart->i4u_lcr); - return 0; -} - - -/** - * get_ioc4_port - given a uart port, return the control structure - * @port: uart port - * @set: set this port as current - */ -static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set) -{ - struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev); - struct ioc4_control *control = idd->idd_serial_data; - struct ioc4_port *port; - int port_num, port_type; - - if (control) { - for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; - port_num++ ) { - port = control->ic_port[port_num].icp_port; - if (!port) - continue; - for (port_type = UART_PORT_MIN; - port_type < UART_PORT_COUNT; - port_type++) { - if (the_port == port->ip_all_ports - [port_type]) { - /* set local copy */ - if (set) { - port->ip_port = the_port; - } - return port; - } - } - } - } - return NULL; -} - -/* The IOC4 hardware provides no atomic way to determine if interrupts - * are pending since two reads are required to do so. The handler must - * read the SIO_IR and the SIO_IES, and take the logical and of the - * two. When this value is zero, all interrupts have been serviced and - * the handler may return. - * - * This has the unfortunate "hole" that, if some other CPU or - * some other thread or some higher level interrupt manages to - * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may - * think we have observed SIO_IR&SIO_IE==0 when in fact this - * condition never really occurred. - * - * To solve this, we use a simple spinlock that must be held - * whenever modifying SIO_IE; holding this lock while observing - * both SIO_IR and SIO_IE guarantees that we do not falsely - * conclude that no enabled interrupts are pending. - */ - -static inline uint32_t -pending_intrs(struct ioc4_soft *soft, int type) -{ - struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; - unsigned long flag; - uint32_t intrs = 0; - - BUG_ON(!((type == IOC4_SIO_INTR_TYPE) - || (type == IOC4_OTHER_INTR_TYPE))); - - spin_lock_irqsave(&soft->is_ir_lock, flag); - - switch (type) { - case IOC4_SIO_INTR_TYPE: - intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw); - break; - - case IOC4_OTHER_INTR_TYPE: - intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw); - - /* Don't process any ATA interrupte */ - intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); - break; - - default: - break; - } - spin_unlock_irqrestore(&soft->is_ir_lock, flag); - return intrs; -} - -/** - * port_init - Initialize the sio and ioc4 hardware for a given port - * called per port from attach... - * @port: port to initialize - */ -static int inline port_init(struct ioc4_port *port) -{ - uint32_t sio_cr; - struct hooks *hooks = port->ip_hooks; - struct ioc4_uartregs __iomem *uart; - - /* Idle the IOC4 serial interface */ - writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr); - - /* Wait until any pending bus activity for this port has ceased */ - do - sio_cr = readl(&port->ip_mem->sio_cr.raw); - while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE)); - - /* Finish reset sequence */ - writel(0, &port->ip_serial_regs->sscr); - - /* Once RESET is done, reload cached tx_prod and rx_cons values - * and set rings to empty by making prod == cons - */ - port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); - port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); - - /* Disable interrupts for this 16550 */ - uart = port->ip_uart_regs; - writeb(0, &uart->i4u_lcr); - writeb(0, &uart->i4u_ier); - - /* Set the default baud */ - set_baud(port, port->ip_baud); - - /* Set line control to 8 bits no parity */ - writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Enable the FIFOs */ - writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr); - /* then reset 16550 FIFOs */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - &uart->i4u_fcr); - - /* Clear modem control register */ - writeb(0, &uart->i4u_mcr); - - /* Clear deltas in modem status register */ - readb(&uart->i4u_msr); - - /* Only do this once per port pair */ - if (port->ip_hooks == &hooks_array[0] - || port->ip_hooks == &hooks_array[2]) { - unsigned long ring_pci_addr; - uint32_t __iomem *sbbr_l; - uint32_t __iomem *sbbr_h; - - if (port->ip_hooks == &hooks_array[0]) { - sbbr_l = &port->ip_serial->sbbr01_l; - sbbr_h = &port->ip_serial->sbbr01_h; - } else { - sbbr_l = &port->ip_serial->sbbr23_l; - sbbr_h = &port->ip_serial->sbbr23_h; - } - - ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; - DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n", - __func__, ring_pci_addr)); - - writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h); - writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l); - } - - /* Set the receive timeout value to 10 msec */ - writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr); - - /* Set rx threshold, enable DMA */ - /* Set high water mark at 3/4 of full ring */ - port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Disable and clear all serial related interrupt bits */ - write_ireg(port->ip_ioc4_soft, hooks->intr_clear, - IOC4_W_IEC, IOC4_SIO_INTR_TYPE); - port->ip_ienb &= ~hooks->intr_clear; - writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw); - return 0; -} - -/** - * handle_dma_error_intr - service any pending DMA error interrupts for the - * given port - 2nd level called via sd_intr - * @arg: handler arg - * @other_ir: ioc4regs - */ -static void handle_dma_error_intr(void *arg, uint32_t other_ir) -{ - struct ioc4_port *port = (struct ioc4_port *)arg; - struct hooks *hooks = port->ip_hooks; - unsigned long flags; - - spin_lock_irqsave(&port->ip_lock, flags); - - /* ACK the interrupt */ - writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw); - - if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) { - printk(KERN_ERR - "PCI error address is 0x%llx, " - "master is serial port %c %s\n", - (((uint64_t)readl(&port->ip_mem->pci_err_addr_h) - << 32) - | readl(&port->ip_mem->pci_err_addr_l.raw)) - & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' + - ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) & - IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1), - (readl(&port->ip_mem->pci_err_addr_l.raw) - & IOC4_PCI_ERR_ADDR_MST_TYP_MSK) - ? "RX" : "TX"); - - if (readl(&port->ip_mem->pci_err_addr_l.raw) - & IOC4_PCI_ERR_ADDR_MUL_ERR) { - printk(KERN_ERR - "Multiple errors occurred\n"); - } - } - spin_unlock_irqrestore(&port->ip_lock, flags); - - /* Re-enable DMA error interrupts */ - write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES, - IOC4_OTHER_INTR_TYPE); -} - -/** - * intr_connect - interrupt connect function - * @soft: soft struct for this card - * @type: interrupt type - * @intrbits: bit pattern to set - * @intr: handler function - * @info: handler arg - */ -static void -intr_connect(struct ioc4_soft *soft, int type, - uint32_t intrbits, ioc4_intr_func_f * intr, void *info) -{ - int i; - struct ioc4_intr_info *intr_ptr; - - BUG_ON(!((type == IOC4_SIO_INTR_TYPE) - || (type == IOC4_OTHER_INTR_TYPE))); - - i = atomic_inc(&soft-> is_intr_type[type].is_num_intrs) - 1; - BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0))); - - /* Save off the lower level interrupt handler */ - intr_ptr = &soft->is_intr_type[type].is_intr_info[i]; - intr_ptr->sd_bits = intrbits; - intr_ptr->sd_intr = intr; - intr_ptr->sd_info = info; -} - -/** - * ioc4_intr - Top level IOC4 interrupt handler. - * @irq: irq value - * @arg: handler arg - */ - -static irqreturn_t ioc4_intr(int irq, void *arg) -{ - struct ioc4_soft *soft; - uint32_t this_ir, this_mir; - int xx, num_intrs = 0; - int intr_type; - int handled = 0; - struct ioc4_intr_info *intr_info; - - soft = arg; - for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) { - num_intrs = (int)atomic_read( - &soft->is_intr_type[intr_type].is_num_intrs); - - this_mir = this_ir = pending_intrs(soft, intr_type); - - /* Farm out the interrupt to the various drivers depending on - * which interrupt bits are set. - */ - for (xx = 0; xx < num_intrs; xx++) { - intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx]; - if ((this_mir = this_ir & intr_info->sd_bits)) { - /* Disable owned interrupts, call handler */ - handled++; - write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC, - intr_type); - intr_info->sd_intr(intr_info->sd_info, this_mir); - this_ir &= ~this_mir; - } - } - } -#ifdef DEBUG_INTERRUPTS - { - struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; - unsigned long flag; - - spin_lock_irqsave(&soft->is_ir_lock, flag); - printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x " - "other_ir 0x%x other_ies 0x%x mask 0x%x\n", - __func__, __LINE__, - (void *)mem, readl(&mem->sio_ir.raw), - readl(&mem->sio_ies.raw), - readl(&mem->other_ir.raw), - readl(&mem->other_ies.raw), - IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); - spin_unlock_irqrestore(&soft->is_ir_lock, flag); - } -#endif - return handled ? IRQ_HANDLED : IRQ_NONE; -} - -/** - * ioc4_attach_local - Device initialization. - * Called at *_attach() time for each - * IOC4 with serial ports in the system. - * @idd: Master module data for this IOC4 - */ -static int inline ioc4_attach_local(struct ioc4_driver_data *idd) -{ - struct ioc4_port *port; - struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; - int port_number; - uint16_t ioc4_revid_min = 62; - uint16_t ioc4_revid; - struct pci_dev *pdev = idd->idd_pdev; - struct ioc4_control* control = idd->idd_serial_data; - struct ioc4_soft *soft = control->ic_soft; - void __iomem *ioc4_misc = idd->idd_misc_regs; - void __iomem *ioc4_serial = soft->is_ioc4_serial_addr; - - /* IOC4 firmware must be at least rev 62 */ - pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid); - - printk(KERN_INFO "IOC4 firmware revision %d\n", ioc4_revid); - if (ioc4_revid < ioc4_revid_min) { - printk(KERN_WARNING - "IOC4 serial not supported on firmware rev %d, " - "please upgrade to rev %d or higher\n", - ioc4_revid, ioc4_revid_min); - return -EPERM; - } - BUG_ON(ioc4_misc == NULL); - BUG_ON(ioc4_serial == NULL); - - /* Create port structures for each port */ - for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS; - port_number++) { - port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL); - if (!port) { - printk(KERN_WARNING - "IOC4 serial memory not available for port\n"); - return -ENOMEM; - } - spin_lock_init(&port->ip_lock); - - /* we need to remember the previous ones, to point back to - * them farther down - setting up the ring buffers. - */ - ports[port_number] = port; - - /* Allocate buffers and jumpstart the hardware. */ - control->ic_port[port_number].icp_port = port; - port->ip_ioc4_soft = soft; - port->ip_pdev = pdev; - port->ip_ienb = 0; - /* Use baud rate calculations based on detected PCI - * bus speed. Simply test whether the PCI clock is - * running closer to 66MHz or 33MHz. - */ - if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) { - port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66; - } else { - port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33; - } - port->ip_baud = 9600; - port->ip_control = control; - port->ip_mem = ioc4_misc; - port->ip_serial = ioc4_serial; - - /* point to the right hook */ - port->ip_hooks = &hooks_array[port_number]; - - /* Get direct hooks to the serial regs and uart regs - * for this port - */ - switch (port_number) { - case 0: - port->ip_serial_regs = &(port->ip_serial->port_0); - port->ip_uart_regs = &(port->ip_serial->uart_0); - break; - case 1: - port->ip_serial_regs = &(port->ip_serial->port_1); - port->ip_uart_regs = &(port->ip_serial->uart_1); - break; - case 2: - port->ip_serial_regs = &(port->ip_serial->port_2); - port->ip_uart_regs = &(port->ip_serial->uart_2); - break; - default: - case 3: - port->ip_serial_regs = &(port->ip_serial->port_3); - port->ip_uart_regs = &(port->ip_serial->uart_3); - break; - } - - /* ring buffers are 1 to a pair of ports */ - if (port_number && (port_number & 1)) { - /* odd use the evens buffer */ - port->ip_dma_ringbuf = - ports[port_number - 1]->ip_dma_ringbuf; - port->ip_cpu_ringbuf = - ports[port_number - 1]->ip_cpu_ringbuf; - port->ip_inring = RING(port, RX_1_OR_3); - port->ip_outring = RING(port, TX_1_OR_3); - - } else { - if (port->ip_dma_ringbuf == 0) { - port->ip_cpu_ringbuf = pci_alloc_consistent - (pdev, TOTAL_RING_BUF_SIZE, - &port->ip_dma_ringbuf); - - } - BUG_ON(!((((int64_t)port->ip_dma_ringbuf) & - (TOTAL_RING_BUF_SIZE - 1)) == 0)); - DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p " - "ip_dma_ringbuf 0x%p\n", - __func__, - (void *)port->ip_cpu_ringbuf, - (void *)port->ip_dma_ringbuf)); - port->ip_inring = RING(port, RX_0_OR_2); - port->ip_outring = RING(port, TX_0_OR_2); - } - DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p", - __func__, - port_number, (void *)port, (void *)control)); - DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", - (void *)port->ip_serial_regs, - (void *)port->ip_uart_regs)); - - /* Initialize the hardware for IOC4 */ - port_init(port); - - DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p " - "outring 0x%p\n", - __func__, - port_number, (void *)port, - (void *)port->ip_inring, - (void *)port->ip_outring)); - - /* Attach interrupt handlers */ - intr_connect(soft, IOC4_SIO_INTR_TYPE, - GET_SIO_IR(port_number), - handle_intr, port); - - intr_connect(soft, IOC4_OTHER_INTR_TYPE, - GET_OTHER_IR(port_number), - handle_dma_error_intr, port); - } - return 0; -} - -/** - * enable_intrs - enable interrupts - * @port: port to enable - * @mask: mask to use - */ -static void enable_intrs(struct ioc4_port *port, uint32_t mask) -{ - struct hooks *hooks = port->ip_hooks; - - if ((port->ip_ienb & mask) != mask) { - write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IES, - IOC4_SIO_INTR_TYPE); - port->ip_ienb |= mask; - } - - if (port->ip_ienb) - write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, - IOC4_W_IES, IOC4_OTHER_INTR_TYPE); -} - -/** - * local_open - local open a port - * @port: port to open - */ -static inline int local_open(struct ioc4_port *port) -{ - int spiniter = 0; - - port->ip_flags = PORT_ACTIVE; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while((readl(&port->ip_serial_regs-> sscr) - & IOC4_SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) { - port->ip_flags = PORT_INACTIVE; - return -1; - } - } - } - - /* Reset the input fifo. If the uart received chars while the port - * was closed and DMA is not enabled, the uart may have a bunch of - * chars hanging around in its rx fifo which will not be discarded - * by rclr in the upper layer. We must get rid of them here. - */ - writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, - &port->ip_uart_regs->i4u_fcr); - - writeb(UART_LCR_WLEN8, &port->ip_uart_regs->i4u_lcr); - /* UART_LCR_STOP == 1 stop */ - - /* Re-enable DMA, set default threshold to intr whenever there is - * data available. - */ - port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; - port->ip_sscr |= 1; /* default threshold */ - - /* Plug in the new sscr. This implicitly clears the DMA_PAUSE - * flag if it was set above - */ - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - port->ip_tx_lowat = 1; - return 0; -} - -/** - * set_rx_timeout - Set rx timeout and threshold values. - * @port: port to use - * @timeout: timeout value in ticks - */ -static inline int set_rx_timeout(struct ioc4_port *port, int timeout) -{ - int threshold; - - port->ip_rx_timeout = timeout; - - /* Timeout is in ticks. Let's figure out how many chars we - * can receive at the current baud rate in that interval - * and set the rx threshold to that amount. There are 4 chars - * per ring entry, so we'll divide the number of chars that will - * arrive in timeout by 4. - * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. - */ - threshold = timeout * port->ip_baud / 4000; - if (threshold == 0) - threshold = 1; /* otherwise we'll intr all the time! */ - - if ((unsigned)threshold > (unsigned)IOC4_SSCR_RX_THRESHOLD) - return 1; - - port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; - port->ip_sscr |= threshold; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Now set the rx timeout to the given value - * again timeout * IOC4_SRTR_HZ / HZ - */ - timeout = timeout * IOC4_SRTR_HZ / 100; - if (timeout > IOC4_SRTR_CNT) - timeout = IOC4_SRTR_CNT; - - writel(timeout, &port->ip_serial_regs->srtr); - return 0; -} - -/** - * config_port - config the hardware - * @port: port to config - * @baud: baud rate for the port - * @byte_size: data size - * @stop_bits: number of stop bits - * @parenb: parity enable ? - * @parodd: odd parity ? - */ -static inline int -config_port(struct ioc4_port *port, - int baud, int byte_size, int stop_bits, int parenb, int parodd) -{ - char lcr, sizebits; - int spiniter = 0; - - DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n", - __func__, baud, byte_size, stop_bits, parenb, parodd)); - - if (set_baud(port, baud)) - return 1; - - switch (byte_size) { - case 5: - sizebits = UART_LCR_WLEN5; - break; - case 6: - sizebits = UART_LCR_WLEN6; - break; - case 7: - sizebits = UART_LCR_WLEN7; - break; - case 8: - sizebits = UART_LCR_WLEN8; - break; - default: - return 1; - } - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while((readl(&port->ip_serial_regs->sscr) - & IOC4_SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - - /* Clear relevant fields in lcr */ - lcr = readb(&port->ip_uart_regs->i4u_lcr); - lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | - UART_LCR_PARITY | LCR_MASK_STOP_BITS); - - /* Set byte size in lcr */ - lcr |= sizebits; - - /* Set parity */ - if (parenb) { - lcr |= UART_LCR_PARITY; - if (!parodd) - lcr |= UART_LCR_EPAR; - } - - /* Set stop bits */ - if (stop_bits) - lcr |= UART_LCR_STOP /* 2 stop bits */ ; - - writeb(lcr, &port->ip_uart_regs->i4u_lcr); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - port->ip_baud = baud; - - /* When we get within this number of ring entries of filling the - * entire ring on tx, place an EXPLICIT intr to generate a lowat - * notification when output has drained. - */ - port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; - if (port->ip_tx_lowat == 0) - port->ip_tx_lowat = 1; - - set_rx_timeout(port, 2); - - return 0; -} - -/** - * do_write - Write bytes to the port. Returns the number of bytes - * actually written. Called from transmit_chars - * @port: port to use - * @buf: the stuff to write - * @len: how many bytes in 'buf' - */ -static inline int do_write(struct ioc4_port *port, char *buf, int len) -{ - int prod_ptr, cons_ptr, total = 0; - struct ring *outring; - struct ring_entry *entry; - struct hooks *hooks = port->ip_hooks; - - BUG_ON(!(len >= 0)); - - prod_ptr = port->ip_tx_prod; - cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; - outring = port->ip_outring; - - /* Maintain a 1-entry red-zone. The ring buffer is full when - * (cons - prod) % ring_size is 1. Rather than do this subtraction - * in the body of the loop, I'll do it now. - */ - cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; - - /* Stuff the bytes into the output */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - int xx; - - /* Get 4 bytes (one ring entry) at a time */ - entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); - - /* Invalidate all entries */ - entry->ring_allsc = 0; - - /* Copy in some bytes */ - for (xx = 0; (xx < 4) && (len > 0); xx++) { - entry->ring_data[xx] = *buf++; - entry->ring_sc[xx] = IOC4_TXCB_VALID; - len--; - total++; - } - - /* If we are within some small threshold of filling up the - * entire ring buffer, we must place an EXPLICIT intr here - * to generate a lowat interrupt in case we subsequently - * really do fill up the ring and the caller goes to sleep. - * No need to place more than one though. - */ - if (!(port->ip_flags & LOWAT_WRITTEN) && - ((cons_ptr - prod_ptr) & PROD_CONS_MASK) - <= port->ip_tx_lowat - * (int)sizeof(struct ring_entry)) { - port->ip_flags |= LOWAT_WRITTEN; - entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE; - } - - /* Go on to next entry */ - prod_ptr += sizeof(struct ring_entry); - prod_ptr &= PROD_CONS_MASK; - } - - /* If we sent something, start DMA if necessary */ - if (total > 0 && !(port->ip_sscr & IOC4_SSCR_DMA_EN)) { - port->ip_sscr |= IOC4_SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - - /* Store the new producer pointer. If tx is disabled, we stuff the - * data into the ring buffer, but we don't actually start tx. - */ - if (!uart_tx_stopped(port->ip_port)) { - writel(prod_ptr, &port->ip_serial_regs->stpir); - - /* If we are now transmitting, enable tx_mt interrupt so we - * can disable DMA if necessary when the tx finishes. - */ - if (total > 0) - enable_intrs(port, hooks->intr_tx_mt); - } - port->ip_tx_prod = prod_ptr; - return total; -} - -/** - * disable_intrs - disable interrupts - * @port: port to enable - * @mask: mask to use - */ -static void disable_intrs(struct ioc4_port *port, uint32_t mask) -{ - struct hooks *hooks = port->ip_hooks; - - if (port->ip_ienb & mask) { - write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IEC, - IOC4_SIO_INTR_TYPE); - port->ip_ienb &= ~mask; - } - - if (!port->ip_ienb) - write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, - IOC4_W_IEC, IOC4_OTHER_INTR_TYPE); -} - -/** - * set_notification - Modify event notification - * @port: port to use - * @mask: events mask - * @set_on: set ? - */ -static int set_notification(struct ioc4_port *port, int mask, int set_on) -{ - struct hooks *hooks = port->ip_hooks; - uint32_t intrbits, sscrbits; - - BUG_ON(!mask); - - intrbits = sscrbits = 0; - - if (mask & N_DATA_READY) - intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); - if (mask & N_OUTPUT_LOWAT) - intrbits |= hooks->intr_tx_explicit; - if (mask & N_DDCD) { - intrbits |= hooks->intr_delta_dcd; - sscrbits |= IOC4_SSCR_RX_RING_DCD; - } - if (mask & N_DCTS) - intrbits |= hooks->intr_delta_cts; - - if (set_on) { - enable_intrs(port, intrbits); - port->ip_notify |= mask; - port->ip_sscr |= sscrbits; - } else { - disable_intrs(port, intrbits); - port->ip_notify &= ~mask; - port->ip_sscr &= ~sscrbits; - } - - /* We require DMA if either DATA_READY or DDCD notification is - * currently requested. If neither of these is requested and - * there is currently no tx in progress, DMA may be disabled. - */ - if (port->ip_notify & (N_DATA_READY | N_DDCD)) - port->ip_sscr |= IOC4_SSCR_DMA_EN; - else if (!(port->ip_ienb & hooks->intr_tx_mt)) - port->ip_sscr &= ~IOC4_SSCR_DMA_EN; - - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - return 0; -} - -/** - * set_mcr - set the master control reg - * @the_port: port to use - * @mask1: mcr mask - * @mask2: shadow mask - */ -static inline int set_mcr(struct uart_port *the_port, - int mask1, int mask2) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - uint32_t shadow; - int spiniter = 0; - char mcr; - - if (!port) - return -1; - - /* Pause the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, - &port->ip_serial_regs->sscr); - while ((readl(&port->ip_serial_regs->sscr) - & IOC4_SSCR_PAUSE_STATE) == 0) { - spiniter++; - if (spiniter > MAXITER) - return -1; - } - } - shadow = readl(&port->ip_serial_regs->shadow); - mcr = (shadow & 0xff000000) >> 24; - - /* Set new value */ - mcr |= mask1; - shadow |= mask2; - - writeb(mcr, &port->ip_uart_regs->i4u_mcr); - writel(shadow, &port->ip_serial_regs->shadow); - - /* Re-enable the DMA interface if necessary */ - if (port->ip_sscr & IOC4_SSCR_DMA_EN) { - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - return 0; -} - -/** - * ioc4_set_proto - set the protocol for the port - * @port: port to use - * @proto: protocol to use - */ -static int ioc4_set_proto(struct ioc4_port *port, int proto) -{ - struct hooks *hooks = port->ip_hooks; - - switch (proto) { - case PROTO_RS232: - /* Clear the appropriate GIO pin */ - writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw)); - break; - - case PROTO_RS422: - /* Set the appropriate GIO pin */ - writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw)); - break; - - default: - return 1; - } - return 0; -} - -/** - * transmit_chars - upper level write, called with ip_lock - * @the_port: port to write - */ -static void transmit_chars(struct uart_port *the_port) -{ - int xmit_count, tail, head; - int result; - char *start; - struct tty_struct *tty; - struct ioc4_port *port = get_ioc4_port(the_port, 0); - struct uart_state *state; - - if (!the_port) - return; - if (!port) - return; - - state = the_port->state; - tty = state->port.tty; - - if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { - /* Nothing to do or hw stopped */ - set_notification(port, N_ALL_OUTPUT, 0); - return; - } - - head = state->xmit.head; - tail = state->xmit.tail; - start = (char *)&state->xmit.buf[tail]; - - /* write out all the data or until the end of the buffer */ - xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); - if (xmit_count > 0) { - result = do_write(port, start, xmit_count); - if (result > 0) { - /* booking */ - xmit_count -= result; - the_port->icount.tx += result; - /* advance the pointers */ - tail += result; - tail &= UART_XMIT_SIZE - 1; - state->xmit.tail = tail; - start = (char *)&state->xmit.buf[tail]; - } - } - if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) - uart_write_wakeup(the_port); - - if (uart_circ_empty(&state->xmit)) { - set_notification(port, N_OUTPUT_LOWAT, 0); - } else { - set_notification(port, N_OUTPUT_LOWAT, 1); - } -} - -/** - * ioc4_change_speed - change the speed of the port - * @the_port: port to change - * @new_termios: new termios settings - * @old_termios: old termios settings - */ -static void -ioc4_change_speed(struct uart_port *the_port, - struct ktermios *new_termios, struct ktermios *old_termios) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - int baud, bits; - unsigned cflag, iflag; - int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; - struct uart_state *state = the_port->state; - - cflag = new_termios->c_cflag; - iflag = new_termios->c_iflag; - - switch (cflag & CSIZE) { - case CS5: - new_data = 5; - bits = 7; - break; - case CS6: - new_data = 6; - bits = 8; - break; - case CS7: - new_data = 7; - bits = 9; - break; - case CS8: - new_data = 8; - bits = 10; - break; - default: - /* cuz we always need a default ... */ - new_data = 5; - bits = 7; - break; - } - if (cflag & CSTOPB) { - bits++; - new_stop = 1; - } - if (cflag & PARENB) { - bits++; - new_parity_enable = 1; - if (cflag & PARODD) - new_parity = 1; - } - baud = uart_get_baud_rate(the_port, new_termios, old_termios, - MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); - DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud)); - - /* default is 9600 */ - if (!baud) - baud = 9600; - - if (!the_port->fifosize) - the_port->fifosize = IOC4_FIFO_CHARS; - the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10)); - the_port->timeout += HZ / 50; /* Add .02 seconds of slop */ - - the_port->ignore_status_mask = N_ALL_INPUT; - - state->port.tty->low_latency = 1; - - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~(N_PARITY_ERROR - | N_FRAMING_ERROR); - if (iflag & IGNBRK) { - the_port->ignore_status_mask &= ~N_BREAK; - if (iflag & IGNPAR) - the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; - } - if (!(cflag & CREAD)) { - /* ignore everything */ - the_port->ignore_status_mask &= ~N_DATA_READY; - } - - if (cflag & CRTSCTS) { - port->ip_sscr |= IOC4_SSCR_HFC_EN; - } - else { - port->ip_sscr &= ~IOC4_SSCR_HFC_EN; - } - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - - /* Set the configuration and proper notification call */ - DPRINT_CONFIG(("%s : port 0x%p cflag 0%o " - "config_port(baud %d data %d stop %d p enable %d parity %d)," - " notification 0x%x\n", - __func__, (void *)port, cflag, baud, new_data, new_stop, - new_parity_enable, new_parity, the_port->ignore_status_mask)); - - if ((config_port(port, baud, /* baud */ - new_data, /* byte size */ - new_stop, /* stop bits */ - new_parity_enable, /* set parity */ - new_parity)) >= 0) { /* parity 1==odd */ - set_notification(port, the_port->ignore_status_mask, 1); - } -} - -/** - * ic4_startup_local - Start up the serial port - returns >= 0 if no errors - * @the_port: Port to operate on - */ -static inline int ic4_startup_local(struct uart_port *the_port) -{ - struct ioc4_port *port; - struct uart_state *state; - - if (!the_port) - return -1; - - port = get_ioc4_port(the_port, 0); - if (!port) - return -1; - - state = the_port->state; - - local_open(port); - - /* set the protocol - mapbase has the port type */ - ioc4_set_proto(port, the_port->mapbase); - - /* set the speed of the serial port */ - ioc4_change_speed(the_port, state->port.tty->termios, - (struct ktermios *)0); - - return 0; -} - -/* - * ioc4_cb_output_lowat - called when the output low water mark is hit - * @the_port: port to output - */ -static void ioc4_cb_output_lowat(struct uart_port *the_port) -{ - unsigned long pflags; - - /* ip_lock is set on the call here */ - if (the_port) { - spin_lock_irqsave(&the_port->lock, pflags); - transmit_chars(the_port); - spin_unlock_irqrestore(&the_port->lock, pflags); - } -} - -/** - * handle_intr - service any interrupts for the given port - 2nd level - * called via sd_intr - * @arg: handler arg - * @sio_ir: ioc4regs - */ -static void handle_intr(void *arg, uint32_t sio_ir) -{ - struct ioc4_port *port = (struct ioc4_port *)arg; - struct hooks *hooks = port->ip_hooks; - unsigned int rx_high_rd_aborted = 0; - unsigned long flags; - struct uart_port *the_port; - int loop_counter; - - /* Possible race condition here: The tx_mt interrupt bit may be - * cleared without the intervention of the interrupt handler, - * e.g. by a write. If the top level interrupt handler reads a - * tx_mt, then some other processor does a write, starting up - * output, then we come in here, see the tx_mt and stop DMA, the - * output started by the other processor will hang. Thus we can - * only rely on tx_mt being legitimate if it is read while the - * port lock is held. Therefore this bit must be ignored in the - * passed in interrupt mask which was read by the top level - * interrupt handler since the port lock was not held at the time - * it was read. We can only rely on this bit being accurate if it - * is read while the port lock is held. So we'll clear it for now, - * and reload it later once we have the port lock. - */ - sio_ir &= ~(hooks->intr_tx_mt); - - spin_lock_irqsave(&port->ip_lock, flags); - - loop_counter = MAXITER; /* to avoid hangs */ - - do { - uint32_t shadow; - - if ( loop_counter-- <= 0 ) { - printk(KERN_WARNING "IOC4 serial: " - "possible hang condition/" - "port stuck on interrupt.\n"); - break; - } - - /* Handle a DCD change */ - if (sio_ir & hooks->intr_delta_dcd) { - /* ACK the interrupt */ - writel(hooks->intr_delta_dcd, - &port->ip_mem->sio_ir.raw); - - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DDCD) - && (shadow & IOC4_SHADOW_DCD) - && (port->ip_port)) { - the_port = port->ip_port; - the_port->icount.dcd = 1; - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } else if ((port->ip_notify & N_DDCD) - && !(shadow & IOC4_SHADOW_DCD)) { - /* Flag delta DCD/no DCD */ - port->ip_flags |= DCD_ON; - } - } - - /* Handle a CTS change */ - if (sio_ir & hooks->intr_delta_cts) { - /* ACK the interrupt */ - writel(hooks->intr_delta_cts, - &port->ip_mem->sio_ir.raw); - - shadow = readl(&port->ip_serial_regs->shadow); - - if ((port->ip_notify & N_DCTS) - && (port->ip_port)) { - the_port = port->ip_port; - the_port->icount.cts = - (shadow & IOC4_SHADOW_CTS) ? 1 : 0; - wake_up_interruptible - (&the_port->state->port.delta_msr_wait); - } - } - - /* rx timeout interrupt. Must be some data available. Put this - * before the check for rx_high since servicing this condition - * may cause that condition to clear. - */ - if (sio_ir & hooks->intr_rx_timer) { - /* ACK the interrupt */ - writel(hooks->intr_rx_timer, - &port->ip_mem->sio_ir.raw); - - if ((port->ip_notify & N_DATA_READY) - && (port->ip_port)) { - /* ip_lock is set on call here */ - receive_chars(port->ip_port); - } - } - - /* rx high interrupt. Must be after rx_timer. */ - else if (sio_ir & hooks->intr_rx_high) { - /* Data available, notify upper layer */ - if ((port->ip_notify & N_DATA_READY) - && port->ip_port) { - /* ip_lock is set on call here */ - receive_chars(port->ip_port); - } - - /* We can't ACK this interrupt. If receive_chars didn't - * cause the condition to clear, we'll have to disable - * the interrupt until the data is drained. - * If the read was aborted, don't disable the interrupt - * as this may cause us to hang indefinitely. An - * aborted read generally means that this interrupt - * hasn't been delivered to the cpu yet anyway, even - * though we see it as asserted when we read the sio_ir. - */ - if ((sio_ir = PENDING(port)) & hooks->intr_rx_high) { - if ((port->ip_flags & READ_ABORTED) == 0) { - port->ip_ienb &= ~hooks->intr_rx_high; - port->ip_flags |= INPUT_HIGH; - } else { - rx_high_rd_aborted++; - } - } - } - - /* We got a low water interrupt: notify upper layer to - * send more data. Must come before tx_mt since servicing - * this condition may cause that condition to clear. - */ - if (sio_ir & hooks->intr_tx_explicit) { - port->ip_flags &= ~LOWAT_WRITTEN; - - /* ACK the interrupt */ - writel(hooks->intr_tx_explicit, - &port->ip_mem->sio_ir.raw); - - if (port->ip_notify & N_OUTPUT_LOWAT) - ioc4_cb_output_lowat(port->ip_port); - } - - /* Handle tx_mt. Must come after tx_explicit. */ - else if (sio_ir & hooks->intr_tx_mt) { - /* If we are expecting a lowat notification - * and we get to this point it probably means that for - * some reason the tx_explicit didn't work as expected - * (that can legitimately happen if the output buffer is - * filled up in just the right way). - * So send the notification now. - */ - if (port->ip_notify & N_OUTPUT_LOWAT) { - ioc4_cb_output_lowat(port->ip_port); - - /* We need to reload the sio_ir since the lowat - * call may have caused another write to occur, - * clearing the tx_mt condition. - */ - sio_ir = PENDING(port); - } - - /* If the tx_mt condition still persists even after the - * lowat call, we've got some work to do. - */ - if (sio_ir & hooks->intr_tx_mt) { - - /* If we are not currently expecting DMA input, - * and the transmitter has just gone idle, - * there is no longer any reason for DMA, so - * disable it. - */ - if (!(port->ip_notify - & (N_DATA_READY | N_DDCD))) { - BUG_ON(!(port->ip_sscr - & IOC4_SSCR_DMA_EN)); - port->ip_sscr &= ~IOC4_SSCR_DMA_EN; - writel(port->ip_sscr, - &port->ip_serial_regs->sscr); - } - - /* Prevent infinite tx_mt interrupt */ - port->ip_ienb &= ~hooks->intr_tx_mt; - } - } - sio_ir = PENDING(port); - - /* if the read was aborted and only hooks->intr_rx_high, - * clear hooks->intr_rx_high, so we do not loop forever. - */ - - if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { - sio_ir &= ~hooks->intr_rx_high; - } - } while (sio_ir & hooks->intr_all); - - spin_unlock_irqrestore(&port->ip_lock, flags); - - /* Re-enable interrupts before returning from interrupt handler. - * Getting interrupted here is okay. It'll just v() our semaphore, and - * we'll come through the loop again. - */ - - write_ireg(port->ip_ioc4_soft, port->ip_ienb, IOC4_W_IES, - IOC4_SIO_INTR_TYPE); -} - -/* - * ioc4_cb_post_ncs - called for some basic errors - * @port: port to use - * @ncs: event - */ -static void ioc4_cb_post_ncs(struct uart_port *the_port, int ncs) -{ - struct uart_icount *icount; - - icount = &the_port->icount; - - if (ncs & NCS_BREAK) - icount->brk++; - if (ncs & NCS_FRAMING) - icount->frame++; - if (ncs & NCS_OVERRUN) - icount->overrun++; - if (ncs & NCS_PARITY) - icount->parity++; -} - -/** - * do_read - Read in bytes from the port. Return the number of bytes - * actually read. - * @the_port: port to use - * @buf: place to put the stuff we read - * @len: how big 'buf' is - */ - -static inline int do_read(struct uart_port *the_port, unsigned char *buf, - int len) -{ - int prod_ptr, cons_ptr, total; - struct ioc4_port *port = get_ioc4_port(the_port, 0); - struct ring *inring; - struct ring_entry *entry; - struct hooks *hooks = port->ip_hooks; - int byte_num; - char *sc; - int loop_counter; - - BUG_ON(!(len >= 0)); - BUG_ON(!port); - - /* There is a nasty timing issue in the IOC4. When the rx_timer - * expires or the rx_high condition arises, we take an interrupt. - * At some point while servicing the interrupt, we read bytes from - * the ring buffer and re-arm the rx_timer. However the rx_timer is - * not started until the first byte is received *after* it is armed, - * and any bytes pending in the rx construction buffers are not drained - * to memory until either there are 4 bytes available or the rx_timer - * expires. This leads to a potential situation where data is left - * in the construction buffers forever - 1 to 3 bytes were received - * after the interrupt was generated but before the rx_timer was - * re-armed. At that point as long as no subsequent bytes are received - * the timer will never be started and the bytes will remain in the - * construction buffer forever. The solution is to execute a DRAIN - * command after rearming the timer. This way any bytes received before - * the DRAIN will be drained to memory, and any bytes received after - * the DRAIN will start the TIMER and be drained when it expires. - * Luckily, this only needs to be done when the DMA buffer is empty - * since there is no requirement that this function return all - * available data as long as it returns some. - */ - /* Re-arm the timer */ - writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); - - prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; - cons_ptr = port->ip_rx_cons; - - if (prod_ptr == cons_ptr) { - int reset_dma = 0; - - /* Input buffer appears empty, do a flush. */ - - /* DMA must be enabled for this to work. */ - if (!(port->ip_sscr & IOC4_SSCR_DMA_EN)) { - port->ip_sscr |= IOC4_SSCR_DMA_EN; - reset_dma = 1; - } - - /* Potential race condition: we must reload the srpir after - * issuing the drain command, otherwise we could think the rx - * buffer is empty, then take a very long interrupt, and when - * we come back it's full and we wait forever for the drain to - * complete. - */ - writel(port->ip_sscr | IOC4_SSCR_RX_DRAIN, - &port->ip_serial_regs->sscr); - prod_ptr = readl(&port->ip_serial_regs->srpir) - & PROD_CONS_MASK; - - /* We must not wait for the DRAIN to complete unless there are - * at least 8 bytes (2 ring entries) available to receive the - * data otherwise the DRAIN will never complete and we'll - * deadlock here. - * In fact, to make things easier, I'll just ignore the flush if - * there is any data at all now available. - */ - if (prod_ptr == cons_ptr) { - loop_counter = 0; - while (readl(&port->ip_serial_regs->sscr) & - IOC4_SSCR_RX_DRAIN) { - loop_counter++; - if (loop_counter > MAXITER) - return -1; - } - - /* SIGH. We have to reload the prod_ptr *again* since - * the drain may have caused it to change - */ - prod_ptr = readl(&port->ip_serial_regs->srpir) - & PROD_CONS_MASK; - } - if (reset_dma) { - port->ip_sscr &= ~IOC4_SSCR_DMA_EN; - writel(port->ip_sscr, &port->ip_serial_regs->sscr); - } - } - inring = port->ip_inring; - port->ip_flags &= ~READ_ABORTED; - - total = 0; - loop_counter = 0xfffff; /* to avoid hangs */ - - /* Grab bytes from the hardware */ - while ((prod_ptr != cons_ptr) && (len > 0)) { - entry = (struct ring_entry *)((caddr_t)inring + cons_ptr); - - if ( loop_counter-- <= 0 ) { - printk(KERN_WARNING "IOC4 serial: " - "possible hang condition/" - "port stuck on read.\n"); - break; - } - - /* According to the producer pointer, this ring entry - * must contain some data. But if the PIO happened faster - * than the DMA, the data may not be available yet, so let's - * wait until it arrives. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - /* Indicate the read is aborted so we don't disable - * the interrupt thinking that the consumer is - * congested. - */ - port->ip_flags |= READ_ABORTED; - len = 0; - break; - } - - /* Load the bytes/status out of the ring entry */ - for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { - sc = &(entry->ring_sc[byte_num]); - - /* Check for change in modem state or overrun */ - if ((*sc & IOC4_RXSB_MODEM_VALID) - && (port->ip_notify & N_DDCD)) { - /* Notify upper layer if DCD dropped */ - - if ((port->ip_flags & DCD_ON) - && !(*sc & IOC4_RXSB_DCD)) { - - /* If we have already copied some data, - * return it. We'll pick up the carrier - * drop on the next pass. That way we - * don't throw away the data that has - * already been copied back to - * the caller's buffer. - */ - if (total > 0) { - len = 0; - break; - } - port->ip_flags &= ~DCD_ON; - - /* Turn off this notification so the - * carrier drop protocol won't see it - * again when it does a read. - */ - *sc &= ~IOC4_RXSB_MODEM_VALID; - - /* To keep things consistent, we need - * to update the consumer pointer so - * the next reader won't come in and - * try to read the same ring entries - * again. This must be done here before - * the dcd change. - */ - - if ((entry->ring_allsc & RING_ANY_VALID) - == 0) { - cons_ptr += (int)sizeof - (struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - writel(cons_ptr, - &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* Notify upper layer of carrier drop */ - if ((port->ip_notify & N_DDCD) - && port->ip_port) { - the_port->icount.dcd = 0; - wake_up_interruptible - (&the_port->state-> - port.delta_msr_wait); - } - - /* If we had any data to return, we - * would have returned it above. - */ - return 0; - } - } - if (*sc & IOC4_RXSB_MODEM_VALID) { - /* Notify that an input overrun occurred */ - if ((*sc & IOC4_RXSB_OVERRUN) - && (port->ip_notify & N_OVERRUN_ERROR)) { - ioc4_cb_post_ncs(the_port, NCS_OVERRUN); - } - /* Don't look at this byte again */ - *sc &= ~IOC4_RXSB_MODEM_VALID; - } - - /* Check for valid data or RX errors */ - if ((*sc & IOC4_RXSB_DATA_VALID) && - ((*sc & (IOC4_RXSB_PAR_ERR - | IOC4_RXSB_FRAME_ERR - | IOC4_RXSB_BREAK)) - && (port->ip_notify & (N_PARITY_ERROR - | N_FRAMING_ERROR - | N_BREAK)))) { - /* There is an error condition on the next byte. - * If we have already transferred some bytes, - * we'll stop here. Otherwise if this is the - * first byte to be read, we'll just transfer - * it alone after notifying the - * upper layer of its status. - */ - if (total > 0) { - len = 0; - break; - } else { - if ((*sc & IOC4_RXSB_PAR_ERR) && - (port->ip_notify & N_PARITY_ERROR)) { - ioc4_cb_post_ncs(the_port, - NCS_PARITY); - } - if ((*sc & IOC4_RXSB_FRAME_ERR) && - (port->ip_notify & N_FRAMING_ERROR)){ - ioc4_cb_post_ncs(the_port, - NCS_FRAMING); - } - if ((*sc & IOC4_RXSB_BREAK) - && (port->ip_notify & N_BREAK)) { - ioc4_cb_post_ncs - (the_port, - NCS_BREAK); - } - len = 1; - } - } - if (*sc & IOC4_RXSB_DATA_VALID) { - *sc &= ~IOC4_RXSB_DATA_VALID; - *buf = entry->ring_data[byte_num]; - buf++; - len--; - total++; - } - } - - /* If we used up this entry entirely, go on to the next one, - * otherwise we must have run out of buffer space, so - * leave the consumer pointer here for the next read in case - * there are still unread bytes in this entry. - */ - if ((entry->ring_allsc & RING_ANY_VALID) == 0) { - cons_ptr += (int)sizeof(struct ring_entry); - cons_ptr &= PROD_CONS_MASK; - } - } - - /* Update consumer pointer and re-arm rx timer interrupt */ - writel(cons_ptr, &port->ip_serial_regs->srcir); - port->ip_rx_cons = cons_ptr; - - /* If we have now dipped below the rx high water mark and we have - * rx_high interrupt turned off, we can now turn it back on again. - */ - if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) - & PROD_CONS_MASK) < ((port->ip_sscr & - IOC4_SSCR_RX_THRESHOLD) - << IOC4_PROD_CONS_PTR_OFF))) { - port->ip_flags &= ~INPUT_HIGH; - enable_intrs(port, hooks->intr_rx_high); - } - return total; -} - -/** - * receive_chars - upper level read. Called with ip_lock. - * @the_port: port to read from - */ -static void receive_chars(struct uart_port *the_port) -{ - struct tty_struct *tty; - unsigned char ch[IOC4_MAX_CHARS]; - int read_count, request_count = IOC4_MAX_CHARS; - struct uart_icount *icount; - struct uart_state *state = the_port->state; - unsigned long pflags; - - /* Make sure all the pointers are "good" ones */ - if (!state) - return; - if (!state->port.tty) - return; - - spin_lock_irqsave(&the_port->lock, pflags); - tty = state->port.tty; - - request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS); - - if (request_count > 0) { - icount = &the_port->icount; - read_count = do_read(the_port, ch, request_count); - if (read_count > 0) { - tty_insert_flip_string(tty, ch, read_count); - icount->rx += read_count; - } - } - - spin_unlock_irqrestore(&the_port->lock, pflags); - - tty_flip_buffer_push(tty); -} - -/** - * ic4_type - What type of console are we? - * @port: Port to operate with (we ignore since we only have one port) - * - */ -static const char *ic4_type(struct uart_port *the_port) -{ - if (the_port->mapbase == PROTO_RS232) - return "SGI IOC4 Serial [rs232]"; - else - return "SGI IOC4 Serial [rs422]"; -} - -/** - * ic4_tx_empty - Is the transmitter empty? - * @port: Port to operate on - * - */ -static unsigned int ic4_tx_empty(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - unsigned int ret = 0; - - if (port_is_active(port, the_port)) { - if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT) - ret = TIOCSER_TEMT; - } - return ret; -} - -/** - * ic4_stop_tx - stop the transmitter - * @port: Port to operate on - * - */ -static void ic4_stop_tx(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - - if (port_is_active(port, the_port)) - set_notification(port, N_OUTPUT_LOWAT, 0); -} - -/** - * null_void_function - - * @port: Port to operate on - * - */ -static void null_void_function(struct uart_port *the_port) -{ -} - -/** - * ic4_shutdown - shut down the port - free irq and disable - * @port: Port to shut down - * - */ -static void ic4_shutdown(struct uart_port *the_port) -{ - unsigned long port_flags; - struct ioc4_port *port; - struct uart_state *state; - - port = get_ioc4_port(the_port, 0); - if (!port) - return; - - state = the_port->state; - port->ip_port = NULL; - - wake_up_interruptible(&state->port.delta_msr_wait); - - if (state->port.tty) - set_bit(TTY_IO_ERROR, &state->port.tty->flags); - - spin_lock_irqsave(&the_port->lock, port_flags); - set_notification(port, N_ALL, 0); - port->ip_flags = PORT_INACTIVE; - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic4_set_mctrl - set control lines (dtr, rts, etc) - * @port: Port to operate on - * @mctrl: Lines to set/unset - * - */ -static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl) -{ - unsigned char mcr = 0; - struct ioc4_port *port; - - port = get_ioc4_port(the_port, 0); - if (!port_is_active(port, the_port)) - return; - - 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; - - set_mcr(the_port, mcr, IOC4_SHADOW_DTR); -} - -/** - * ic4_get_mctrl - get control line info - * @port: port to operate on - * - */ -static unsigned int ic4_get_mctrl(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - uint32_t shadow; - unsigned int ret = 0; - - if (!port_is_active(port, the_port)) - return 0; - - shadow = readl(&port->ip_serial_regs->shadow); - if (shadow & IOC4_SHADOW_DCD) - ret |= TIOCM_CAR; - if (shadow & IOC4_SHADOW_DR) - ret |= TIOCM_DSR; - if (shadow & IOC4_SHADOW_CTS) - ret |= TIOCM_CTS; - return ret; -} - -/** - * ic4_start_tx - Start transmitter, flush any output - * @port: Port to operate on - * - */ -static void ic4_start_tx(struct uart_port *the_port) -{ - struct ioc4_port *port = get_ioc4_port(the_port, 0); - - if (port_is_active(port, the_port)) { - set_notification(port, N_OUTPUT_LOWAT, 1); - enable_intrs(port, port->ip_hooks->intr_tx_mt); - } -} - -/** - * ic4_break_ctl - handle breaks - * @port: Port to operate on - * @break_state: Break state - * - */ -static void ic4_break_ctl(struct uart_port *the_port, int break_state) -{ -} - -/** - * ic4_startup - Start up the serial port - * @port: Port to operate on - * - */ -static int ic4_startup(struct uart_port *the_port) -{ - int retval; - struct ioc4_port *port; - struct ioc4_control *control; - struct uart_state *state; - unsigned long port_flags; - - if (!the_port) - return -ENODEV; - port = get_ioc4_port(the_port, 1); - if (!port) - return -ENODEV; - state = the_port->state; - - control = port->ip_control; - if (!control) { - port->ip_port = NULL; - return -ENODEV; - } - - /* Start up the serial port */ - spin_lock_irqsave(&the_port->lock, port_flags); - retval = ic4_startup_local(the_port); - spin_unlock_irqrestore(&the_port->lock, port_flags); - return retval; -} - -/** - * ic4_set_termios - set termios stuff - * @port: port to operate on - * @termios: New settings - * @termios: Old - * - */ -static void -ic4_set_termios(struct uart_port *the_port, - struct ktermios *termios, struct ktermios *old_termios) -{ - unsigned long port_flags; - - spin_lock_irqsave(&the_port->lock, port_flags); - ioc4_change_speed(the_port, termios, old_termios); - spin_unlock_irqrestore(&the_port->lock, port_flags); -} - -/** - * ic4_request_port - allocate resources for port - no op.... - * @port: port to operate on - * - */ -static int ic4_request_port(struct uart_port *port) -{ - return 0; -} - -/* Associate the uart functions above - given to serial core */ - -static struct uart_ops ioc4_ops = { - .tx_empty = ic4_tx_empty, - .set_mctrl = ic4_set_mctrl, - .get_mctrl = ic4_get_mctrl, - .stop_tx = ic4_stop_tx, - .start_tx = ic4_start_tx, - .stop_rx = null_void_function, - .enable_ms = null_void_function, - .break_ctl = ic4_break_ctl, - .startup = ic4_startup, - .shutdown = ic4_shutdown, - .set_termios = ic4_set_termios, - .type = ic4_type, - .release_port = null_void_function, - .request_port = ic4_request_port, -}; - -/* - * Boot-time initialization code - */ - -static struct uart_driver ioc4_uart_rs232 = { - .owner = THIS_MODULE, - .driver_name = "ioc4_serial_rs232", - .dev_name = DEVICE_NAME_RS232, - .major = DEVICE_MAJOR, - .minor = DEVICE_MINOR_RS232, - .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS, -}; - -static struct uart_driver ioc4_uart_rs422 = { - .owner = THIS_MODULE, - .driver_name = "ioc4_serial_rs422", - .dev_name = DEVICE_NAME_RS422, - .major = DEVICE_MAJOR, - .minor = DEVICE_MINOR_RS422, - .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS, -}; - - -/** - * ioc4_serial_remove_one - detach function - * - * @idd: IOC4 master module data for this IOC4 - */ - -static int ioc4_serial_remove_one(struct ioc4_driver_data *idd) -{ - int port_num, port_type; - struct ioc4_control *control; - struct uart_port *the_port; - struct ioc4_port *port; - struct ioc4_soft *soft; - - /* If serial driver did not attach, don't try to detach */ - control = idd->idd_serial_data; - if (!control) - return 0; - - for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) { - for (port_type = UART_PORT_MIN; - port_type < UART_PORT_COUNT; - port_type++) { - the_port = &control->ic_port[port_num].icp_uart_port - [port_type]; - if (the_port) { - switch (port_type) { - case UART_PORT_RS422: - uart_remove_one_port(&ioc4_uart_rs422, - the_port); - break; - default: - case UART_PORT_RS232: - uart_remove_one_port(&ioc4_uart_rs232, - the_port); - break; - } - } - } - port = control->ic_port[port_num].icp_port; - /* we allocate in pairs */ - if (!(port_num & 1) && port) { - pci_free_consistent(port->ip_pdev, - TOTAL_RING_BUF_SIZE, - port->ip_cpu_ringbuf, - port->ip_dma_ringbuf); - kfree(port); - } - } - soft = control->ic_soft; - if (soft) { - free_irq(control->ic_irq, soft); - if (soft->is_ioc4_serial_addr) { - iounmap(soft->is_ioc4_serial_addr); - release_mem_region((unsigned long) - soft->is_ioc4_serial_addr, - sizeof(struct ioc4_serial)); - } - kfree(soft); - } - kfree(control); - idd->idd_serial_data = NULL; - - return 0; -} - - -/** - * ioc4_serial_core_attach_rs232 - register with serial core - * This is done during pci probing - * @pdev: handle for this card - */ -static inline int -ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) -{ - struct ioc4_port *port; - struct uart_port *the_port; - struct ioc4_driver_data *idd = pci_get_drvdata(pdev); - struct ioc4_control *control = idd->idd_serial_data; - int port_num; - int port_type_idx; - struct uart_driver *u_driver; - - - DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n", - __func__, pdev, (void *)control)); - - if (!control) - return -ENODEV; - - port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232 - : UART_PORT_RS422; - - u_driver = (port_type == PROTO_RS232) ? &ioc4_uart_rs232 - : &ioc4_uart_rs422; - - /* once around for each port on this card */ - for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) { - the_port = &control->ic_port[port_num].icp_uart_port - [port_type_idx]; - port = control->ic_port[port_num].icp_port; - port->ip_all_ports[port_type_idx] = the_port; - - DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n", - __func__, (void *)the_port, - (void *)port, - port_type == PROTO_RS232 ? "rs232" : "rs422")); - - /* membase, iobase and mapbase just need to be non-0 */ - the_port->membase = (unsigned char __iomem *)1; - the_port->iobase = (pdev->bus->number << 16) | port_num; - the_port->line = (Num_of_ioc4_cards << 2) | port_num; - the_port->mapbase = port_type; - the_port->type = PORT_16550A; - the_port->fifosize = IOC4_FIFO_CHARS; - the_port->ops = &ioc4_ops; - the_port->irq = control->ic_irq; - the_port->dev = &pdev->dev; - spin_lock_init(&the_port->lock); - if (uart_add_one_port(u_driver, the_port) < 0) { - printk(KERN_WARNING - "%s: unable to add port %d bus %d\n", - __func__, the_port->line, pdev->bus->number); - } else { - DPRINT_CONFIG( - ("IOC4 serial port %d irq = %d, bus %d\n", - the_port->line, the_port->irq, pdev->bus->number)); - } - } - return 0; -} - -/** - * ioc4_serial_attach_one - register attach function - * called per card found from IOC4 master module. - * @idd: Master module data for this IOC4 - */ -int -ioc4_serial_attach_one(struct ioc4_driver_data *idd) -{ - unsigned long tmp_addr1; - struct ioc4_serial __iomem *serial; - struct ioc4_soft *soft; - struct ioc4_control *control; - int ret = 0; - - - DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev, - idd->idd_pci_id)); - - /* PCI-RT does not bring out serial connections. - * Do not attach to this particular IOC4. - */ - if (idd->idd_variant == IOC4_VARIANT_PCI_RT) - return 0; - - /* request serial registers */ - tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET; - - if (!request_mem_region(tmp_addr1, sizeof(struct ioc4_serial), - "sioc4_uart")) { - printk(KERN_WARNING - "ioc4 (%p): unable to get request region for " - "uart space\n", (void *)idd->idd_pdev); - ret = -ENODEV; - goto out1; - } - serial = ioremap(tmp_addr1, sizeof(struct ioc4_serial)); - if (!serial) { - printk(KERN_WARNING - "ioc4 (%p) : unable to remap ioc4 serial register\n", - (void *)idd->idd_pdev); - ret = -ENODEV; - goto out2; - } - DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n", - __func__, (void *)idd->idd_misc_regs, - (void *)serial)); - - /* Get memory for the new card */ - control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL); - - if (!control) { - printk(KERN_WARNING "ioc4_attach_one" - ": unable to get memory for the IOC4\n"); - ret = -ENOMEM; - goto out2; - } - idd->idd_serial_data = control; - - /* Allocate the soft structure */ - soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL); - if (!soft) { - printk(KERN_WARNING - "ioc4 (%p): unable to get memory for the soft struct\n", - (void *)idd->idd_pdev); - ret = -ENOMEM; - goto out3; - } - - spin_lock_init(&soft->is_ir_lock); - soft->is_ioc4_misc_addr = idd->idd_misc_regs; - soft->is_ioc4_serial_addr = serial; - - /* Init the IOC4 */ - writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT, - &idd->idd_misc_regs->sio_cr.raw); - - /* Enable serial port mode select generic PIO pins as outputs */ - writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL - | IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL, - &idd->idd_misc_regs->gpcr_s.raw); - - /* Clear and disable all serial interrupts */ - write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE); - writel(~0, &idd->idd_misc_regs->sio_ir.raw); - write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC, - IOC4_OTHER_INTR_TYPE); - writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw); - control->ic_soft = soft; - - /* Hook up interrupt handler */ - if (!request_irq(idd->idd_pdev->irq, ioc4_intr, IRQF_SHARED, - "sgi-ioc4serial", soft)) { - control->ic_irq = idd->idd_pdev->irq; - } else { - printk(KERN_WARNING - "%s : request_irq fails for IRQ 0x%x\n ", - __func__, idd->idd_pdev->irq); - } - ret = ioc4_attach_local(idd); - if (ret) - goto out4; - - /* register port with the serial core - 1 rs232, 1 rs422 */ - - if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232))) - goto out4; - - if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422))) - goto out5; - - Num_of_ioc4_cards++; - - return ret; - - /* error exits that give back resources */ -out5: - ioc4_serial_remove_one(idd); -out4: - kfree(soft); -out3: - kfree(control); -out2: - if (serial) - iounmap(serial); - release_mem_region(tmp_addr1, sizeof(struct ioc4_serial)); -out1: - - return ret; -} - - -static struct ioc4_submodule ioc4_serial_submodule = { - .is_name = "IOC4_serial", - .is_owner = THIS_MODULE, - .is_probe = ioc4_serial_attach_one, - .is_remove = ioc4_serial_remove_one, -}; - -/** - * ioc4_serial_init - module init - */ -static int __init ioc4_serial_init(void) -{ - int ret; - - /* register with serial core */ - if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) { - printk(KERN_WARNING - "%s: Couldn't register rs232 IOC4 serial driver\n", - __func__); - goto out; - } - if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) { - printk(KERN_WARNING - "%s: Couldn't register rs422 IOC4 serial driver\n", - __func__); - goto out_uart_rs232; - } - - /* register with IOC4 main module */ - ret = ioc4_register_submodule(&ioc4_serial_submodule); - if (ret) - goto out_uart_rs422; - return 0; - -out_uart_rs422: - uart_unregister_driver(&ioc4_uart_rs422); -out_uart_rs232: - uart_unregister_driver(&ioc4_uart_rs232); -out: - return ret; -} - -static void __exit ioc4_serial_exit(void) -{ - ioc4_unregister_submodule(&ioc4_serial_submodule); - uart_unregister_driver(&ioc4_uart_rs232); - uart_unregister_driver(&ioc4_uart_rs422); -} - -late_initcall(ioc4_serial_init); /* Call only after tty init is done */ -module_exit(ioc4_serial_exit); - -MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) "); -MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c deleted file mode 100644 index ebff4a1d4bcc..000000000000 --- a/drivers/serial/ip22zilog.c +++ /dev/null @@ -1,1221 +0,0 @@ -/* - * Driver for Zilog serial chips found on SGI workstations and - * servers. This driver could actually be made more generic. - * - * This is based on the drivers/serial/sunzilog.c code as of 2.6.0-test7 and the - * old drivers/sgi/char/sgiserial.c code which itself is based of the original - * drivers/sbus/char/zs.c code. A lot of code has been simply moved over - * directly from there but much has been rewritten. Credits therefore go out - * to David S. Miller, Eddie C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell - * for their work there. - * - * Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org) - * Copyright (C) 2002 David S. Miller (davem@redhat.com) - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_SERIAL_IP22_ZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#include "ip22zilog.h" - -/* - * On IP22 we need to delay after register accesses but we do not need to - * flush writes. - */ -#define ZSDELAY() udelay(5) -#define ZSDELAY_LONG() udelay(20) -#define ZS_WSYNC(channel) do { } while (0) - -#define NUM_IP22ZILOG 1 -#define NUM_CHANNELS (NUM_IP22ZILOG * 2) - -#define ZS_CLOCK 3672000 /* Zilog input clock rate. */ -#define ZS_CLOCK_DIVISOR 16 /* Divisor this driver uses. */ - -/* - * We wrap our port structure around the generic uart_port. - */ -struct uart_ip22zilog_port { - struct uart_port port; - - /* IRQ servicing chain. */ - struct uart_ip22zilog_port *next; - - /* Current values of Zilog write registers. */ - unsigned char curregs[NUM_ZSREGS]; - - unsigned int flags; -#define IP22ZILOG_FLAG_IS_CONS 0x00000004 -#define IP22ZILOG_FLAG_IS_KGDB 0x00000008 -#define IP22ZILOG_FLAG_MODEM_STATUS 0x00000010 -#define IP22ZILOG_FLAG_IS_CHANNEL_A 0x00000020 -#define IP22ZILOG_FLAG_REGS_HELD 0x00000040 -#define IP22ZILOG_FLAG_TX_STOPPED 0x00000080 -#define IP22ZILOG_FLAG_TX_ACTIVE 0x00000100 -#define IP22ZILOG_FLAG_RESET_DONE 0x00000200 - - unsigned int tty_break; - - unsigned char parity_mask; - unsigned char prev_status; -}; - -#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase)) -#define UART_ZILOG(PORT) ((struct uart_ip22zilog_port *)(PORT)) -#define IP22ZILOG_GET_CURR_REG(PORT, REGNUM) \ - (UART_ZILOG(PORT)->curregs[REGNUM]) -#define IP22ZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL) \ - ((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL)) -#define ZS_IS_CONS(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_CONS) -#define ZS_IS_KGDB(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_KGDB) -#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & IP22ZILOG_FLAG_MODEM_STATUS) -#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_CHANNEL_A) -#define ZS_REGS_HELD(UP) ((UP)->flags & IP22ZILOG_FLAG_REGS_HELD) -#define ZS_TX_STOPPED(UP) ((UP)->flags & IP22ZILOG_FLAG_TX_STOPPED) -#define ZS_TX_ACTIVE(UP) ((UP)->flags & IP22ZILOG_FLAG_TX_ACTIVE) - -/* Reading and writing Zilog8530 registers. The delays are to make this - * driver work on the IP22 which needs a settling delay after each chip - * register access, other machines handle this in hardware via auxiliary - * flip-flops which implement the settle time we do in software. - * - * The port lock must be held and local IRQs must be disabled - * when {read,write}_zsreg is invoked. - */ -static unsigned char read_zsreg(struct zilog_channel *channel, - unsigned char reg) -{ - unsigned char retval; - - writeb(reg, &channel->control); - ZSDELAY(); - retval = readb(&channel->control); - ZSDELAY(); - - return retval; -} - -static void write_zsreg(struct zilog_channel *channel, - unsigned char reg, unsigned char value) -{ - writeb(reg, &channel->control); - ZSDELAY(); - writeb(value, &channel->control); - ZSDELAY(); -} - -static void ip22zilog_clear_fifo(struct zilog_channel *channel) -{ - int i; - - for (i = 0; i < 32; i++) { - unsigned char regval; - - regval = readb(&channel->control); - ZSDELAY(); - if (regval & Rx_CH_AV) - break; - - regval = read_zsreg(channel, R1); - readb(&channel->data); - ZSDELAY(); - - if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) { - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - } - } -} - -/* This function must only be called when the TX is not busy. The UART - * port lock must be held and local interrupts disabled. - */ -static void __load_zsregs(struct zilog_channel *channel, unsigned char *regs) -{ - int i; - - /* Let pending transmits finish. */ - for (i = 0; i < 1000; i++) { - unsigned char stat = read_zsreg(channel, R1); - if (stat & ALL_SNT) - break; - udelay(100); - } - - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - ip22zilog_clear_fifo(channel); - - /* Disable all interrupts. */ - write_zsreg(channel, R1, - regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); - - /* Set parity, sync config, stop bits, and clock divisor. */ - write_zsreg(channel, R4, regs[R4]); - - /* Set misc. TX/RX control bits. */ - write_zsreg(channel, R10, regs[R10]); - - /* Set TX/RX controls sans the enable bits. */ - write_zsreg(channel, R3, regs[R3] & ~RxENAB); - write_zsreg(channel, R5, regs[R5] & ~TxENAB); - - /* Synchronous mode config. */ - write_zsreg(channel, R6, regs[R6]); - write_zsreg(channel, R7, regs[R7]); - - /* Don't mess with the interrupt vector (R2, unused by us) and - * master interrupt control (R9). We make sure this is setup - * properly at probe time then never touch it again. - */ - - /* Disable baud generator. */ - write_zsreg(channel, R14, regs[R14] & ~BRENAB); - - /* Clock mode control. */ - write_zsreg(channel, R11, regs[R11]); - - /* Lower and upper byte of baud rate generator divisor. */ - write_zsreg(channel, R12, regs[R12]); - write_zsreg(channel, R13, regs[R13]); - - /* Now rewrite R14, with BRENAB (if set). */ - write_zsreg(channel, R14, regs[R14]); - - /* External status interrupt control. */ - write_zsreg(channel, R15, regs[R15]); - - /* Reset external status interrupts. */ - write_zsreg(channel, R0, RES_EXT_INT); - write_zsreg(channel, R0, RES_EXT_INT); - - /* Rewrite R3/R5, this time without enables masked. */ - write_zsreg(channel, R3, regs[R3]); - write_zsreg(channel, R5, regs[R5]); - - /* Rewrite R1, this time without IRQ enabled masked. */ - write_zsreg(channel, R1, regs[R1]); -} - -/* Reprogram the Zilog channel HW registers with the copies found in the - * software state struct. If the transmitter is busy, we defer this update - * until the next TX complete interrupt. Else, we do it right now. - * - * The UART port lock must be held and local interrupts disabled. - */ -static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up, - struct zilog_channel *channel) -{ - if (!ZS_REGS_HELD(up)) { - if (ZS_TX_ACTIVE(up)) { - up->flags |= IP22ZILOG_FLAG_REGS_HELD; - } else { - __load_zsregs(channel, up->curregs); - } - } -} - -#define Rx_BRK 0x0100 /* BREAK event software flag. */ -#define Rx_SYS 0x0200 /* SysRq event software flag. */ - -static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up, - struct zilog_channel *channel) -{ - struct tty_struct *tty; - unsigned char ch, flag; - unsigned int r1; - - tty = NULL; - if (up->port.state != NULL && - up->port.state->port.tty != NULL) - tty = up->port.state->port.tty; - - for (;;) { - ch = readb(&channel->control); - ZSDELAY(); - if (!(ch & Rx_CH_AV)) - break; - - r1 = read_zsreg(channel, R1); - if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - } - - ch = readb(&channel->data); - ZSDELAY(); - - ch &= up->parity_mask; - - /* Handle the null char got when BREAK is removed. */ - if (!ch) - r1 |= up->tty_break; - - /* A real serial line, record the character and status. */ - flag = TTY_NORMAL; - up->port.icount.rx++; - if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) { - up->tty_break = 0; - - if (r1 & (Rx_SYS | Rx_BRK)) { - up->port.icount.brk++; - if (r1 & Rx_SYS) - continue; - r1 &= ~(PAR_ERR | CRC_ERR); - } - else if (r1 & PAR_ERR) - up->port.icount.parity++; - else if (r1 & CRC_ERR) - up->port.icount.frame++; - if (r1 & Rx_OVR) - up->port.icount.overrun++; - r1 &= up->port.read_status_mask; - if (r1 & Rx_BRK) - flag = TTY_BREAK; - else if (r1 & PAR_ERR) - flag = TTY_PARITY; - else if (r1 & CRC_ERR) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - continue; - - if (tty) - uart_insert_char(&up->port, r1, Rx_OVR, ch, flag); - } - return tty; -} - -static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, - struct zilog_channel *channel) -{ - unsigned char status; - - status = readb(&channel->control); - ZSDELAY(); - - writeb(RES_EXT_INT, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - if (up->curregs[R15] & BRKIE) { - if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) { - if (uart_handle_break(&up->port)) - up->tty_break = Rx_SYS; - else - up->tty_break = Rx_BRK; - } - } - - if (ZS_WANTS_MODEM_STATUS(up)) { - if (status & SYNC) - up->port.icount.dsr++; - - /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. - * But it does not tell us which bit has changed, we have to keep - * track of this ourselves. - */ - if ((status ^ up->prev_status) ^ DCD) - uart_handle_dcd_change(&up->port, - (status & DCD)); - if ((status ^ up->prev_status) ^ CTS) - uart_handle_cts_change(&up->port, - (status & CTS)); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); - } - - up->prev_status = status; -} - -static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, - struct zilog_channel *channel) -{ - struct circ_buf *xmit; - - if (ZS_IS_CONS(up)) { - unsigned char status = readb(&channel->control); - ZSDELAY(); - - /* TX still busy? Just wait for the next TX done interrupt. - * - * It can occur because of how we do serial console writes. It would - * be nice to transmit console writes just like we normally would for - * a TTY line. (ie. buffered and TX interrupt driven). That is not - * easy because console writes cannot sleep. One solution might be - * to poll on enough port->xmit space becomming free. -DaveM - */ - if (!(status & Tx_BUF_EMP)) - return; - } - - up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE; - - if (ZS_REGS_HELD(up)) { - __load_zsregs(channel, up->curregs); - up->flags &= ~IP22ZILOG_FLAG_REGS_HELD; - } - - if (ZS_TX_STOPPED(up)) { - up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED; - goto ack_tx_int; - } - - if (up->port.x_char) { - up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; - writeb(up->port.x_char, &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - - if (up->port.state == NULL) - goto ack_tx_int; - xmit = &up->port.state->xmit; - if (uart_circ_empty(xmit)) - goto ack_tx_int; - if (uart_tx_stopped(&up->port)) - goto ack_tx_int; - - up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; - writeb(xmit->buf[xmit->tail], &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - return; - -ack_tx_int: - writeb(RES_Tx_P, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); -} - -static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) -{ - struct uart_ip22zilog_port *up = dev_id; - - while (up) { - struct zilog_channel *channel - = ZILOG_CHANNEL_FROM_PORT(&up->port); - struct tty_struct *tty; - unsigned char r3; - - spin_lock(&up->port.lock); - r3 = read_zsreg(channel, R3); - - /* Channel A */ - tty = NULL; - if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { - writeb(RES_H_IUS, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - if (r3 & CHARxIP) - tty = ip22zilog_receive_chars(up, channel); - if (r3 & CHAEXT) - ip22zilog_status_handle(up, channel); - if (r3 & CHATxIP) - ip22zilog_transmit_chars(up, channel); - } - spin_unlock(&up->port.lock); - - if (tty) - tty_flip_buffer_push(tty); - - /* Channel B */ - up = up->next; - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - - spin_lock(&up->port.lock); - tty = NULL; - if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { - writeb(RES_H_IUS, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - if (r3 & CHBRxIP) - tty = ip22zilog_receive_chars(up, channel); - if (r3 & CHBEXT) - ip22zilog_status_handle(up, channel); - if (r3 & CHBTxIP) - ip22zilog_transmit_chars(up, channel); - } - spin_unlock(&up->port.lock); - - if (tty) - tty_flip_buffer_push(tty); - - up = up->next; - } - - return IRQ_HANDLED; -} - -/* A convenient way to quickly get R0 status. The caller must _not_ hold the - * port lock, it is acquired here. - */ -static __inline__ unsigned char ip22zilog_read_channel_status(struct uart_port *port) -{ - struct zilog_channel *channel; - unsigned char status; - - channel = ZILOG_CHANNEL_FROM_PORT(port); - status = readb(&channel->control); - ZSDELAY(); - - return status; -} - -/* The port lock is not held. */ -static unsigned int ip22zilog_tx_empty(struct uart_port *port) -{ - unsigned long flags; - unsigned char status; - unsigned int ret; - - spin_lock_irqsave(&port->lock, flags); - - status = ip22zilog_read_channel_status(port); - - spin_unlock_irqrestore(&port->lock, flags); - - if (status & Tx_BUF_EMP) - ret = TIOCSER_TEMT; - else - ret = 0; - - return ret; -} - -/* The port lock is held and interrupts are disabled. */ -static unsigned int ip22zilog_get_mctrl(struct uart_port *port) -{ - unsigned char status; - unsigned int ret; - - status = ip22zilog_read_channel_status(port); - - ret = 0; - if (status & DCD) - ret |= TIOCM_CAR; - if (status & SYNC) - ret |= TIOCM_DSR; - if (status & CTS) - ret |= TIOCM_CTS; - - return ret; -} - -/* The port lock is held and interrupts are disabled. */ -static void ip22zilog_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; - struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char set_bits, clear_bits; - - set_bits = clear_bits = 0; - - if (mctrl & TIOCM_RTS) - set_bits |= RTS; - else - clear_bits |= RTS; - if (mctrl & TIOCM_DTR) - set_bits |= DTR; - else - clear_bits |= DTR; - - /* NOTE: Not subject to 'transmitter active' rule. */ - up->curregs[R5] |= set_bits; - up->curregs[R5] &= ~clear_bits; - write_zsreg(channel, R5, up->curregs[R5]); -} - -/* The port lock is held and interrupts are disabled. */ -static void ip22zilog_stop_tx(struct uart_port *port) -{ - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; - - up->flags |= IP22ZILOG_FLAG_TX_STOPPED; -} - -/* The port lock is held and interrupts are disabled. */ -static void ip22zilog_start_tx(struct uart_port *port) -{ - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; - struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char status; - - up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; - up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED; - - status = readb(&channel->control); - ZSDELAY(); - - /* TX busy? Just wait for the TX done interrupt. */ - if (!(status & Tx_BUF_EMP)) - return; - - /* Send the first character to jump-start the TX done - * IRQ sending engine. - */ - if (port->x_char) { - writeb(port->x_char, &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - port->icount.tx++; - port->x_char = 0; - } else { - struct circ_buf *xmit = &port->state->xmit; - - writeb(xmit->buf[xmit->tail], &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - } -} - -/* The port lock is held and interrupts are disabled. */ -static void ip22zilog_stop_rx(struct uart_port *port) -{ - struct uart_ip22zilog_port *up = UART_ZILOG(port); - struct zilog_channel *channel; - - if (ZS_IS_CONS(up)) - return; - - channel = ZILOG_CHANNEL_FROM_PORT(port); - - /* Disable all RX interrupts. */ - up->curregs[R1] &= ~RxINT_MASK; - ip22zilog_maybe_update_regs(up, channel); -} - -/* The port lock is held. */ -static void ip22zilog_enable_ms(struct uart_port *port) -{ - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; - struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char new_reg; - - new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE); - if (new_reg != up->curregs[R15]) { - up->curregs[R15] = new_reg; - - /* NOTE: Not subject to 'transmitter active' rule. */ - write_zsreg(channel, R15, up->curregs[R15]); - } -} - -/* The port lock is not held. */ -static void ip22zilog_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; - struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char set_bits, clear_bits, new_reg; - unsigned long flags; - - set_bits = clear_bits = 0; - - if (break_state) - set_bits |= SND_BRK; - else - clear_bits |= SND_BRK; - - spin_lock_irqsave(&port->lock, flags); - - new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; - if (new_reg != up->curregs[R5]) { - up->curregs[R5] = new_reg; - - /* NOTE: Not subject to 'transmitter active' rule. */ - write_zsreg(channel, R5, up->curregs[R5]); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void __ip22zilog_reset(struct uart_ip22zilog_port *up) -{ - struct zilog_channel *channel; - int i; - - if (up->flags & IP22ZILOG_FLAG_RESET_DONE) - return; - - /* Let pending transmits finish. */ - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - for (i = 0; i < 1000; i++) { - unsigned char stat = read_zsreg(channel, R1); - if (stat & ALL_SNT) - break; - udelay(100); - } - - if (!ZS_IS_CHANNEL_A(up)) { - up++; - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - } - write_zsreg(channel, R9, FHWRES); - ZSDELAY_LONG(); - (void) read_zsreg(channel, R0); - - up->flags |= IP22ZILOG_FLAG_RESET_DONE; - up->next->flags |= IP22ZILOG_FLAG_RESET_DONE; -} - -static void __ip22zilog_startup(struct uart_ip22zilog_port *up) -{ - struct zilog_channel *channel; - - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - - __ip22zilog_reset(up); - - __load_zsregs(channel, up->curregs); - /* set master interrupt enable */ - write_zsreg(channel, R9, up->curregs[R9]); - up->prev_status = readb(&channel->control); - - /* Enable receiver and transmitter. */ - up->curregs[R3] |= RxENAB; - up->curregs[R5] |= TxENAB; - - up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; - ip22zilog_maybe_update_regs(up, channel); -} - -static int ip22zilog_startup(struct uart_port *port) -{ - struct uart_ip22zilog_port *up = UART_ZILOG(port); - unsigned long flags; - - if (ZS_IS_CONS(up)) - return 0; - - spin_lock_irqsave(&port->lock, flags); - __ip22zilog_startup(up); - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - -/* - * The test for ZS_IS_CONS is explained by the following e-mail: - ***** - * From: Russell King - * Date: Sun, 8 Dec 2002 10:18:38 +0000 - * - * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: - * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, - * > and I noticed that something is not right with reference - * > counting in this case. It seems that when the console - * > is open by kernel initially, this is not accounted - * > as an open, and uart_startup is not called. - * - * That is correct. We are unable to call uart_startup when the serial - * console is initialised because it may need to allocate memory (as - * request_irq does) and the memory allocators may not have been - * initialised. - * - * 1. initialise the port into a state where it can send characters in the - * console write method. - * - * 2. don't do the actual hardware shutdown in your shutdown() method (but - * do the normal software shutdown - ie, free irqs etc) - ***** - */ -static void ip22zilog_shutdown(struct uart_port *port) -{ - struct uart_ip22zilog_port *up = UART_ZILOG(port); - struct zilog_channel *channel; - unsigned long flags; - - if (ZS_IS_CONS(up)) - return; - - spin_lock_irqsave(&port->lock, flags); - - channel = ZILOG_CHANNEL_FROM_PORT(port); - - /* Disable receiver and transmitter. */ - up->curregs[R3] &= ~RxENAB; - up->curregs[R5] &= ~TxENAB; - - /* Disable all interrupts and BRK assertion. */ - up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); - up->curregs[R5] &= ~SND_BRK; - ip22zilog_maybe_update_regs(up, channel); - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* Shared by TTY driver and serial console setup. The port lock is held - * and local interrupts are disabled. - */ -static void -ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag, - unsigned int iflag, int brg) -{ - - up->curregs[R10] = NRZ; - up->curregs[R11] = TCBR | RCBR; - - /* Program BAUD and clock source. */ - up->curregs[R4] &= ~XCLK_MASK; - up->curregs[R4] |= X16CLK; - up->curregs[R12] = brg & 0xff; - up->curregs[R13] = (brg >> 8) & 0xff; - up->curregs[R14] = BRENAB; - - /* Character size, stop bits, and parity. */ - up->curregs[3] &= ~RxN_MASK; - up->curregs[5] &= ~TxN_MASK; - switch (cflag & CSIZE) { - case CS5: - up->curregs[3] |= Rx5; - up->curregs[5] |= Tx5; - up->parity_mask = 0x1f; - break; - case CS6: - up->curregs[3] |= Rx6; - up->curregs[5] |= Tx6; - up->parity_mask = 0x3f; - break; - case CS7: - up->curregs[3] |= Rx7; - up->curregs[5] |= Tx7; - up->parity_mask = 0x7f; - break; - case CS8: - default: - up->curregs[3] |= Rx8; - up->curregs[5] |= Tx8; - up->parity_mask = 0xff; - break; - }; - up->curregs[4] &= ~0x0c; - if (cflag & CSTOPB) - up->curregs[4] |= SB2; - else - up->curregs[4] |= SB1; - if (cflag & PARENB) - up->curregs[4] |= PAR_ENAB; - else - up->curregs[4] &= ~PAR_ENAB; - if (!(cflag & PARODD)) - up->curregs[4] |= PAR_EVEN; - else - up->curregs[4] &= ~PAR_EVEN; - - up->port.read_status_mask = Rx_OVR; - if (iflag & INPCK) - up->port.read_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= BRK_ABRT; - - up->port.ignore_status_mask = 0; - if (iflag & IGNPAR) - up->port.ignore_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & IGNBRK) { - up->port.ignore_status_mask |= BRK_ABRT; - if (iflag & IGNPAR) - up->port.ignore_status_mask |= Rx_OVR; - } - - if ((cflag & CREAD) == 0) - up->port.ignore_status_mask = 0xff; -} - -/* The port lock is not held. */ -static void -ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; - unsigned long flags; - int baud, brg; - - baud = uart_get_baud_rate(port, termios, old, 1200, 76800); - - spin_lock_irqsave(&up->port.lock, flags); - - brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - - ip22zilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg); - - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->flags |= IP22ZILOG_FLAG_MODEM_STATUS; - else - up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS; - - ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); - uart_update_timeout(port, termios->c_cflag, baud); - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static const char *ip22zilog_type(struct uart_port *port) -{ - return "IP22-Zilog"; -} - -/* We do not request/release mappings of the registers here, this - * happens at early serial probe time. - */ -static void ip22zilog_release_port(struct uart_port *port) -{ -} - -static int ip22zilog_request_port(struct uart_port *port) -{ - return 0; -} - -/* These do not need to do anything interesting either. */ -static void ip22zilog_config_port(struct uart_port *port, int flags) -{ -} - -/* We do not support letting the user mess with the divisor, IRQ, etc. */ -static int ip22zilog_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static struct uart_ops ip22zilog_pops = { - .tx_empty = ip22zilog_tx_empty, - .set_mctrl = ip22zilog_set_mctrl, - .get_mctrl = ip22zilog_get_mctrl, - .stop_tx = ip22zilog_stop_tx, - .start_tx = ip22zilog_start_tx, - .stop_rx = ip22zilog_stop_rx, - .enable_ms = ip22zilog_enable_ms, - .break_ctl = ip22zilog_break_ctl, - .startup = ip22zilog_startup, - .shutdown = ip22zilog_shutdown, - .set_termios = ip22zilog_set_termios, - .type = ip22zilog_type, - .release_port = ip22zilog_release_port, - .request_port = ip22zilog_request_port, - .config_port = ip22zilog_config_port, - .verify_port = ip22zilog_verify_port, -}; - -static struct uart_ip22zilog_port *ip22zilog_port_table; -static struct zilog_layout **ip22zilog_chip_regs; - -static struct uart_ip22zilog_port *ip22zilog_irq_chain; -static int zilog_irq = -1; - -static void * __init alloc_one_table(unsigned long size) -{ - return kzalloc(size, GFP_KERNEL); -} - -static void __init ip22zilog_alloc_tables(void) -{ - ip22zilog_port_table = (struct uart_ip22zilog_port *) - alloc_one_table(NUM_CHANNELS * sizeof(struct uart_ip22zilog_port)); - ip22zilog_chip_regs = (struct zilog_layout **) - alloc_one_table(NUM_IP22ZILOG * sizeof(struct zilog_layout *)); - - if (ip22zilog_port_table == NULL || ip22zilog_chip_regs == NULL) { - panic("IP22-Zilog: Cannot allocate IP22-Zilog tables."); - } -} - -/* Get the address of the registers for IP22-Zilog instance CHIP. */ -static struct zilog_layout * __init get_zs(int chip) -{ - unsigned long base; - - if (chip < 0 || chip >= NUM_IP22ZILOG) { - panic("IP22-Zilog: Illegal chip number %d in get_zs.", chip); - } - - /* Not probe-able, hard code it. */ - base = (unsigned long) &sgioc->uart; - - zilog_irq = SGI_SERIAL_IRQ; - request_mem_region(base, 8, "IP22-Zilog"); - - return (struct zilog_layout *) base; -} - -#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */ - -#ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE -static void ip22zilog_put_char(struct uart_port *port, int ch) -{ - struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); - int loops = ZS_PUT_CHAR_MAX_DELAY; - - /* This is a timed polling loop so do not switch the explicit - * udelay with ZSDELAY as that is a NOP on some platforms. -DaveM - */ - do { - unsigned char val = readb(&channel->control); - if (val & Tx_BUF_EMP) { - ZSDELAY(); - break; - } - udelay(5); - } while (--loops); - - writeb(ch, &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); -} - -static void -ip22zilog_console_write(struct console *con, const char *s, unsigned int count) -{ - struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - uart_console_write(&up->port, s, count, ip22zilog_put_char); - udelay(2); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int __init ip22zilog_console_setup(struct console *con, char *options) -{ - struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; - unsigned long flags; - int baud = 9600, bits = 8; - int parity = 'n'; - int flow = 'n'; - - up->flags |= IP22ZILOG_FLAG_IS_CONS; - - printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index); - - spin_lock_irqsave(&up->port.lock, flags); - - up->curregs[R15] |= BRKIE; - - __ip22zilog_startup(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(&up->port, con, baud, parity, bits, flow); -} - -static struct uart_driver ip22zilog_reg; - -static struct console ip22zilog_console = { - .name = "ttyS", - .write = ip22zilog_console_write, - .device = uart_console_device, - .setup = ip22zilog_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &ip22zilog_reg, -}; -#endif /* CONFIG_SERIAL_IP22_ZILOG_CONSOLE */ - -static struct uart_driver ip22zilog_reg = { - .owner = THIS_MODULE, - .driver_name = "serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = NUM_CHANNELS, -#ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE - .cons = &ip22zilog_console, -#endif -}; - -static void __init ip22zilog_prepare(void) -{ - struct uart_ip22zilog_port *up; - struct zilog_layout *rp; - int channel, chip; - - /* - * Temporary fix. - */ - for (channel = 0; channel < NUM_CHANNELS; channel++) - spin_lock_init(&ip22zilog_port_table[channel].port.lock); - - ip22zilog_irq_chain = &ip22zilog_port_table[NUM_CHANNELS - 1]; - up = &ip22zilog_port_table[0]; - for (channel = NUM_CHANNELS - 1 ; channel > 0; channel--) - up[channel].next = &up[channel - 1]; - up[channel].next = NULL; - - for (chip = 0; chip < NUM_IP22ZILOG; chip++) { - if (!ip22zilog_chip_regs[chip]) { - ip22zilog_chip_regs[chip] = rp = get_zs(chip); - - up[(chip * 2) + 0].port.membase = (char *) &rp->channelB; - up[(chip * 2) + 1].port.membase = (char *) &rp->channelA; - - /* In theory mapbase is the physical address ... */ - up[(chip * 2) + 0].port.mapbase = - (unsigned long) ioremap((unsigned long) &rp->channelB, 8); - up[(chip * 2) + 1].port.mapbase = - (unsigned long) ioremap((unsigned long) &rp->channelA, 8); - } - - /* Channel A */ - up[(chip * 2) + 0].port.iotype = UPIO_MEM; - up[(chip * 2) + 0].port.irq = zilog_irq; - up[(chip * 2) + 0].port.uartclk = ZS_CLOCK; - up[(chip * 2) + 0].port.fifosize = 1; - up[(chip * 2) + 0].port.ops = &ip22zilog_pops; - up[(chip * 2) + 0].port.type = PORT_IP22ZILOG; - up[(chip * 2) + 0].port.flags = 0; - up[(chip * 2) + 0].port.line = (chip * 2) + 0; - up[(chip * 2) + 0].flags = 0; - - /* Channel B */ - up[(chip * 2) + 1].port.iotype = UPIO_MEM; - up[(chip * 2) + 1].port.irq = zilog_irq; - up[(chip * 2) + 1].port.uartclk = ZS_CLOCK; - up[(chip * 2) + 1].port.fifosize = 1; - up[(chip * 2) + 1].port.ops = &ip22zilog_pops; - up[(chip * 2) + 1].port.type = PORT_IP22ZILOG; - up[(chip * 2) + 1].port.line = (chip * 2) + 1; - up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A; - } - - for (channel = 0; channel < NUM_CHANNELS; channel++) { - struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel]; - int brg; - - /* Normal serial TTY. */ - up->parity_mask = 0xff; - up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; - up->curregs[R4] = PAR_EVEN | X16CLK | SB1; - up->curregs[R3] = RxENAB | Rx8; - up->curregs[R5] = TxENAB | Tx8; - up->curregs[R9] = NV | MIE; - up->curregs[R10] = NRZ; - up->curregs[R11] = TCBR | RCBR; - brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR); - up->curregs[R12] = (brg & 0xff); - up->curregs[R13] = (brg >> 8) & 0xff; - up->curregs[R14] = BRENAB; - } -} - -static int __init ip22zilog_ports_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: IP22 Zilog driver (%d chips).\n", NUM_IP22ZILOG); - - ip22zilog_prepare(); - - if (request_irq(zilog_irq, ip22zilog_interrupt, 0, - "IP22-Zilog", ip22zilog_irq_chain)) { - panic("IP22-Zilog: Unable to register zs interrupt handler.\n"); - } - - ret = uart_register_driver(&ip22zilog_reg); - if (ret == 0) { - int i; - - for (i = 0; i < NUM_CHANNELS; i++) { - struct uart_ip22zilog_port *up = &ip22zilog_port_table[i]; - - uart_add_one_port(&ip22zilog_reg, &up->port); - } - } - - return ret; -} - -static int __init ip22zilog_init(void) -{ - /* IP22 Zilog setup is hard coded, no probing to do. */ - ip22zilog_alloc_tables(); - ip22zilog_ports_init(); - - return 0; -} - -static void __exit ip22zilog_exit(void) -{ - int i; - struct uart_ip22zilog_port *up; - - for (i = 0; i < NUM_CHANNELS; i++) { - up = &ip22zilog_port_table[i]; - - uart_remove_one_port(&ip22zilog_reg, &up->port); - } - - /* Free IO mem */ - up = &ip22zilog_port_table[0]; - for (i = 0; i < NUM_IP22ZILOG; i++) { - if (up[(i * 2) + 0].port.mapbase) { - iounmap((void*)up[(i * 2) + 0].port.mapbase); - up[(i * 2) + 0].port.mapbase = 0; - } - if (up[(i * 2) + 1].port.mapbase) { - iounmap((void*)up[(i * 2) + 1].port.mapbase); - up[(i * 2) + 1].port.mapbase = 0; - } - } - - uart_unregister_driver(&ip22zilog_reg); -} - -module_init(ip22zilog_init); -module_exit(ip22zilog_exit); - -/* David wrote it but I'm to blame for the bugs ... */ -MODULE_AUTHOR("Ralf Baechle "); -MODULE_DESCRIPTION("SGI Zilog serial port driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/ip22zilog.h b/drivers/serial/ip22zilog.h deleted file mode 100644 index a59a9a8341d2..000000000000 --- a/drivers/serial/ip22zilog.h +++ /dev/null @@ -1,281 +0,0 @@ -#ifndef _IP22_ZILOG_H -#define _IP22_ZILOG_H - -#include - -struct zilog_channel { -#ifdef __BIG_ENDIAN - volatile unsigned char unused0[3]; - volatile unsigned char control; - volatile unsigned char unused1[3]; - volatile unsigned char data; -#else /* __LITTLE_ENDIAN */ - volatile unsigned char control; - volatile unsigned char unused0[3]; - volatile unsigned char data; - volatile unsigned char unused1[3]; -#endif -}; - -struct zilog_layout { - struct zilog_channel channelB; - struct zilog_channel channelA; -}; - -#define NUM_ZSREGS 16 - -/* Conversion routines to/from brg time constants from/to bits - * per second. - */ -#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) -#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) - -/* The Zilog register set */ - -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ -#define RxINT_MASK 0x18 - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENAB 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ -#define RxN_MASK 0xc0 - -/* Write Register 4 */ - -#define PAR_ENAB 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ -#define XCLK_MASK 0xC0 - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define TxN_MASK 0x60 -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENAB 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define ZCIE 2 /* Zero count IE */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ -#define CHB_Tx_EMPTY 0x00 -#define CHB_EXT_STAT 0x02 -#define CHB_Rx_AVAIL 0x04 -#define CHB_SPECIAL 0x06 -#define CHA_Tx_EMPTY 0x08 -#define CHA_EXT_STAT 0x0a -#define CHA_Rx_AVAIL 0x0c -#define CHA_SPECIAL 0x0e -#define STATUS_MASK 0x0e - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Misc macros */ -#define ZS_CLEARERR(channel) do { writeb(ERR_RES, &channel->control); \ - udelay(5); } while(0) - -#define ZS_CLEARSTAT(channel) do { writeb(RES_EXT_INT, &channel->control); \ - udelay(5); } while(0) - -#define ZS_CLEARFIFO(channel) do { readb(&channel->data); \ - udelay(2); \ - readb(&channel->data); \ - udelay(2); \ - readb(&channel->data); \ - udelay(2); } while(0) - -#endif /* _IP22_ZILOG_H */ diff --git a/drivers/serial/jsm/Makefile b/drivers/serial/jsm/Makefile deleted file mode 100644 index e46b6e0f8b18..000000000000 --- a/drivers/serial/jsm/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for Jasmine adapter -# - -obj-$(CONFIG_SERIAL_JSM) += jsm.o - -jsm-objs := jsm_driver.o jsm_neo.o jsm_tty.o - diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h deleted file mode 100644 index 38a509c684cd..000000000000 --- a/drivers/serial/jsm/jsm.h +++ /dev/null @@ -1,388 +0,0 @@ -/************************************************************************ - * Copyright 2003 Digi International (www.digi.com) - * - * Copyright (C) 2004 IBM Corporation. All rights reserved. - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. - * - * Contact Information: - * Scott H Kilau - * Wendy Xiong - * - ***********************************************************************/ - -#ifndef __JSM_DRIVER_H -#define __JSM_DRIVER_H - -#include -#include /* To pick up the varions Linux types */ -#include -#include -#include - -/* - * Debugging levels can be set using debug insmod variable - * They can also be compiled out completely. - */ -enum { - DBG_INIT = 0x01, - DBG_BASIC = 0x02, - DBG_CORE = 0x04, - DBG_OPEN = 0x08, - DBG_CLOSE = 0x10, - DBG_READ = 0x20, - DBG_WRITE = 0x40, - DBG_IOCTL = 0x80, - DBG_PROC = 0x100, - DBG_PARAM = 0x200, - DBG_PSCAN = 0x400, - DBG_EVENT = 0x800, - DBG_DRAIN = 0x1000, - DBG_MSIGS = 0x2000, - DBG_MGMT = 0x4000, - DBG_INTR = 0x8000, - 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 MAXLINES 256 -#define MAXPORTS 8 -#define MAX_STOPS_SENT 5 - -/* Board type definitions */ - -#define T_NEO 0000 -#define T_CLASSIC 0001 -#define T_PCIBUS 0400 - -/* Board State Definitions */ - -#define BD_RUNNING 0x0 -#define BD_REASON 0x7f -#define BD_NOTFOUND 0x1 -#define BD_NOIOPORT 0x2 -#define BD_NOMEM 0x3 -#define BD_NOBIOS 0x4 -#define BD_NOFEP 0x5 -#define BD_FAILED 0x6 -#define BD_ALLOCATED 0x7 -#define BD_TRIBOOT 0x8 -#define BD_BADKME 0x80 - - -/* 4 extra for alignment play space */ -#define WRITEBUFLEN ((4096) + 4) -#define MYFLIPLEN N_TTY_BUF_SIZE - -#define JSM_VERSION "jsm: 1.2-1-INKERNEL" -#define JSM_PARTNUM "40002438_A-INKERNEL" - -struct jsm_board; -struct jsm_channel; - -/************************************************************************ - * Per board operations structure * - ************************************************************************/ -struct board_ops { - irq_handler_t intr; - void (*uart_init) (struct jsm_channel *ch); - void (*uart_off) (struct jsm_channel *ch); - void (*param) (struct jsm_channel *ch); - void (*assert_modem_signals) (struct jsm_channel *ch); - void (*flush_uart_write) (struct jsm_channel *ch); - void (*flush_uart_read) (struct jsm_channel *ch); - void (*disable_receiver) (struct jsm_channel *ch); - void (*enable_receiver) (struct jsm_channel *ch); - void (*send_break) (struct jsm_channel *ch); - void (*clear_break) (struct jsm_channel *ch, int); - void (*send_start_character) (struct jsm_channel *ch); - void (*send_stop_character) (struct jsm_channel *ch); - void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch); - u32 (*get_uart_bytes_left) (struct jsm_channel *ch); - void (*send_immediate_char) (struct jsm_channel *ch, unsigned char); -}; - - -/* - * Per-board information - */ -struct jsm_board -{ - int boardnum; /* Board number: 0-32 */ - - int type; /* Type of board */ - u8 rev; /* PCI revision ID */ - struct pci_dev *pci_dev; - u32 maxports; /* MAX ports this board can handle */ - - spinlock_t bd_intr_lock; /* Used to protect the poller tasklet and - * the interrupt routine from each other. - */ - - u32 nasync; /* Number of ports on card */ - - u32 irq; /* Interrupt request number */ - - u64 membase; /* Start of base memory of the card */ - u64 membase_end; /* End of base memory of the card */ - - u8 __iomem *re_map_membase;/* Remapped memory of the card */ - - u64 iobase; /* Start of io base of the card */ - u64 iobase_end; /* End of io base of the card */ - - u32 bd_uart_offset; /* Space between each UART */ - - struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */ - char *flipbuf; /* Our flip buffer, alloced if board is found */ - - u32 bd_dividend; /* Board/UARTs specific dividend */ - - struct board_ops *bd_ops; - - struct list_head jsm_board_entry; -}; - -/************************************************************************ - * Device flag definitions for ch_flags. - ************************************************************************/ -#define CH_PRON 0x0001 /* Printer on string */ -#define CH_STOP 0x0002 /* Output is stopped */ -#define CH_STOPI 0x0004 /* Input is stopped */ -#define CH_CD 0x0008 /* Carrier is present */ -#define CH_FCAR 0x0010 /* Carrier forced on */ -#define CH_HANGUP 0x0020 /* Hangup received */ - -#define CH_RECEIVER_OFF 0x0040 /* Receiver is off */ -#define CH_OPENING 0x0080 /* Port in fragile open state */ -#define CH_CLOSING 0x0100 /* Port in fragile close state */ -#define CH_FIFO_ENABLED 0x0200 /* Port has FIFOs enabled */ -#define CH_TX_FIFO_EMPTY 0x0400 /* TX Fifo is completely empty */ -#define CH_TX_FIFO_LWM 0x0800 /* TX Fifo is below Low Water */ -#define CH_BREAK_SENDING 0x1000 /* Break is being sent */ -#define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */ -#define CH_FLIPBUF_IN_USE 0x4000 /* Channel's flipbuf is in use */ -#define CH_BAUD0 0x08000 /* Used for checking B0 transitions */ - -/* Our Read/Error/Write queue sizes */ -#define RQUEUEMASK 0x1FFF /* 8 K - 1 */ -#define EQUEUEMASK 0x1FFF /* 8 K - 1 */ -#define WQUEUEMASK 0x0FFF /* 4 K - 1 */ -#define RQUEUESIZE (RQUEUEMASK + 1) -#define EQUEUESIZE RQUEUESIZE -#define WQUEUESIZE (WQUEUEMASK + 1) - - -/************************************************************************ - * Channel information structure. - ************************************************************************/ -struct jsm_channel { - struct uart_port uart_port; - struct jsm_board *ch_bd; /* Board structure pointer */ - - spinlock_t ch_lock; /* provide for serialization */ - wait_queue_head_t ch_flags_wait; - - u32 ch_portnum; /* Port number, 0 offset. */ - u32 ch_open_count; /* open count */ - u32 ch_flags; /* Channel flags */ - - u64 ch_close_delay; /* How long we should drop RTS/DTR for */ - - tcflag_t ch_c_iflag; /* channel iflags */ - tcflag_t ch_c_cflag; /* channel cflags */ - tcflag_t ch_c_oflag; /* channel oflags */ - tcflag_t ch_c_lflag; /* channel lflags */ - u8 ch_stopc; /* Stop character */ - u8 ch_startc; /* Start character */ - - u8 ch_mostat; /* FEP output modem status */ - u8 ch_mistat; /* FEP input modem status */ - - struct neo_uart_struct __iomem *ch_neo_uart; /* Pointer to the "mapped" UART struct */ - u8 ch_cached_lsr; /* Cached value of the LSR register */ - - u8 *ch_rqueue; /* Our read queue buffer - malloc'ed */ - u16 ch_r_head; /* Head location of the read queue */ - u16 ch_r_tail; /* Tail location of the read queue */ - - u8 *ch_equeue; /* Our error queue buffer - malloc'ed */ - u16 ch_e_head; /* Head location of the error queue */ - u16 ch_e_tail; /* Tail location of the error queue */ - - u8 *ch_wqueue; /* Our write queue buffer - malloc'ed */ - u16 ch_w_head; /* Head location of the write queue */ - u16 ch_w_tail; /* Tail location of the write queue */ - - u64 ch_rxcount; /* total of data received so far */ - u64 ch_txcount; /* total of data transmitted so far */ - - u8 ch_r_tlevel; /* Receive Trigger level */ - u8 ch_t_tlevel; /* Transmit Trigger level */ - - u8 ch_r_watermark; /* Receive Watermark */ - - - u32 ch_stops_sent; /* How many times I have sent a stop character - * to try to stop the other guy sending. - */ - u64 ch_err_parity; /* Count of parity errors on channel */ - u64 ch_err_frame; /* Count of framing errors on channel */ - u64 ch_err_break; /* Count of breaks on channel */ - u64 ch_err_overrun; /* Count of overruns on channel */ - - u64 ch_xon_sends; /* Count of xons transmitted */ - u64 ch_xoff_sends; /* Count of xoffs transmitted */ -}; - - -/************************************************************************ - * Per channel/port NEO UART structure * - ************************************************************************ - * Base Structure Entries Usage Meanings to Host * - * * - * W = read write R = read only * - * U = Unused. * - ************************************************************************/ - -struct neo_uart_struct { - u8 txrx; /* WR RHR/THR - Holding Reg */ - u8 ier; /* WR IER - Interrupt Enable Reg */ - u8 isr_fcr; /* WR ISR/FCR - Interrupt Status Reg/Fifo Control Reg */ - u8 lcr; /* WR LCR - Line Control Reg */ - u8 mcr; /* WR MCR - Modem Control Reg */ - u8 lsr; /* WR LSR - Line Status Reg */ - u8 msr; /* WR MSR - Modem Status Reg */ - u8 spr; /* WR SPR - Scratch Pad Reg */ - u8 fctr; /* WR FCTR - Feature Control Reg */ - u8 efr; /* WR EFR - Enhanced Function Reg */ - u8 tfifo; /* WR TXCNT/TXTRG - Transmit FIFO Reg */ - u8 rfifo; /* WR RXCNT/RXTRG - Recieve FIFO Reg */ - u8 xoffchar1; /* WR XOFF 1 - XOff Character 1 Reg */ - u8 xoffchar2; /* WR XOFF 2 - XOff Character 2 Reg */ - u8 xonchar1; /* WR XON 1 - Xon Character 1 Reg */ - u8 xonchar2; /* WR XON 2 - XOn Character 2 Reg */ - - u8 reserved1[0x2ff - 0x200]; /* U Reserved by Exar */ - u8 txrxburst[64]; /* RW 64 bytes of RX/TX FIFO Data */ - u8 reserved2[0x37f - 0x340]; /* U Reserved by Exar */ - u8 rxburst_with_errors[64]; /* R 64 bytes of RX FIFO Data + LSR */ -}; - -/* Where to read the extended interrupt register (32bits instead of 8bits) */ -#define UART_17158_POLL_ADDR_OFFSET 0x80 - -/* - * These are the redefinitions for the FCTR on the XR17C158, since - * Exar made them different than their earlier design. (XR16C854) - */ - -/* These are only applicable when table D is selected */ -#define UART_17158_FCTR_RTS_NODELAY 0x00 -#define UART_17158_FCTR_RTS_4DELAY 0x01 -#define UART_17158_FCTR_RTS_6DELAY 0x02 -#define UART_17158_FCTR_RTS_8DELAY 0x03 -#define UART_17158_FCTR_RTS_12DELAY 0x12 -#define UART_17158_FCTR_RTS_16DELAY 0x05 -#define UART_17158_FCTR_RTS_20DELAY 0x13 -#define UART_17158_FCTR_RTS_24DELAY 0x06 -#define UART_17158_FCTR_RTS_28DELAY 0x14 -#define UART_17158_FCTR_RTS_32DELAY 0x07 -#define UART_17158_FCTR_RTS_36DELAY 0x16 -#define UART_17158_FCTR_RTS_40DELAY 0x08 -#define UART_17158_FCTR_RTS_44DELAY 0x09 -#define UART_17158_FCTR_RTS_48DELAY 0x10 -#define UART_17158_FCTR_RTS_52DELAY 0x11 - -#define UART_17158_FCTR_RTS_IRDA 0x10 -#define UART_17158_FCTR_RS485 0x20 -#define UART_17158_FCTR_TRGA 0x00 -#define UART_17158_FCTR_TRGB 0x40 -#define UART_17158_FCTR_TRGC 0x80 -#define UART_17158_FCTR_TRGD 0xC0 - -/* 17158 trigger table selects.. */ -#define UART_17158_FCTR_BIT6 0x40 -#define UART_17158_FCTR_BIT7 0x80 - -/* 17158 TX/RX memmapped buffer offsets */ -#define UART_17158_RX_FIFOSIZE 64 -#define UART_17158_TX_FIFOSIZE 64 - -/* 17158 Extended IIR's */ -#define UART_17158_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */ -#define UART_17158_IIR_XONXOFF 0x10 /* Received an XON/XOFF char */ -#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20 /* CTS/DSR or RTS/DTR state change */ -#define UART_17158_IIR_FIFO_ENABLED 0xC0 /* 16550 FIFOs are Enabled */ - -/* - * These are the extended interrupts that get sent - * back to us from the UART's 32bit interrupt register - */ -#define UART_17158_RX_LINE_STATUS 0x1 /* RX Ready */ -#define UART_17158_RXRDY_TIMEOUT 0x2 /* RX Ready Timeout */ -#define UART_17158_TXRDY 0x3 /* TX Ready */ -#define UART_17158_MSR 0x4 /* Modem State Change */ -#define UART_17158_TX_AND_FIFO_CLR 0x40 /* Transmitter Holding Reg Empty */ -#define UART_17158_RX_FIFO_DATA_ERROR 0x80 /* UART detected an RX FIFO Data error */ - -/* - * These are the EXTENDED definitions for the 17C158's Interrupt - * Enable Register. - */ -#define UART_17158_EFR_ECB 0x10 /* Enhanced control bit */ -#define UART_17158_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */ -#define UART_17158_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */ -#define UART_17158_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */ -#define UART_17158_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow COntrol Enable */ - -#define UART_17158_XOFF_DETECT 0x1 /* Indicates whether chip saw an incoming XOFF char */ -#define UART_17158_XON_DETECT 0x2 /* Indicates whether chip saw an incoming XON char */ - -#define UART_17158_IER_RSVD1 0x10 /* Reserved by Exar */ -#define UART_17158_IER_XOFF 0x20 /* Xoff Interrupt Enable */ -#define UART_17158_IER_RTSDTR 0x40 /* Output Interrupt Enable */ -#define UART_17158_IER_CTSDSR 0x80 /* Input Interrupt Enable */ - -#define PCI_DEVICE_NEO_2DB9_PCI_NAME "Neo 2 - DB9 Universal PCI" -#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME "Neo 2 - DB9 Universal PCI - Powered Ring Indicator" -#define PCI_DEVICE_NEO_2RJ45_PCI_NAME "Neo 2 - RJ45 Universal PCI" -#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME "Neo 2 - RJ45 Universal PCI - Powered Ring Indicator" -#define PCIE_DEVICE_NEO_IBM_PCI_NAME "Neo 4 - PCI Express - IBM" - -/* - * Our Global Variables. - */ -extern struct uart_driver jsm_uart_driver; -extern struct board_ops jsm_neo_ops; -extern int jsm_debug; - -/************************************************************************* - * - * Prototypes for non-static functions used in more than one module - * - *************************************************************************/ -int jsm_tty_write(struct uart_port *port); -int jsm_tty_init(struct jsm_board *); -int jsm_uart_port_init(struct jsm_board *); -int jsm_remove_uart_port(struct jsm_board *); -void jsm_input(struct jsm_channel *ch); -void jsm_check_queue_flow_control(struct jsm_channel *ch); - -#endif diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c deleted file mode 100644 index 18f548449c63..000000000000 --- a/drivers/serial/jsm/jsm_driver.c +++ /dev/null @@ -1,297 +0,0 @@ -/************************************************************************ - * Copyright 2003 Digi International (www.digi.com) - * - * Copyright (C) 2004 IBM Corporation. All rights reserved. - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. - * - * Contact Information: - * Scott H Kilau - * Wendy Xiong - * - * - ***********************************************************************/ -#include -#include -#include - -#include "jsm.h" - -MODULE_AUTHOR("Digi International, http://www.digi.com"); -MODULE_DESCRIPTION("Driver for the Digi International " - "Neo PCI based product line"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("jsm"); - -#define JSM_DRIVER_NAME "jsm" -#define NR_PORTS 32 -#define JSM_MINOR_START 0 - -struct uart_driver jsm_uart_driver = { - .owner = THIS_MODULE, - .driver_name = JSM_DRIVER_NAME, - .dev_name = "ttyn", - .major = 0, - .minor = JSM_MINOR_START, - .nr = NR_PORTS, -}; - -static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, - pci_channel_state_t state); -static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev); -static void jsm_io_resume(struct pci_dev *pdev); - -static struct pci_error_handlers jsm_err_handler = { - .error_detected = jsm_io_error_detected, - .slot_reset = jsm_io_slot_reset, - .resume = jsm_io_resume, -}; - -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) -{ - int rc = 0; - struct jsm_board *brd; - static int adapter_count = 0; - - rc = pci_enable_device(pdev); - if (rc) { - dev_err(&pdev->dev, "Device enable FAILED\n"); - goto out; - } - - rc = pci_request_regions(pdev, "jsm"); - if (rc) { - dev_err(&pdev->dev, "pci_request_region FAILED\n"); - goto out_disable_device; - } - - brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL); - if (!brd) { - dev_err(&pdev->dev, - "memory allocation for board structure failed\n"); - rc = -ENOMEM; - goto out_release_regions; - } - - /* store the info for the board we've found */ - brd->boardnum = adapter_count++; - brd->pci_dev = pdev; - if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM) - brd->maxports = 4; - else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8) - brd->maxports = 8; - else - brd->maxports = 2; - - spin_lock_init(&brd->bd_intr_lock); - - /* store which revision we have */ - brd->rev = pdev->revision; - - brd->irq = pdev->irq; - - jsm_printk(INIT, INFO, &brd->pci_dev, - "jsm_found_board - NEO adapter\n"); - - /* get the PCI Base Address Registers */ - brd->membase = pci_resource_start(pdev, 0); - brd->membase_end = pci_resource_end(pdev, 0); - - if (brd->membase & 1) - brd->membase &= ~3; - else - brd->membase &= ~15; - - /* Assign the board_ops struct */ - brd->bd_ops = &jsm_neo_ops; - - brd->bd_uart_offset = 0x200; - brd->bd_dividend = 921600; - - brd->re_map_membase = ioremap(brd->membase, 0x1000); - if (!brd->re_map_membase) { - dev_err(&pdev->dev, - "card has no PCI Memory resources, " - "failing board.\n"); - rc = -ENOMEM; - goto out_kfree_brd; - } - - rc = request_irq(brd->irq, brd->bd_ops->intr, - IRQF_SHARED, "JSM", brd); - if (rc) { - printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq); - goto out_iounmap; - } - - rc = jsm_tty_init(brd); - if (rc < 0) { - dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc); - rc = -ENXIO; - goto out_free_irq; - } - - rc = jsm_uart_port_init(brd); - if (rc < 0) { - /* XXX: leaking all resources from jsm_tty_init here! */ - dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc); - rc = -ENXIO; - goto out_free_irq; - } - - /* Log the information about the board */ - dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n", - adapter_count, brd->rev, brd->irq); - - /* - * allocate flip buffer for board. - * - * Okay to malloc with GFP_KERNEL, we are not at interrupt - * context, and there are no locks held. - */ - brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); - if (!brd->flipbuf) { - /* XXX: leaking all resources from jsm_tty_init and - jsm_uart_port_init here! */ - dev_err(&pdev->dev, "memory allocation for flipbuf failed\n"); - rc = -ENOMEM; - goto out_free_uart; - } - - pci_set_drvdata(pdev, brd); - pci_save_state(pdev); - - return 0; - out_free_uart: - jsm_remove_uart_port(brd); - out_free_irq: - jsm_remove_uart_port(brd); - free_irq(brd->irq, brd); - out_iounmap: - iounmap(brd->re_map_membase); - out_kfree_brd: - kfree(brd); - out_release_regions: - pci_release_regions(pdev); - out_disable_device: - pci_disable_device(pdev); - out: - return rc; -} - -static void __devexit jsm_remove_one(struct pci_dev *pdev) -{ - struct jsm_board *brd = pci_get_drvdata(pdev); - int i = 0; - - jsm_remove_uart_port(brd); - - free_irq(brd->irq, brd); - iounmap(brd->re_map_membase); - - /* Free all allocated channels structs */ - for (i = 0; i < brd->maxports; i++) { - if (brd->channels[i]) { - kfree(brd->channels[i]->ch_rqueue); - kfree(brd->channels[i]->ch_equeue); - kfree(brd->channels[i]->ch_wqueue); - kfree(brd->channels[i]); - } - } - - pci_release_regions(pdev); - pci_disable_device(pdev); - kfree(brd->flipbuf); - kfree(brd); -} - -static struct pci_device_id jsm_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, - { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); - -static struct pci_driver jsm_driver = { - .name = "jsm", - .id_table = jsm_pci_tbl, - .probe = jsm_probe_one, - .remove = __devexit_p(jsm_remove_one), - .err_handler = &jsm_err_handler, -}; - -static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, - pci_channel_state_t state) -{ - struct jsm_board *brd = pci_get_drvdata(pdev); - - jsm_remove_uart_port(brd); - - return PCI_ERS_RESULT_NEED_RESET; -} - -static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev) -{ - int rc; - - rc = pci_enable_device(pdev); - - if (rc) - return PCI_ERS_RESULT_DISCONNECT; - - pci_set_master(pdev); - - return PCI_ERS_RESULT_RECOVERED; -} - -static void jsm_io_resume(struct pci_dev *pdev) -{ - struct jsm_board *brd = pci_get_drvdata(pdev); - - pci_restore_state(pdev); - - jsm_uart_port_init(brd); -} - -static int __init jsm_init_module(void) -{ - int rc; - - rc = uart_register_driver(&jsm_uart_driver); - if (!rc) { - rc = pci_register_driver(&jsm_driver); - if (rc) - uart_unregister_driver(&jsm_uart_driver); - } - return rc; -} - -static void __exit jsm_exit_module(void) -{ - pci_unregister_driver(&jsm_driver); - uart_unregister_driver(&jsm_uart_driver); -} - -module_init(jsm_init_module); -module_exit(jsm_exit_module); diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c deleted file mode 100644 index 7960d9633c15..000000000000 --- a/drivers/serial/jsm/jsm_neo.c +++ /dev/null @@ -1,1412 +0,0 @@ -/************************************************************************ - * Copyright 2003 Digi International (www.digi.com) - * - * Copyright (C) 2004 IBM Corporation. All rights reserved. - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. - * - * Contact Information: - * Scott H Kilau - * Wendy Xiong - * - ***********************************************************************/ -#include /* For udelay */ -#include /* For the various UART offsets */ -#include -#include -#include - -#include "jsm.h" /* Driver main header file */ - -static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; - -/* - * This function allows calls to ensure that all outstanding - * PCI writes have been completed, by doing a PCI read against - * a non-destructive, read-only location on the Neo card. - * - * In this case, we are reading the DVID (Read-only Device Identification) - * value of the Neo card. - */ -static inline void neo_pci_posting_flush(struct jsm_board *bd) -{ - readb(bd->re_map_membase + 0x8D); -} - -static void neo_set_cts_flow_control(struct jsm_channel *ch) -{ - u8 ier, efr; - 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"); - - /* Turn on auto CTS flow control */ - ier |= (UART_17158_IER_CTSDSR); - efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR); - - /* Turn off auto Xon flow control */ - efr &= ~(UART_17158_EFR_IXON); - - /* Why? Becuz Exar's spec says we have to zero it out before setting it */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Turn on UART enhanced bits */ - writeb(efr, &ch->ch_neo_uart->efr); - - /* Turn on table D, with 8 char hi/low watermarks */ - writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr); - - /* Feed the UART our trigger levels */ - writeb(8, &ch->ch_neo_uart->tfifo); - ch->ch_t_tlevel = 8; - - writeb(ier, &ch->ch_neo_uart->ier); -} - -static void neo_set_rts_flow_control(struct jsm_channel *ch) -{ - u8 ier, efr; - 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"); - - /* Turn on auto RTS flow control */ - ier |= (UART_17158_IER_RTSDTR); - efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR); - - /* Turn off auto Xoff flow control */ - ier &= ~(UART_17158_IER_XOFF); - efr &= ~(UART_17158_EFR_IXOFF); - - /* Why? Becuz Exar's spec says we have to zero it out before setting it */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Turn on UART enhanced bits */ - writeb(efr, &ch->ch_neo_uart->efr); - - writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr); - ch->ch_r_watermark = 4; - - writeb(56, &ch->ch_neo_uart->rfifo); - ch->ch_r_tlevel = 56; - - writeb(ier, &ch->ch_neo_uart->ier); - - /* - * From the Neo UART spec sheet: - * The auto RTS/DTR function must be started by asserting - * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after - * it is enabled. - */ - ch->ch_mostat |= (UART_MCR_RTS); -} - - -static void neo_set_ixon_flow_control(struct jsm_channel *ch) -{ - u8 ier, efr; - 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"); - - /* Turn off auto CTS flow control */ - ier &= ~(UART_17158_IER_CTSDSR); - efr &= ~(UART_17158_EFR_CTSDSR); - - /* Turn on auto Xon flow control */ - efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON); - - /* Why? Becuz Exar's spec says we have to zero it out before setting it */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Turn on UART enhanced bits */ - writeb(efr, &ch->ch_neo_uart->efr); - - writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); - ch->ch_r_watermark = 4; - - writeb(32, &ch->ch_neo_uart->rfifo); - ch->ch_r_tlevel = 32; - - /* Tell UART what start/stop chars it should be looking for */ - writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1); - writeb(0, &ch->ch_neo_uart->xonchar2); - - writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1); - writeb(0, &ch->ch_neo_uart->xoffchar2); - - writeb(ier, &ch->ch_neo_uart->ier); -} - -static void neo_set_ixoff_flow_control(struct jsm_channel *ch) -{ - u8 ier, efr; - 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"); - - /* Turn off auto RTS flow control */ - ier &= ~(UART_17158_IER_RTSDTR); - efr &= ~(UART_17158_EFR_RTSDTR); - - /* Turn on auto Xoff flow control */ - ier |= (UART_17158_IER_XOFF); - efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF); - - /* Why? Becuz Exar's spec says we have to zero it out before setting it */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Turn on UART enhanced bits */ - writeb(efr, &ch->ch_neo_uart->efr); - - /* Turn on table D, with 8 char hi/low watermarks */ - writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); - - writeb(8, &ch->ch_neo_uart->tfifo); - ch->ch_t_tlevel = 8; - - /* Tell UART what start/stop chars it should be looking for */ - writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1); - writeb(0, &ch->ch_neo_uart->xonchar2); - - writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1); - writeb(0, &ch->ch_neo_uart->xoffchar2); - - writeb(ier, &ch->ch_neo_uart->ier); -} - -static void neo_set_no_input_flow_control(struct jsm_channel *ch) -{ - u8 ier, efr; - 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"); - - /* Turn off auto RTS flow control */ - ier &= ~(UART_17158_IER_RTSDTR); - efr &= ~(UART_17158_EFR_RTSDTR); - - /* Turn off auto Xoff flow control */ - ier &= ~(UART_17158_IER_XOFF); - if (ch->ch_c_iflag & IXON) - efr &= ~(UART_17158_EFR_IXOFF); - else - efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF); - - /* Why? Becuz Exar's spec says we have to zero it out before setting it */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Turn on UART enhanced bits */ - writeb(efr, &ch->ch_neo_uart->efr); - - /* Turn on table D, with 8 char hi/low watermarks */ - writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); - - ch->ch_r_watermark = 0; - - writeb(16, &ch->ch_neo_uart->tfifo); - ch->ch_t_tlevel = 16; - - writeb(16, &ch->ch_neo_uart->rfifo); - ch->ch_r_tlevel = 16; - - writeb(ier, &ch->ch_neo_uart->ier); -} - -static void neo_set_no_output_flow_control(struct jsm_channel *ch) -{ - u8 ier, efr; - 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"); - - /* Turn off auto CTS flow control */ - ier &= ~(UART_17158_IER_CTSDSR); - efr &= ~(UART_17158_EFR_CTSDSR); - - /* Turn off auto Xon flow control */ - if (ch->ch_c_iflag & IXOFF) - efr &= ~(UART_17158_EFR_IXON); - else - efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON); - - /* Why? Becuz Exar's spec says we have to zero it out before setting it */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Turn on UART enhanced bits */ - writeb(efr, &ch->ch_neo_uart->efr); - - /* Turn on table D, with 8 char hi/low watermarks */ - writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); - - ch->ch_r_watermark = 0; - - writeb(16, &ch->ch_neo_uart->tfifo); - ch->ch_t_tlevel = 16; - - writeb(16, &ch->ch_neo_uart->rfifo); - ch->ch_r_tlevel = 16; - - writeb(ier, &ch->ch_neo_uart->ier); -} - -static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch) -{ - - /* if hardware flow control is set, then skip this whole thing */ - if (ch->ch_c_cflag & CRTSCTS) - return; - - jsm_printk(PARAM, INFO, &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); - writeb(0, &ch->ch_neo_uart->xonchar2); - - writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1); - writeb(0, &ch->ch_neo_uart->xoffchar2); -} - -static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) -{ - int qleft = 0; - u8 linestatus = 0; - u8 error_mask = 0; - int n = 0; - int total = 0; - u16 head; - u16 tail; - - if (!ch) - return; - - /* cache head and tail of queue */ - head = ch->ch_r_head & RQUEUEMASK; - tail = ch->ch_r_tail & RQUEUEMASK; - - /* Get our cached LSR */ - linestatus = ch->ch_cached_lsr; - ch->ch_cached_lsr = 0; - - /* Store how much space we have left in the queue */ - if ((qleft = tail - head - 1) < 0) - qleft += RQUEUEMASK + 1; - - /* - * If the UART is not in FIFO mode, force the FIFO copy to - * NOT be run, by setting total to 0. - * - * On the other hand, if the UART IS in FIFO mode, then ask - * the UART to give us an approximation of data it has RX'ed. - */ - if (!(ch->ch_flags & CH_FIFO_ENABLED)) - total = 0; - else { - total = readb(&ch->ch_neo_uart->rfifo); - - /* - * EXAR chip bug - RX FIFO COUNT - Fudge factor. - * - * This resolves a problem/bug with the Exar chip that sometimes - * returns a bogus value in the rfifo register. - * The count can be any where from 0-3 bytes "off". - * Bizarre, but true. - */ - total -= 3; - } - - /* - * Finally, bound the copy to make sure we don't overflow - * our own queue... - * The byte by byte copy loop below this loop this will - * deal with the queue overflow possibility. - */ - total = min(total, qleft); - - while (total > 0) { - /* - * Grab the linestatus register, we need to check - * to see if there are any errors in the FIFO. - */ - linestatus = readb(&ch->ch_neo_uart->lsr); - - /* - * Break out if there is a FIFO error somewhere. - * This will allow us to go byte by byte down below, - * finding the exact location of the error. - */ - if (linestatus & UART_17158_RX_FIFO_DATA_ERROR) - break; - - /* Make sure we don't go over the end of our queue */ - n = min(((u32) total), (RQUEUESIZE - (u32) head)); - - /* - * Cut down n even further if needed, this is to fix - * a problem with memcpy_fromio() with the Neo on the - * IBM pSeries platform. - * 15 bytes max appears to be the magic number. - */ - n = min((u32) n, (u32) 12); - - /* - * Since we are grabbing the linestatus register, which - * will reset some bits after our read, we need to ensure - * we don't miss our TX FIFO emptys. - */ - if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - - linestatus = 0; - - /* Copy data from uart to the queue */ - memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n); - /* - * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed - * that all the data currently in the FIFO is free of - * breaks and parity/frame/orun errors. - */ - memset(ch->ch_equeue + head, 0, n); - - /* Add to and flip head if needed */ - head = (head + n) & RQUEUEMASK; - total -= n; - qleft -= n; - ch->ch_rxcount += n; - } - - /* - * Create a mask to determine whether we should - * insert the character (if any) into our queue. - */ - if (ch->ch_c_iflag & IGNBRK) - error_mask |= UART_LSR_BI; - - /* - * Now cleanup any leftover bytes still in the UART. - * Also deal with any possible queue overflow here as well. - */ - while (1) { - - /* - * Its possible we have a linestatus from the loop above - * this, so we "OR" on any extra bits. - */ - linestatus |= readb(&ch->ch_neo_uart->lsr); - - /* - * If the chip tells us there is no more data pending to - * be read, we can then leave. - * But before we do, cache the linestatus, just in case. - */ - if (!(linestatus & UART_LSR_DR)) { - ch->ch_cached_lsr = linestatus; - break; - } - - /* No need to store this bit */ - linestatus &= ~UART_LSR_DR; - - /* - * Since we are grabbing the linestatus register, which - * will reset some bits after our read, we need to ensure - * we don't miss our TX FIFO emptys. - */ - if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) { - linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR); - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - } - - /* - * Discard character if we are ignoring the error mask. - */ - if (linestatus & error_mask) { - u8 discard; - linestatus = 0; - memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1); - continue; - } - - /* - * If our queue is full, we have no choice but to drop some data. - * The assumption is that HWFLOW or SWFLOW should have stopped - * things way way before we got to this point. - * - * I decided that I wanted to ditch the oldest data first, - * I hope thats okay with everyone? Yes? Good. - */ - while (qleft < 1) { - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, - "Queue full, dropping DATA:%x LSR:%x\n", - ch->ch_rqueue[tail], ch->ch_equeue[tail]); - - ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK; - ch->ch_err_overrun++; - qleft++; - } - - 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]); - - /* Ditch any remaining linestatus value. */ - linestatus = 0; - - /* Add to and flip head if needed */ - head = (head + 1) & RQUEUEMASK; - - qleft--; - ch->ch_rxcount++; - } - - /* - * Write new final heads to channel structure. - */ - ch->ch_r_head = head & RQUEUEMASK; - ch->ch_e_head = head & EQUEUEMASK; - jsm_input(ch); -} - -static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) -{ - u16 head; - u16 tail; - int n; - int s; - int qlen; - u32 len_written = 0; - - if (!ch) - return; - - /* No data to write to the UART */ - if (ch->ch_w_tail == ch->ch_w_head) - return; - - /* If port is "stopped", don't send any data to the UART */ - if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) - return; - /* - * If FIFOs are disabled. Send data directly to txrx register - */ - if (!(ch->ch_flags & CH_FIFO_ENABLED)) { - u8 lsrbits = readb(&ch->ch_neo_uart->lsr); - - ch->ch_cached_lsr |= lsrbits; - if (ch->ch_cached_lsr & UART_LSR_THRE) { - ch->ch_cached_lsr &= ~(UART_LSR_THRE); - - writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx); - jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, - "Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]); - ch->ch_w_tail++; - ch->ch_w_tail &= WQUEUEMASK; - ch->ch_txcount++; - } - return; - } - - /* - * We have to do it this way, because of the EXAR TXFIFO count bug. - */ - if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) - return; - - n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel; - - /* cache head and tail of queue */ - head = ch->ch_w_head & WQUEUEMASK; - tail = ch->ch_w_tail & WQUEUEMASK; - qlen = (head - tail) & WQUEUEMASK; - - /* Find minimum of the FIFO space, versus queue length */ - n = min(n, qlen); - - while (n > 0) { - - s = ((head >= tail) ? head : WQUEUESIZE) - tail; - s = min(s, n); - - if (s <= 0) - break; - - memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s); - /* Add and flip queue if needed */ - tail = (tail + s) & WQUEUEMASK; - n -= s; - ch->ch_txcount += s; - len_written += s; - } - - /* Update the final tail */ - ch->ch_w_tail = tail & WQUEUEMASK; - - if (len_written >= ch->ch_t_tlevel) - ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - - if (!jsm_tty_write(&ch->uart_port)) - uart_write_wakeup(&ch->uart_port); -} - -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); - - /* Scrub off lower bits. They signify delta's, which I don't care about */ - /* Keep DDCD and DDSR though */ - msignals &= 0xf8; - - if (msignals & UART_MSR_DDCD) - uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_DCD); - if (msignals & UART_MSR_DDSR) - uart_handle_cts_change(&ch->uart_port, msignals & UART_MSR_CTS); - if (msignals & UART_MSR_DCD) - ch->ch_mistat |= UART_MSR_DCD; - else - ch->ch_mistat &= ~UART_MSR_DCD; - - if (msignals & UART_MSR_DSR) - ch->ch_mistat |= UART_MSR_DSR; - else - ch->ch_mistat &= ~UART_MSR_DSR; - - if (msignals & UART_MSR_RI) - ch->ch_mistat |= UART_MSR_RI; - else - ch->ch_mistat &= ~UART_MSR_RI; - - if (msignals & UART_MSR_CTS) - ch->ch_mistat |= UART_MSR_CTS; - 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", - ch->ch_portnum, - !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR), - !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS), - !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS), - !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR), - !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI), - !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD)); -} - -/* Make the UART raise any of the output signals we want up */ -static void neo_assert_modem_signals(struct jsm_channel *ch) -{ - if (!ch) - return; - - writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); -} - -/* - * Flush the WRITE FIFO on the Neo. - * - * NOTE: Channel lock MUST be held before calling this function! - */ -static void neo_flush_uart_write(struct jsm_channel *ch) -{ - u8 tmp = 0; - int i = 0; - - if (!ch) - return; - - writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr); - - for (i = 0; i < 10; i++) { - - /* 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); - udelay(10); - } - else - break; - } - - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); -} - - -/* - * Flush the READ FIFO on the Neo. - * - * NOTE: Channel lock MUST be held before calling this function! - */ -static void neo_flush_uart_read(struct jsm_channel *ch) -{ - u8 tmp = 0; - int i = 0; - - if (!ch) - return; - - writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr); - - for (i = 0; i < 10; i++) { - - /* 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); - udelay(10); - } - else - break; - } -} - -/* - * No locks are assumed to be held when calling this function. - */ -static void neo_clear_break(struct jsm_channel *ch, int force) -{ - unsigned long lock_flags; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - /* Turn break off, and unset some variables */ - if (ch->ch_flags & CH_BREAK_SENDING) { - u8 temp = readb(&ch->ch_neo_uart->lcr); - 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); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); - } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); -} - -/* - * Parse the ISR register. - */ -static inline void neo_parse_isr(struct jsm_board *brd, u32 port) -{ - struct jsm_channel *ch; - u8 isr; - u8 cause; - unsigned long lock_flags; - - if (!brd) - return; - - if (port > brd->maxports) - return; - - ch = brd->channels[port]; - if (!ch) - return; - - /* Here we try to figure out what caused the interrupt to happen */ - while (1) { - - isr = readb(&ch->ch_neo_uart->isr_fcr); - - /* Bail if no pending interrupt */ - if (isr & UART_IIR_NO_INT) - break; - - /* - * Yank off the upper 2 bits, which just show that the FIFO's are enabled. - */ - isr &= ~(UART_17158_IIR_FIFO_ENABLED); - - jsm_printk(INTR, INFO, &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 */ - neo_copy_data_from_uart_to_queue(ch); - - /* Call our tty layer to enforce queue flow control if needed. */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - jsm_check_queue_flow_control(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - } - - if (isr & UART_IIR_THRI) { - /* Transfer data (if any) from Write Queue -> UART. */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - neo_copy_data_from_queue_to_uart(ch); - } - - 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); - - /* - * Since the UART detected either an XON or - * XOFF match, we need to figure out which - * one it was, so we can suspend or resume data flow. - */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - if (cause == UART_17158_XON_DETECT) { - /* Is output stopped right now, if so, resume it */ - 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); - } - 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_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "Port: %d. XOFF detected in incoming data\n", port); - } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - } - - if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) { - /* - * If we get here, this means the hardware is doing auto flow control. - * Check to see whether RTS/DTR or CTS/DSR caused this interrupt. - */ - cause = readb(&ch->ch_neo_uart->mcr); - - /* Which pin is doing auto flow? RTS or DTR? */ - spin_lock_irqsave(&ch->ch_lock, lock_flags); - if ((cause & 0x4) == 0) { - if (cause & UART_MCR_RTS) - ch->ch_mostat |= UART_MCR_RTS; - else - ch->ch_mostat &= ~(UART_MCR_RTS); - } else { - if (cause & UART_MCR_DTR) - ch->ch_mostat |= UART_MCR_DTR; - else - ch->ch_mostat &= ~(UART_MCR_DTR); - } - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - } - - /* Parse any modem signal changes */ - jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, - "MOD_STAT: sending to parse_modem_sigs\n"); - neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); - } -} - -static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) -{ - struct jsm_channel *ch; - int linestatus; - unsigned long lock_flags; - - if (!brd) - return; - - if (port > brd->maxports) - return; - - ch = brd->channels[port]; - if (!ch) - return; - - 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); - - ch->ch_cached_lsr |= linestatus; - - if (ch->ch_cached_lsr & UART_LSR_DR) { - /* Read data from uart -> queue */ - neo_copy_data_from_uart_to_queue(ch); - spin_lock_irqsave(&ch->ch_lock, lock_flags); - jsm_check_queue_flow_control(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - } - - /* - * This is a special flag. It indicates that at least 1 - * RX error (parity, framing, or break) has happened. - * Mark this in our struct, which will tell me that I have - *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, - "%s:%d Port: %d Got an RX error, need to parse LSR\n", - __FILE__, __LINE__, port); - - /* - * The next 3 tests should *NOT* happen, as the above test - * should encapsulate all 3... At least, thats what Exar says. - */ - - 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); - } - - 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); - } - - 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); - } - - if (linestatus & UART_LSR_OE) { - /* - * Rx Oruns. Exar says that an orun will NOT corrupt - * the FIFO. It will just replace the holding register - * with this new data byte. So basically just ignore this. - * 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); - } - - if (linestatus & UART_LSR_THRE) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* Transfer data (if any) from Write Queue -> UART. */ - neo_copy_data_from_queue_to_uart(ch); - } - else if (linestatus & UART_17158_TX_AND_FIFO_CLR) { - spin_lock_irqsave(&ch->ch_lock, lock_flags); - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* Transfer data (if any) from Write Queue -> UART. */ - neo_copy_data_from_queue_to_uart(ch); - } -} - -/* - * neo_param() - * Send any/all changes to the line to the UART. - */ -static void neo_param(struct jsm_channel *ch) -{ - u8 lcr = 0; - u8 uart_lcr, ier; - u32 baud; - int quot; - struct jsm_board *bd; - - bd = ch->ch_bd; - if (!bd) - return; - - /* - * If baud rate is zero, flush queues, and set mval to drop DTR. - */ - if ((ch->ch_c_cflag & (CBAUD)) == 0) { - ch->ch_r_head = ch->ch_r_tail = 0; - ch->ch_e_head = ch->ch_e_tail = 0; - ch->ch_w_head = ch->ch_w_tail = 0; - - neo_flush_uart_write(ch); - neo_flush_uart_read(ch); - - ch->ch_flags |= (CH_BAUD0); - ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR); - neo_assert_modem_signals(ch); - return; - - } else { - int i; - unsigned int cflag; - static struct { - unsigned int rate; - unsigned int cflag; - } baud_rates[] = { - { 921600, B921600 }, - { 460800, B460800 }, - { 230400, B230400 }, - { 115200, B115200 }, - { 57600, B57600 }, - { 38400, B38400 }, - { 19200, B19200 }, - { 9600, B9600 }, - { 4800, B4800 }, - { 2400, B2400 }, - { 1200, B1200 }, - { 600, B600 }, - { 300, B300 }, - { 200, B200 }, - { 150, B150 }, - { 134, B134 }, - { 110, B110 }, - { 75, B75 }, - { 50, B50 }, - }; - - cflag = C_BAUD(ch->uart_port.state->port.tty); - baud = 9600; - for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { - if (baud_rates[i].cflag == cflag) { - baud = baud_rates[i].rate; - break; - } - } - - if (ch->ch_flags & CH_BAUD0) - ch->ch_flags &= ~(CH_BAUD0); - } - - if (ch->ch_c_cflag & PARENB) - lcr |= UART_LCR_PARITY; - - if (!(ch->ch_c_cflag & PARODD)) - lcr |= UART_LCR_EPAR; - - /* - * Not all platforms support mark/space parity, - * so this will hide behind an ifdef. - */ -#ifdef CMSPAR - if (ch->ch_c_cflag & CMSPAR) - lcr |= UART_LCR_SPAR; -#endif - - if (ch->ch_c_cflag & CSTOPB) - lcr |= UART_LCR_STOP; - - switch (ch->ch_c_cflag & CSIZE) { - case CS5: - lcr |= UART_LCR_WLEN5; - break; - case CS6: - lcr |= UART_LCR_WLEN6; - break; - case CS7: - lcr |= UART_LCR_WLEN7; - break; - case CS8: - default: - lcr |= UART_LCR_WLEN8; - break; - } - - ier = readb(&ch->ch_neo_uart->ier); - uart_lcr = readb(&ch->ch_neo_uart->lcr); - - if (baud == 0) - baud = 9600; - - quot = ch->ch_bd->bd_dividend / baud; - - if (quot != 0) { - writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr); - writeb((quot & 0xff), &ch->ch_neo_uart->txrx); - writeb((quot >> 8), &ch->ch_neo_uart->ier); - writeb(lcr, &ch->ch_neo_uart->lcr); - } - - if (uart_lcr != lcr) - writeb(lcr, &ch->ch_neo_uart->lcr); - - if (ch->ch_c_cflag & CREAD) - ier |= (UART_IER_RDI | UART_IER_RLSI); - - ier |= (UART_IER_THRI | UART_IER_MSI); - - writeb(ier, &ch->ch_neo_uart->ier); - - /* Set new start/stop chars */ - neo_set_new_start_stop_chars(ch); - - if (ch->ch_c_cflag & CRTSCTS) - neo_set_cts_flow_control(ch); - else if (ch->ch_c_iflag & IXON) { - /* If start/stop is set to disable, then we should disable flow control */ - if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR)) - neo_set_no_output_flow_control(ch); - else - neo_set_ixon_flow_control(ch); - } - else - neo_set_no_output_flow_control(ch); - - if (ch->ch_c_cflag & CRTSCTS) - neo_set_rts_flow_control(ch); - else if (ch->ch_c_iflag & IXOFF) { - /* If start/stop is set to disable, then we should disable flow control */ - if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR)) - neo_set_no_input_flow_control(ch); - else - neo_set_ixoff_flow_control(ch); - } - else - neo_set_no_input_flow_control(ch); - /* - * Adjust the RX FIFO Trigger level if baud is less than 9600. - * Not exactly elegant, but this is needed because of the Exar chip's - * delay on firing off the RX FIFO interrupt on slower baud rates. - */ - if (baud < 9600) { - writeb(1, &ch->ch_neo_uart->rfifo); - ch->ch_r_tlevel = 1; - } - - neo_assert_modem_signals(ch); - - /* Get current status of the modem signals now */ - neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); - return; -} - -/* - * jsm_neo_intr() - * - * Neo specific interrupt handler. - */ -static irqreturn_t neo_intr(int irq, void *voidbrd) -{ - struct jsm_board *brd = voidbrd; - struct jsm_channel *ch; - int port = 0; - int type = 0; - int current_port; - u32 tmp; - u32 uart_poll; - unsigned long lock_flags; - unsigned long lock_flags2; - int outofloop_count = 0; - - /* Lock out the slow poller from running on this board. */ - spin_lock_irqsave(&brd->bd_intr_lock, lock_flags); - - /* - * Read in "extended" IRQ information from the 32bit Neo register. - * Bits 0-7: What port triggered the interrupt. - * Bits 8-31: Each 3bits indicate what type of interrupt occurred. - */ - 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); - - if (!uart_poll) { - jsm_printk(INTR, INFO, &brd->pci_dev, - "Kernel interrupted to me, but no pending interrupts...\n"); - spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); - return IRQ_NONE; - } - - /* At this point, we have at least SOMETHING to service, dig further... */ - - current_port = 0; - - /* Loop on each port */ - while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){ - - tmp = uart_poll; - outofloop_count++; - - /* Check current port to see if it has interrupt pending */ - if ((tmp & jsm_offset_table[current_port]) != 0) { - port = current_port; - type = tmp >> (8 + (port * 3)); - type &= 0x7; - } else { - current_port++; - continue; - } - - jsm_printk(INTR, INFO, &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, - "Interrupt with no type! port: %d\n", port); - continue; - } - - /* Switch on type of interrupt we have */ - switch (type) { - - case UART_17158_RXRDY_TIMEOUT: - /* - * RXRDY Time-out is cleared by reading data in the - * RX FIFO until it falls below the trigger level. - */ - - /* Verify the port is in range. */ - if (port > brd->nasync) - continue; - - ch = brd->channels[port]; - neo_copy_data_from_uart_to_queue(ch); - - /* Call our tty layer to enforce queue flow control if needed. */ - spin_lock_irqsave(&ch->ch_lock, lock_flags2); - jsm_check_queue_flow_control(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); - - continue; - - case UART_17158_RX_LINE_STATUS: - /* - * RXRDY and RX LINE Status (logic OR of LSR[4:1]) - */ - neo_parse_lsr(brd, port); - continue; - - case UART_17158_TXRDY: - /* - * TXRDY interrupt clears after reading ISR register for the UART channel. - */ - - /* - * Yes, this is odd... - * Why would I check EVERY possibility of type of - * interrupt, when we know its TXRDY??? - * Becuz for some reason, even tho we got triggered for TXRDY, - * it seems to be occassionally wrong. Instead of TX, which - * it should be, I was getting things like RXDY too. Weird. - */ - neo_parse_isr(brd, port); - continue; - - case UART_17158_MSR: - /* - * MSR or flow control was seen. - */ - neo_parse_isr(brd, port); - continue; - - default: - /* - * The UART triggered us with a bogus interrupt type. - * It appears the Exar chip, when REALLY bogged down, will throw - * 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); - continue; - } - } - - spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); - - jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n"); - return IRQ_HANDLED; -} - -/* - * Neo specific way of turning off the receiver. - * Used as a way to enforce queue flow control when in - * hardware flow control mode. - */ -static void neo_disable_receiver(struct jsm_channel *ch) -{ - u8 tmp = readb(&ch->ch_neo_uart->ier); - tmp &= ~(UART_IER_RDI); - writeb(tmp, &ch->ch_neo_uart->ier); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); -} - - -/* - * Neo specific way of turning on the receiver. - * Used as a way to un-enforce queue flow control when in - * hardware flow control mode. - */ -static void neo_enable_receiver(struct jsm_channel *ch) -{ - u8 tmp = readb(&ch->ch_neo_uart->ier); - tmp |= (UART_IER_RDI); - writeb(tmp, &ch->ch_neo_uart->ier); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); -} - -static void neo_send_start_character(struct jsm_channel *ch) -{ - if (!ch) - return; - - if (ch->ch_startc != __DISABLED_CHAR) { - ch->ch_xon_sends++; - writeb(ch->ch_startc, &ch->ch_neo_uart->txrx); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); - } -} - -static void neo_send_stop_character(struct jsm_channel *ch) -{ - if (!ch) - return; - - if (ch->ch_stopc != __DISABLED_CHAR) { - ch->ch_xoff_sends++; - writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); - } -} - -/* - * neo_uart_init - */ -static void neo_uart_init(struct jsm_channel *ch) -{ - writeb(0, &ch->ch_neo_uart->ier); - writeb(0, &ch->ch_neo_uart->efr); - writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr); - - /* Clear out UART and FIFO */ - readb(&ch->ch_neo_uart->txrx); - writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr); - readb(&ch->ch_neo_uart->lsr); - readb(&ch->ch_neo_uart->msr); - - ch->ch_flags |= CH_FIFO_ENABLED; - - /* Assert any signals we want up */ - writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr); -} - -/* - * Make the UART completely turn off. - */ -static void neo_uart_off(struct jsm_channel *ch) -{ - /* Turn off UART enhanced bits */ - writeb(0, &ch->ch_neo_uart->efr); - - /* Stop all interrupts from occurring. */ - writeb(0, &ch->ch_neo_uart->ier); -} - -static u32 neo_get_uart_bytes_left(struct jsm_channel *ch) -{ - u8 left = 0; - u8 lsr = readb(&ch->ch_neo_uart->lsr); - - /* We must cache the LSR as some of the bits get reset once read... */ - ch->ch_cached_lsr |= lsr; - - /* Determine whether the Transmitter is empty or not */ - if (!(lsr & UART_LSR_TEMT)) - left = 1; - else { - ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); - left = 0; - } - - return left; -} - -/* Channel lock MUST be held by the calling function! */ -static void neo_send_break(struct jsm_channel *ch) -{ - /* - * Set the time we should stop sending the break. - * If we are already sending a break, toss away the existing - * time to stop, and use this new value instead. - */ - - /* Tell the UART to start sending the break */ - if (!(ch->ch_flags & CH_BREAK_SENDING)) { - u8 temp = readb(&ch->ch_neo_uart->lcr); - writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr); - ch->ch_flags |= (CH_BREAK_SENDING); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); - } -} - -/* - * neo_send_immediate_char. - * - * Sends a specific character as soon as possible to the UART, - * jumping over any bytes that might be in the write queue. - * - * The channel lock MUST be held by the calling function. - */ -static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c) -{ - if (!ch) - return; - - writeb(c, &ch->ch_neo_uart->txrx); - - /* flush write operation */ - neo_pci_posting_flush(ch->ch_bd); -} - -struct board_ops jsm_neo_ops = { - .intr = neo_intr, - .uart_init = neo_uart_init, - .uart_off = neo_uart_off, - .param = neo_param, - .assert_modem_signals = neo_assert_modem_signals, - .flush_uart_write = neo_flush_uart_write, - .flush_uart_read = neo_flush_uart_read, - .disable_receiver = neo_disable_receiver, - .enable_receiver = neo_enable_receiver, - .send_break = neo_send_break, - .clear_break = neo_clear_break, - .send_start_character = neo_send_start_character, - .send_stop_character = neo_send_stop_character, - .copy_data_from_queue_to_uart = neo_copy_data_from_queue_to_uart, - .get_uart_bytes_left = neo_get_uart_bytes_left, - .send_immediate_char = neo_send_immediate_char -}; diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c deleted file mode 100644 index 7a4a914ecff0..000000000000 --- a/drivers/serial/jsm/jsm_tty.c +++ /dev/null @@ -1,910 +0,0 @@ -/************************************************************************ - * Copyright 2003 Digi International (www.digi.com) - * - * Copyright (C) 2004 IBM Corporation. All rights reserved. - * - * 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, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. - * - * Contact Information: - * Scott H Kilau - * Ananda Venkatarman - * Modifications: - * 01/19/06: changed jsm_input routine to use the dynamically allocated - * tty_buffer changes. Contributors: Scott Kilau and Ananda V. - ***********************************************************************/ -#include -#include -#include -#include /* For udelay */ -#include -#include - -#include "jsm.h" - -static DECLARE_BITMAP(linemap, MAXLINES); - -static void jsm_carrier(struct jsm_channel *ch); - -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"); - - mstat = (ch->ch_mostat | ch->ch_mistat); - - result = 0; - - if (mstat & UART_MCR_DTR) - result |= TIOCM_DTR; - if (mstat & UART_MCR_RTS) - result |= TIOCM_RTS; - if (mstat & UART_MSR_CTS) - result |= TIOCM_CTS; - if (mstat & UART_MSR_DSR) - result |= TIOCM_DSR; - if (mstat & UART_MSR_RI) - result |= TIOCM_RI; - if (mstat & UART_MSR_DCD) - result |= TIOCM_CD; - - jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n"); - return result; -} - -static unsigned int jsm_tty_tx_empty(struct uart_port *port) -{ - return TIOCSER_TEMT; -} - -/* - * Return modem signals to ld. - */ -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"); - - result = jsm_get_mstat(channel); - - if (result < 0) - return -ENXIO; - - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); - - return result; -} - -/* - * jsm_set_modem_info() - * - * Set modem signals, called by ld. - */ -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"); - - if (mctrl & TIOCM_RTS) - channel->ch_mostat |= UART_MCR_RTS; - else - channel->ch_mostat &= ~UART_MCR_RTS; - - if (mctrl & TIOCM_DTR) - channel->ch_mostat |= UART_MCR_DTR; - else - channel->ch_mostat &= ~UART_MCR_DTR; - - channel->ch_bd->bd_ops->assert_modem_signals(channel); - - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); - udelay(10); -} - -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"); - - channel->ch_flags &= ~(CH_STOP); - jsm_tty_write(port); - - jsm_printk(IOCTL, INFO, &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"); - - channel->ch_flags |= (CH_STOP); - - jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); -} - -static void jsm_tty_send_xchar(struct uart_port *port, char ch) -{ - unsigned long lock_flags; - struct jsm_channel *channel = (struct jsm_channel *)port; - struct ktermios *termios; - - spin_lock_irqsave(&port->lock, lock_flags); - termios = port->state->port.tty->termios; - if (ch == termios->c_cc[VSTART]) - channel->ch_bd->bd_ops->send_start_character(channel); - - if (ch == termios->c_cc[VSTOP]) - channel->ch_bd->bd_ops->send_stop_character(channel); - spin_unlock_irqrestore(&port->lock, lock_flags); -} - -static void jsm_tty_stop_rx(struct uart_port *port) -{ - struct jsm_channel *channel = (struct jsm_channel *)port; - - channel->ch_bd->bd_ops->disable_receiver(channel); -} - -static void jsm_tty_enable_ms(struct uart_port *port) -{ - /* Nothing needed */ -} - -static void jsm_tty_break(struct uart_port *port, int break_state) -{ - unsigned long lock_flags; - struct jsm_channel *channel = (struct jsm_channel *)port; - - spin_lock_irqsave(&port->lock, lock_flags); - if (break_state == -1) - channel->ch_bd->bd_ops->send_break(channel); - else - channel->ch_bd->bd_ops->clear_break(channel, 0); - - spin_unlock_irqrestore(&port->lock, lock_flags); -} - -static int jsm_tty_open(struct uart_port *port) -{ - struct jsm_board *brd; - struct jsm_channel *channel = (struct jsm_channel *)port; - struct ktermios *termios; - - /* Get board pointer from our array of majors we have allocated */ - brd = channel->ch_bd; - - /* - * Allocate channel buffers for read/write/error. - * Set flag, so we don't get trounced on. - */ - channel->ch_flags |= (CH_OPENING); - - /* Drop locks, as malloc with GFP_KERNEL can sleep */ - - 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"); - 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"); - return -ENOMEM; - } - } - if (!channel->ch_wqueue) { - channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); - if (!channel->ch_wqueue) { - jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, - "unable to allocate write queue buf"); - return -ENOMEM; - } - } - - channel->ch_flags &= ~(CH_OPENING); - /* - * Initialize if neither terminal is open. - */ - jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, - "jsm_open: initializing channel in open...\n"); - - /* - * Flush input queues. - */ - channel->ch_r_head = channel->ch_r_tail = 0; - channel->ch_e_head = channel->ch_e_tail = 0; - channel->ch_w_head = channel->ch_w_tail = 0; - - brd->bd_ops->flush_uart_write(channel); - brd->bd_ops->flush_uart_read(channel); - - channel->ch_flags = 0; - channel->ch_cached_lsr = 0; - channel->ch_stops_sent = 0; - - termios = port->state->port.tty->termios; - channel->ch_c_cflag = termios->c_cflag; - channel->ch_c_iflag = termios->c_iflag; - channel->ch_c_oflag = termios->c_oflag; - channel->ch_c_lflag = termios->c_lflag; - channel->ch_startc = termios->c_cc[VSTART]; - channel->ch_stopc = termios->c_cc[VSTOP]; - - /* Tell UART to init itself */ - brd->bd_ops->uart_init(channel); - - /* - * Run param in case we changed anything - */ - brd->bd_ops->param(channel); - - jsm_carrier(channel); - - channel->ch_open_count++; - - jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n"); - return 0; -} - -static void jsm_tty_close(struct uart_port *port) -{ - struct jsm_board *bd; - struct ktermios *ts; - struct jsm_channel *channel = (struct jsm_channel *)port; - - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); - - bd = channel->ch_bd; - ts = port->state->port.tty->termios; - - channel->ch_flags &= ~(CH_STOPI); - - channel->ch_open_count--; - - /* - * If we have HUPCL set, lower DTR and RTS - */ - if (channel->ch_c_cflag & HUPCL) { - jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, - "Close. HUPCL set, dropping DTR/RTS\n"); - - /* Drop RTS/DTR */ - channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS); - bd->bd_ops->assert_modem_signals(channel); - } - - /* 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"); -} - -static void jsm_tty_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old_termios) -{ - unsigned long lock_flags; - struct jsm_channel *channel = (struct jsm_channel *)port; - - spin_lock_irqsave(&port->lock, lock_flags); - channel->ch_c_cflag = termios->c_cflag; - channel->ch_c_iflag = termios->c_iflag; - channel->ch_c_oflag = termios->c_oflag; - channel->ch_c_lflag = termios->c_lflag; - channel->ch_startc = termios->c_cc[VSTART]; - channel->ch_stopc = termios->c_cc[VSTOP]; - - channel->ch_bd->bd_ops->param(channel); - jsm_carrier(channel); - spin_unlock_irqrestore(&port->lock, lock_flags); -} - -static const char *jsm_tty_type(struct uart_port *port) -{ - return "jsm"; -} - -static void jsm_tty_release_port(struct uart_port *port) -{ -} - -static int jsm_tty_request_port(struct uart_port *port) -{ - return 0; -} - -static void jsm_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_JSM; -} - -static struct uart_ops jsm_ops = { - .tx_empty = jsm_tty_tx_empty, - .set_mctrl = jsm_tty_set_mctrl, - .get_mctrl = jsm_tty_get_mctrl, - .stop_tx = jsm_tty_stop_tx, - .start_tx = jsm_tty_start_tx, - .send_xchar = jsm_tty_send_xchar, - .stop_rx = jsm_tty_stop_rx, - .enable_ms = jsm_tty_enable_ms, - .break_ctl = jsm_tty_break, - .startup = jsm_tty_open, - .shutdown = jsm_tty_close, - .set_termios = jsm_tty_set_termios, - .type = jsm_tty_type, - .release_port = jsm_tty_release_port, - .request_port = jsm_tty_request_port, - .config_port = jsm_config_port, -}; - -/* - * jsm_tty_init() - * - * 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 i; - void __iomem *vaddr; - struct jsm_channel *ch; - - if (!brd) - return -ENXIO; - - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); - - /* - * Initialize board structure elements. - */ - - brd->nasync = brd->maxports; - - /* - * Allocate channel memory that might not have been allocated - * when the driver was first loaded. - */ - for (i = 0; i < brd->nasync; i++) { - if (!brd->channels[i]) { - - /* - * Okay to malloc with GFP_KERNEL, we are not at - * interrupt context, and there are no locks held. - */ - brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); - if (!brd->channels[i]) { - jsm_printk(CORE, ERR, &brd->pci_dev, - "%s:%d Unable to allocate memory for channel struct\n", - __FILE__, __LINE__); - } - } - } - - ch = brd->channels[0]; - vaddr = brd->re_map_membase; - - /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { - - if (!brd->channels[i]) - continue; - - spin_lock_init(&ch->ch_lock); - - if (brd->bd_uart_offset == 0x200) - ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i); - - ch->ch_bd = brd; - ch->ch_portnum = i; - - /* .25 second delay */ - ch->ch_close_delay = 250; - - init_waitqueue_head(&ch->ch_flags_wait); - } - - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); - return 0; -} - -int jsm_uart_port_init(struct jsm_board *brd) -{ - int i, rc; - unsigned int line; - struct jsm_channel *ch; - - if (!brd) - return -ENXIO; - - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); - - /* - * Initialize board structure elements. - */ - - brd->nasync = brd->maxports; - - /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { - - if (!brd->channels[i]) - continue; - - brd->channels[i]->uart_port.irq = brd->irq; - brd->channels[i]->uart_port.uartclk = 14745600; - brd->channels[i]->uart_port.type = PORT_JSM; - brd->channels[i]->uart_port.iotype = UPIO_MEM; - brd->channels[i]->uart_port.membase = brd->re_map_membase; - brd->channels[i]->uart_port.fifosize = 16; - brd->channels[i]->uart_port.ops = &jsm_ops; - line = find_first_zero_bit(linemap, MAXLINES); - if (line >= MAXLINES) { - printk(KERN_INFO "jsm: linemap is full, added device failed\n"); - continue; - } else - set_bit(line, linemap); - brd->channels[i]->uart_port.line = line; - rc = uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port); - if (rc){ - printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i); - return rc; - } - else - printk(KERN_INFO "jsm: Port %d added\n", i); - } - - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); - return 0; -} - -int jsm_remove_uart_port(struct jsm_board *brd) -{ - int i; - struct jsm_channel *ch; - - if (!brd) - return -ENXIO; - - jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); - - /* - * Initialize board structure elements. - */ - - brd->nasync = brd->maxports; - - /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++) { - - if (!brd->channels[i]) - continue; - - ch = brd->channels[i]; - - clear_bit(ch->uart_port.line, linemap); - uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); - } - - jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); - return 0; -} - -void jsm_input(struct jsm_channel *ch) -{ - struct jsm_board *bd; - struct tty_struct *tp; - u32 rmask; - u16 head; - u16 tail; - int data_len; - unsigned long lock_flags; - int len = 0; - int n = 0; - int s = 0; - int i = 0; - - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); - - if (!ch) - return; - - tp = ch->uart_port.state->port.tty; - - bd = ch->ch_bd; - if(!bd) - return; - - spin_lock_irqsave(&ch->ch_lock, lock_flags); - - /* - *Figure the number of characters in the buffer. - *Exit immediately if none. - */ - - rmask = RQUEUEMASK; - - head = ch->ch_r_head & rmask; - tail = ch->ch_r_tail & rmask; - - data_len = (head - tail) & rmask; - if (data_len == 0) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - return; - } - - jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); - - /* - *If the device is not open, or CREAD is off, flush - *input data and return immediately. - */ - 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); - ch->ch_r_head = tail; - - /* Force queue flow control to be released, if needed */ - jsm_check_queue_flow_control(ch); - - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - return; - } - - /* - * If we are throttled, simply don't read any data. - */ - if (ch->ch_flags & CH_STOPI) { - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - jsm_printk(READ, INFO, &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"); - - 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"); - return; - } - - len = tty_buffer_request_room(tp, data_len); - n = len; - - /* - * n now contains the most amount of data we can copy, - * bounded either by the flip buffer size or the amount - * of data the card actually has pending... - */ - while (n) { - s = ((head >= tail) ? head : RQUEUESIZE) - tail; - s = min(s, n); - - if (s <= 0) - break; - - /* - * If conditions are such that ld needs to see all - * UART errors, we will have to walk each character - * and error byte and send them to the buffer one at - * a time. - */ - - if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { - for (i = 0; i < s; i++) { - /* - * Give the Linux ld the flags in the - * format it likes. - */ - if (*(ch->ch_equeue +tail +i) & UART_LSR_BI) - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_BREAK); - else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE) - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY); - else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE) - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME); - else - tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL); - } - } else { - tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ; - } - tail += s; - n -= s; - /* Flip queue if needed */ - tail &= rmask; - } - - ch->ch_r_tail = tail & rmask; - ch->ch_e_tail = tail & rmask; - jsm_check_queue_flow_control(ch); - spin_unlock_irqrestore(&ch->ch_lock, lock_flags); - - /* 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"); -} - -static void jsm_carrier(struct jsm_channel *ch) -{ - struct jsm_board *bd; - - int virt_carrier = 0; - int phys_carrier = 0; - - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n"); - if (!ch) - return; - - bd = ch->ch_bd; - - if (!bd) - 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); - 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); - - /* - * Test for a VIRTUAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { - - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "carrier: virt DCD rose\n"); - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL carrier transition to HIGH. - */ - if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { - - /* - * When carrier rises, wake any threads waiting - * for carrier in the open routine. - */ - - jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, - "carrier: physical DCD rose\n"); - - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Test for a PHYSICAL transition to low, so long as we aren't - * currently ignoring physical transitions (which is what "virtual - * carrier" indicates). - * - * The transition of the virtual carrier to low really doesn't - * matter... it really only means "ignore carrier state", not - * "make pretend that carrier is there". - */ - if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) - && (phys_carrier == 0)) { - /* - * When carrier drops: - * - * Drop carrier on all open units. - * - * Flush queues, waking up any task waiting in the - * line discipline. - * - * Send a hangup to the control terminal. - * - * Enable all select calls. - */ - if (waitqueue_active(&(ch->ch_flags_wait))) - wake_up_interruptible(&ch->ch_flags_wait); - } - - /* - * Make sure that our cached values reflect the current reality. - */ - if (virt_carrier == 1) - ch->ch_flags |= CH_FCAR; - else - ch->ch_flags &= ~CH_FCAR; - - if (phys_carrier == 1) - ch->ch_flags |= CH_CD; - else - ch->ch_flags &= ~CH_CD; -} - - -void jsm_check_queue_flow_control(struct jsm_channel *ch) -{ - struct board_ops *bd_ops = ch->ch_bd->bd_ops; - int qleft; - - /* Store how much space we have left in the queue */ - if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0) - qleft += RQUEUEMASK + 1; - - /* - * Check to see if we should enforce flow control on our queue because - * the ld (or user) isn't reading data out of our queue fast enuf. - * - * NOTE: This is done based on what the current flow control of the - * port is set for. - * - * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt. - * This will cause the UART's FIFO to back up, and force - * the RTS signal to be dropped. - * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to - * the other side, in hopes it will stop sending data to us. - * 3) NONE - Nothing we can do. We will simply drop any extra data - * that gets sent into us when the queue fills up. - */ - if (qleft < 256) { - /* HWFLOW */ - if (ch->ch_c_cflag & CRTSCTS) { - 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", - qleft); - } - } - /* SWFLOW */ - else if (ch->ch_c_iflag & IXOFF) { - 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); - } - } - } - - /* - * Check to see if we should unenforce flow control because - * ld (or user) finally read enuf data out of our queue. - * - * NOTE: This is done based on what the current flow control of the - * port is set for. - * - * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt. - * This will cause the UART's FIFO to raise RTS back up, - * which will allow the other side to start sending data again. - * 2) SWFLOW (IXOFF) - Send a start character to - * the other side, so it will start sending data to us again. - * 3) NONE - Do nothing. Since we didn't do anything to turn off the - * other side, we don't need to do anything now. - */ - if (qleft > (RQUEUESIZE / 2)) { - /* HWFLOW */ - if (ch->ch_c_cflag & CRTSCTS) { - 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", - qleft); - } - } - /* SWFLOW */ - 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_tty_write() - * - * Take data from the user or kernel and send it out to the FEP. - * In here exists all the Transparent Print magic as well. - */ -int jsm_tty_write(struct uart_port *port) -{ - int bufcount; - int data_count = 0,data_count1 =0; - u16 head; - u16 tail; - u16 tmask; - u32 remain; - int temp_tail = port->state->xmit.tail; - struct jsm_channel *channel = (struct jsm_channel *)port; - - tmask = WQUEUEMASK; - head = (channel->ch_w_head) & tmask; - tail = (channel->ch_w_tail) & tmask; - - if ((bufcount = tail - head - 1) < 0) - bufcount += WQUEUESIZE; - - bufcount = min(bufcount, 56); - remain = WQUEUESIZE - head; - - data_count = 0; - if (bufcount >= remain) { - bufcount -= remain; - while ((port->state->xmit.head != temp_tail) && - (data_count < remain)) { - channel->ch_wqueue[head++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - data_count++; - } - if (data_count == remain) head = 0; - } - - data_count1 = 0; - if (bufcount > 0) { - remain = bufcount; - while ((port->state->xmit.head != temp_tail) && - (data_count1 < remain)) { - channel->ch_wqueue[head++] = - port->state->xmit.buf[temp_tail]; - - temp_tail++; - temp_tail &= (UART_XMIT_SIZE - 1); - data_count1++; - - } - } - - port->state->xmit.tail = temp_tail; - - data_count += data_count1; - if (data_count) { - head &= tmask; - channel->ch_w_head = head; - } - - if (data_count) { - channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); - } - - return data_count; -} diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c deleted file mode 100644 index 25a8bc565f40..000000000000 --- a/drivers/serial/kgdboc.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Based on the same principle as kgdboe using the NETPOLL api, this - * driver uses a console polling api to implement a gdb serial inteface - * which is multiplexed on a console port. - * - * Maintainer: Jason Wessel - * - * 2007-2008 (c) Jason Wessel - Wind River Systems, Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_CONFIG_LEN 40 - -static struct kgdb_io kgdboc_io_ops; - -/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */ -static int configured = -1; - -static char config[MAX_CONFIG_LEN]; -static struct kparam_string kps = { - .string = config, - .maxlen = MAX_CONFIG_LEN, -}; - -static int kgdboc_use_kms; /* 1 if we use kernel mode switching */ -static struct tty_driver *kgdb_tty_driver; -static int kgdb_tty_line; - -#ifdef CONFIG_KDB_KEYBOARD -static int kgdboc_reset_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) -{ - input_reset_device(dev); - - /* Retrun an error - we do not want to bind, just to reset */ - return -ENODEV; -} - -static void kgdboc_reset_disconnect(struct input_handle *handle) -{ - /* We do not expect anyone to actually bind to us */ - BUG(); -} - -static const struct input_device_id kgdboc_reset_ids[] = { - { - .flags = INPUT_DEVICE_ID_MATCH_EVBIT, - .evbit = { BIT_MASK(EV_KEY) }, - }, - { } -}; - -static struct input_handler kgdboc_reset_handler = { - .connect = kgdboc_reset_connect, - .disconnect = kgdboc_reset_disconnect, - .name = "kgdboc_reset", - .id_table = kgdboc_reset_ids, -}; - -static DEFINE_MUTEX(kgdboc_reset_mutex); - -static void kgdboc_restore_input_helper(struct work_struct *dummy) -{ - /* - * We need to take a mutex to prevent several instances of - * this work running on different CPUs so they don't try - * to register again already registered handler. - */ - mutex_lock(&kgdboc_reset_mutex); - - if (input_register_handler(&kgdboc_reset_handler) == 0) - input_unregister_handler(&kgdboc_reset_handler); - - mutex_unlock(&kgdboc_reset_mutex); -} - -static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); - -static void kgdboc_restore_input(void) -{ - if (likely(system_state == SYSTEM_RUNNING)) - schedule_work(&kgdboc_restore_input_work); -} - -static int kgdboc_register_kbd(char **cptr) -{ - if (strncmp(*cptr, "kbd", 3) == 0) { - if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { - kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; - kdb_poll_idx++; - if (cptr[0][3] == ',') - *cptr += 4; - else - return 1; - } - } - return 0; -} - -static void kgdboc_unregister_kbd(void) -{ - int i; - - for (i = 0; i < kdb_poll_idx; i++) { - if (kdb_poll_funcs[i] == kdb_get_kbd_char) { - kdb_poll_idx--; - kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; - kdb_poll_funcs[kdb_poll_idx] = NULL; - i--; - } - } - flush_work_sync(&kgdboc_restore_input_work); -} -#else /* ! CONFIG_KDB_KEYBOARD */ -#define kgdboc_register_kbd(x) 0 -#define kgdboc_unregister_kbd() -#define kgdboc_restore_input() -#endif /* ! CONFIG_KDB_KEYBOARD */ - -static int kgdboc_option_setup(char *opt) -{ - if (strlen(opt) > MAX_CONFIG_LEN) { - printk(KERN_ERR "kgdboc: config string too long\n"); - return -ENOSPC; - } - strcpy(config, opt); - - return 0; -} - -__setup("kgdboc=", kgdboc_option_setup); - -static void cleanup_kgdboc(void) -{ - kgdboc_unregister_kbd(); - if (configured == 1) - kgdb_unregister_io_module(&kgdboc_io_ops); -} - -static int configure_kgdboc(void) -{ - struct tty_driver *p; - int tty_line = 0; - int err; - char *cptr = config; - struct console *cons; - - err = kgdboc_option_setup(config); - if (err || !strlen(config) || isspace(config[0])) - goto noconfig; - - err = -ENODEV; - kgdboc_io_ops.is_console = 0; - kgdb_tty_driver = NULL; - - kgdboc_use_kms = 0; - if (strncmp(cptr, "kms,", 4) == 0) { - cptr += 4; - kgdboc_use_kms = 1; - } - - if (kgdboc_register_kbd(&cptr)) - goto do_register; - - p = tty_find_polling_driver(cptr, &tty_line); - if (!p) - goto noconfig; - - cons = console_drivers; - while (cons) { - int idx; - if (cons->device && cons->device(cons, &idx) == p && - idx == tty_line) { - kgdboc_io_ops.is_console = 1; - break; - } - cons = cons->next; - } - - kgdb_tty_driver = p; - kgdb_tty_line = tty_line; - -do_register: - err = kgdb_register_io_module(&kgdboc_io_ops); - if (err) - goto noconfig; - - configured = 1; - - return 0; - -noconfig: - config[0] = 0; - configured = 0; - cleanup_kgdboc(); - - return err; -} - -static int __init init_kgdboc(void) -{ - /* Already configured? */ - if (configured == 1) - return 0; - - return configure_kgdboc(); -} - -static int kgdboc_get_char(void) -{ - if (!kgdb_tty_driver) - return -1; - return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, - kgdb_tty_line); -} - -static void kgdboc_put_char(u8 chr) -{ - if (!kgdb_tty_driver) - return; - kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, - kgdb_tty_line, chr); -} - -static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) -{ - int len = strlen(kmessage); - - if (len >= MAX_CONFIG_LEN) { - printk(KERN_ERR "kgdboc: config string too long\n"); - return -ENOSPC; - } - - /* Only copy in the string if the init function has not run yet */ - if (configured < 0) { - strcpy(config, kmessage); - return 0; - } - - if (kgdb_connected) { - printk(KERN_ERR - "kgdboc: Cannot reconfigure while KGDB is connected.\n"); - - return -EBUSY; - } - - strcpy(config, kmessage); - /* Chop out \n char as a result of echo */ - if (config[len - 1] == '\n') - config[len - 1] = '\0'; - - if (configured == 1) - cleanup_kgdboc(); - - /* Go and configure with the new params. */ - return configure_kgdboc(); -} - -static int dbg_restore_graphics; - -static void kgdboc_pre_exp_handler(void) -{ - if (!dbg_restore_graphics && kgdboc_use_kms) { - dbg_restore_graphics = 1; - con_debug_enter(vc_cons[fg_console].d); - } - /* Increment the module count when the debugger is active */ - if (!kgdb_connected) - try_module_get(THIS_MODULE); -} - -static void kgdboc_post_exp_handler(void) -{ - /* decrement the module count when the debugger detaches */ - if (!kgdb_connected) - module_put(THIS_MODULE); - if (kgdboc_use_kms && dbg_restore_graphics) { - dbg_restore_graphics = 0; - con_debug_leave(); - } - kgdboc_restore_input(); -} - -static struct kgdb_io kgdboc_io_ops = { - .name = "kgdboc", - .read_char = kgdboc_get_char, - .write_char = kgdboc_put_char, - .pre_exception = kgdboc_pre_exp_handler, - .post_exception = kgdboc_post_exp_handler, -}; - -#ifdef CONFIG_KGDB_SERIAL_CONSOLE -/* This is only available if kgdboc is a built in for early debugging */ -static int __init kgdboc_early_init(char *opt) -{ - /* save the first character of the config string because the - * init routine can destroy it. - */ - char save_ch; - - kgdboc_option_setup(opt); - save_ch = config[0]; - init_kgdboc(); - config[0] = save_ch; - return 0; -} - -early_param("ekgdboc", kgdboc_early_init); -#endif /* CONFIG_KGDB_SERIAL_CONSOLE */ - -module_init(init_kgdboc); -module_exit(cleanup_kgdboc); -module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); -MODULE_PARM_DESC(kgdboc, "[,baud]"); -MODULE_DESCRIPTION("KGDB Console TTY Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/m32r_sio.c b/drivers/serial/m32r_sio.c deleted file mode 100644 index bea5c215460c..000000000000 --- a/drivers/serial/m32r_sio.c +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * m32r_sio.c - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.c. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata - * - * 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. - */ - -/* - * A note about mapbase / membase - * - * mapbase is the physical address of the IO port. Currently, we don't - * support this very well, and it may well be dropped from this driver - * in future. As such, mapbase should be NULL. - * - * membase is an 'ioremapped' cookie. This is compatible with the old - * serial.c driver, and is currently the preferred form. - */ - -#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define PORT_M32R_BASE PORT_M32R_SIO -#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) -#define BAUD_RATE 115200 - -#include -#include "m32r_sio.h" -#include "m32r_sio_reg.h" - -/* - * Debugging. - */ -#if 0 -#define DEBUG_AUTOCONF(fmt...) printk(fmt) -#else -#define DEBUG_AUTOCONF(fmt...) do { } while (0) -#endif - -#if 0 -#define DEBUG_INTR(fmt...) printk(fmt) -#else -#define DEBUG_INTR(fmt...) do { } while (0) -#endif - -#define PASS_LIMIT 256 - -/* - * We default to IRQ0 for the "no irq" hack. Some - * machine types want others as well - they're free - * to redefine this in their header file. - */ -#define is_real_interrupt(irq) ((irq) != 0) - -#define BASE_BAUD 115200 - -/* Standard COM flags */ -#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) - -/* - * SERIAL_PORT_DFNS tells us about built-in ports that have no - * standard enumeration mechanism. Platforms that can find all - * serial ports via mechanisms like ACPI or PCI need not supply it. - */ -#if defined(CONFIG_PLAT_USRV) - -#define SERIAL_PORT_DFNS \ - /* UART CLK PORT IRQ FLAGS */ \ - { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ - -#else /* !CONFIG_PLAT_USRV */ - -#if defined(CONFIG_SERIAL_M32R_PLDSIO) -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ - STD_COM_FLAGS }, /* ttyS0 */ -#else -#define SERIAL_PORT_DFNS \ - { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ - STD_COM_FLAGS }, /* ttyS0 */ -#endif - -#endif /* !CONFIG_PLAT_USRV */ - -static struct old_serial_port old_serial_port[] = { - SERIAL_PORT_DFNS -}; - -#define UART_NR ARRAY_SIZE(old_serial_port) - -struct uart_sio_port { - struct uart_port port; - struct timer_list timer; /* "no irq" timer */ - struct list_head list; /* ports on this IRQ */ - unsigned short rev; - unsigned char acr; - unsigned char ier; - unsigned char lcr; - unsigned char mcr_mask; /* mask of user bits */ - unsigned char mcr_force; /* mask of forced bits */ - unsigned char lsr_break_flag; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); -}; - -struct irq_info { - spinlock_t lock; - struct list_head *head; -}; - -static struct irq_info irq_lists[NR_IRQS]; - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial_uart_config uart_config[] = { - [PORT_UNKNOWN] = { - .name = "unknown", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, - [PORT_INDEX(PORT_M32R_SIO)] = { - .name = "M32RSIO", - .dfl_xmit_fifo_size = 1, - .flags = 0, - }, -}; - -#ifdef CONFIG_SERIAL_M32R_PLDSIO - -#define __sio_in(x) inw((unsigned long)(x)) -#define __sio_out(v,x) outw((v),(unsigned long)(x)) - -static inline void sio_set_baud_rate(unsigned long baud) -{ - unsigned short sbaud; - sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1; - __sio_out(sbaud, PLD_ESIO0BAUR); -} - -static void sio_reset(void) -{ - unsigned short tmp; - - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0CR); - sio_set_baud_rate(BAUD_RATE); - __sio_out(0x0300, PLD_ESIO0CR); - __sio_out(0x0003, PLD_ESIO0CR); -} - -static void sio_init(void) -{ - unsigned short tmp; - - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0RXB); - tmp = __sio_in(PLD_ESIO0CR); - __sio_out(0x0300, PLD_ESIO0CR); - __sio_out(0x0003, PLD_ESIO0CR); -} - -static void sio_error(int *status) -{ - printk("SIO0 error[%04x]\n", *status); - do { - sio_init(); - } while ((*status = __sio_in(PLD_ESIO0CR)) != 3); -} - -#else /* not CONFIG_SERIAL_M32R_PLDSIO */ - -#define __sio_in(x) inl(x) -#define __sio_out(v,x) outl((v),(x)) - -static inline void sio_set_baud_rate(unsigned long baud) -{ - unsigned long i, j; - - i = boot_cpu_data.bus_clock / (baud * 16); - j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud; - i -= 1; - j = (j + 1) >> 1; - - __sio_out(i, M32R_SIO0_BAUR_PORTL); - __sio_out(j, M32R_SIO0_RBAUR_PORTL); -} - -static void sio_reset(void) -{ - __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */ - __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */ - __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */ - sio_set_baud_rate(BAUD_RATE); - __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL); - __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */ -} - -static void sio_init(void) -{ - unsigned int tmp; - - tmp = __sio_in(M32R_SIO0_RXB_PORTL); - tmp = __sio_in(M32R_SIO0_RXB_PORTL); - tmp = __sio_in(M32R_SIO0_STS_PORTL); - __sio_out(0x00000003, M32R_SIO0_CR_PORTL); -} - -static void sio_error(int *status) -{ - printk("SIO0 error[%04x]\n", *status); - do { - sio_init(); - } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3); -} - -#endif /* CONFIG_SERIAL_M32R_PLDSIO */ - -static unsigned int sio_in(struct uart_sio_port *up, int offset) -{ - return __sio_in(up->port.iobase + offset); -} - -static void sio_out(struct uart_sio_port *up, int offset, int value) -{ - __sio_out(value, up->port.iobase + offset); -} - -static unsigned int serial_in(struct uart_sio_port *up, int offset) -{ - if (!offset) - return 0; - - return __sio_in(offset); -} - -static void serial_out(struct uart_sio_port *up, int offset, int value) -{ - if (!offset) - return; - - __sio_out(value, offset); -} - -static void m32r_sio_stop_tx(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void m32r_sio_start_tx(struct uart_port *port) -{ -#ifdef CONFIG_SERIAL_M32R_PLDSIO - struct uart_sio_port *up = (struct uart_sio_port *)port; - struct circ_buf *xmit = &up->port.state->xmit; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - } - while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); -#else - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -#endif -} - -static void m32r_sio_stop_rx(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void m32r_sio_enable_ms(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void receive_chars(struct uart_sio_port *up, int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch; - unsigned char flag; - int max_count = 256; - - do { - ch = sio_in(up, SIORXB); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ingored. - */ - *status &= up->port.read_status_mask; - - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } - - if (*status & UART_LSR_BI) { - DEBUG_INTR("handling break...."); - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); - - if (*status & UART_LSR_OE) { - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - ignore_char: - *status = serial_in(up, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); -} - -static void transmit_chars(struct uart_sio_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { -#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */ - serial_out(up, UART_TX, up->port.x_char); -#endif - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - m32r_sio_stop_tx(&up->port); - return; - } - - count = up->port.fifosize; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - while (!(serial_in(up, UART_LSR) & UART_LSR_THRE)); - - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - DEBUG_INTR("THRE..."); - - if (uart_circ_empty(xmit)) - m32r_sio_stop_tx(&up->port); -} - -/* - * This handles the interrupt from one port. - */ -static inline void m32r_sio_handle_port(struct uart_sio_port *up, - unsigned int status) -{ - DEBUG_INTR("status = %x...", status); - - if (status & 0x04) - receive_chars(up, &status); - if (status & 0x01) - transmit_chars(up); -} - -/* - * This is the serial driver's interrupt routine. - * - * Arjan thinks the old way was overly complex, so it got simplified. - * Alan disagrees, saying that need the complexity to handle the weird - * nature of ISA shared interrupts. (This is a special exception.) - * - * In order to handle ISA shared interrupts properly, we need to check - * that all ports have been serviced, and therefore the ISA interrupt - * line has been de-asserted. - * - * This means we need to loop through all ports. checking that they - * don't have an interrupt pending. - */ -static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) -{ - struct irq_info *i = dev_id; - struct list_head *l, *end = NULL; - int pass_counter = 0; - - DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); - -#ifdef CONFIG_SERIAL_M32R_PLDSIO -// if (irq == PLD_IRQ_SIO0_SND) -// irq = PLD_IRQ_SIO0_RCV; -#else - if (irq == M32R_IRQ_SIO0_S) - irq = M32R_IRQ_SIO0_R; -#endif - - spin_lock(&i->lock); - - l = i->head; - do { - struct uart_sio_port *up; - unsigned int sts; - - up = list_entry(l, struct uart_sio_port, list); - - sts = sio_in(up, SIOSTS); - if (sts & 0x5) { - spin_lock(&up->port.lock); - m32r_sio_handle_port(up, sts); - spin_unlock(&up->port.lock); - - end = NULL; - } else if (end == NULL) - end = l; - - l = l->next; - - if (l == i->head && pass_counter++ > PASS_LIMIT) { - if (sts & 0xe0) - sio_error(&sts); - break; - } - } while (l != end); - - spin_unlock(&i->lock); - - DEBUG_INTR("end.\n"); - - return IRQ_HANDLED; -} - -/* - * To support ISA shared interrupts, we need to have one interrupt - * handler that ensures that the IRQ line has been deasserted - * before returning. Failing to do this will result in the IRQ - * line being stuck active, and, since ISA irqs are edge triggered, - * no more IRQs will be seen. - */ -static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up) -{ - spin_lock_irq(&i->lock); - - if (!list_empty(i->head)) { - if (i->head == &up->list) - i->head = i->head->next; - list_del(&up->list); - } else { - BUG_ON(i->head != &up->list); - i->head = NULL; - } - - spin_unlock_irq(&i->lock); -} - -static int serial_link_irq_chain(struct uart_sio_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - int ret, irq_flags = 0; - - spin_lock_irq(&i->lock); - - if (i->head) { - list_add(&up->list, i->head); - spin_unlock_irq(&i->lock); - - ret = 0; - } else { - INIT_LIST_HEAD(&up->list); - i->head = &up->list; - spin_unlock_irq(&i->lock); - - ret = request_irq(up->port.irq, m32r_sio_interrupt, - irq_flags, "SIO0-RX", i); - ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt, - irq_flags, "SIO0-TX", i); - if (ret < 0) - serial_do_unlink(i, up); - } - - return ret; -} - -static void serial_unlink_irq_chain(struct uart_sio_port *up) -{ - struct irq_info *i = irq_lists + up->port.irq; - - BUG_ON(i->head == NULL); - - if (list_empty(i->head)) { - free_irq(up->port.irq, i); - free_irq(up->port.irq + 1, i); - } - - serial_do_unlink(i, up); -} - -/* - * This function is used to handle ports that do not have an interrupt. - */ -static void m32r_sio_timeout(unsigned long data) -{ - struct uart_sio_port *up = (struct uart_sio_port *)data; - unsigned int timeout; - unsigned int sts; - - sts = sio_in(up, SIOSTS); - if (sts & 0x5) { - spin_lock(&up->port.lock); - m32r_sio_handle_port(up, sts); - spin_unlock(&up->port.lock); - } - - timeout = up->port.timeout; - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - mod_timer(&up->timer, jiffies + timeout); -} - -static unsigned int m32r_sio_tx_empty(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int m32r_sio_get_mctrl(struct uart_port *port) -{ - return 0; -} - -static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - -} - -static void m32r_sio_break_ctl(struct uart_port *port, int break_state) -{ - -} - -static int m32r_sio_startup(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - int retval; - - sio_init(); - - /* - * If the "interrupt" for this port doesn't correspond with any - * hardware interrupt, we use a timer-based system. The original - * driver used to do this with IRQ0. - */ - if (!is_real_interrupt(up->port.irq)) { - unsigned int timeout = up->port.timeout; - - timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - - up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + timeout); - } else { - retval = serial_link_irq_chain(up); - if (retval) - return retval; - } - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - * - M32R_SIO: 0x0c - * - M32R_PLDSIO: 0x04 - */ - up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - sio_out(up, SIOTRCR, up->ier); - - /* - * And clear the interrupt registers again for luck. - */ - sio_reset(); - - return 0; -} - -static void m32r_sio_shutdown(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - sio_out(up, SIOTRCR, 0); - - /* - * Disable break condition and FIFOs - */ - - sio_init(); - - if (!is_real_interrupt(up->port.irq)) - del_timer_sync(&up->timer); - else - serial_unlink_irq_chain(up); -} - -static unsigned int m32r_sio_get_divisor(struct uart_port *port, - unsigned int baud) -{ - return uart_get_divisor(port, baud); -} - -static void m32r_sio_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned char cval = 0; - unsigned long flags; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - 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 - - /* - * Ask the core to calculate the divisor for us. - */ -#ifdef CONFIG_SERIAL_M32R_PLDSIO - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); -#else - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); -#endif - quot = m32r_sio_get_divisor(port, baud); - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - sio_set_baud_rate(baud); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - up->lcr = cval; /* Save LCR */ - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void m32r_sio_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - if (up->pm) - up->pm(port, state, oldstate); -} - -/* - * Resource handling. This is complicated by the fact that resources - * depend on the port type. Maybe we should be claiming the standard - * 8250 ports, and then trying to get other resources as necessary? - */ -static int -m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) -{ - unsigned int size = 8 << up->port.regshift; -#ifndef CONFIG_SERIAL_M32R_PLDSIO - unsigned long start; -#endif - int ret = 0; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { -#ifdef CONFIG_SERIAL_M32R_PLDSIO - *res = request_mem_region(up->port.mapbase, size, "serial"); -#else - start = up->port.mapbase; - *res = request_mem_region(start, size, "serial"); -#endif - if (!*res) - ret = -EBUSY; - } - break; - - case UPIO_PORT: - *res = request_region(up->port.iobase, size, "serial"); - if (!*res) - ret = -EBUSY; - break; - } - return ret; -} - -static void m32r_sio_release_port(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - unsigned long start, offset = 0, size = 0; - - size <<= up->port.regshift; - - switch (up->port.iotype) { - case UPIO_MEM: - if (up->port.mapbase) { - /* - * Unmap the area. - */ - iounmap(up->port.membase); - up->port.membase = NULL; - - start = up->port.mapbase; - - if (size) - release_mem_region(start + offset, size); - release_mem_region(start, 8 << up->port.regshift); - } - break; - - case UPIO_PORT: - start = up->port.iobase; - - if (size) - release_region(start + offset, size); - release_region(start + offset, 8 << up->port.regshift); - break; - - default: - break; - } -} - -static int m32r_sio_request_port(struct uart_port *port) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - struct resource *res = NULL; - int ret = 0; - - ret = m32r_sio_request_std_resource(up, &res); - - /* - * If we have a mapbase, then request that as well. - */ - if (ret == 0 && up->port.flags & UPF_IOREMAP) { - int size = res->end - res->start + 1; - - up->port.membase = ioremap(up->port.mapbase, size); - if (!up->port.membase) - ret = -ENOMEM; - } - - if (ret < 0) { - if (res) - release_resource(res); - } - - return ret; -} - -static void m32r_sio_config_port(struct uart_port *port, int flags) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - spin_lock_irqsave(&up->port.lock, flags); - - up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int -m32r_sio_verify_port(struct 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 >= ARRAY_SIZE(uart_config)) - return -EINVAL; - return 0; -} - -static const char * -m32r_sio_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops m32r_sio_pops = { - .tx_empty = m32r_sio_tx_empty, - .set_mctrl = m32r_sio_set_mctrl, - .get_mctrl = m32r_sio_get_mctrl, - .stop_tx = m32r_sio_stop_tx, - .start_tx = m32r_sio_start_tx, - .stop_rx = m32r_sio_stop_rx, - .enable_ms = m32r_sio_enable_ms, - .break_ctl = m32r_sio_break_ctl, - .startup = m32r_sio_startup, - .shutdown = m32r_sio_shutdown, - .set_termios = m32r_sio_set_termios, - .pm = m32r_sio_pm, - .type = m32r_sio_type, - .release_port = m32r_sio_release_port, - .request_port = m32r_sio_request_port, - .config_port = m32r_sio_config_port, - .verify_port = m32r_sio_verify_port, -}; - -static struct uart_sio_port m32r_sio_ports[UART_NR]; - -static void __init m32r_sio_init_ports(void) -{ - struct uart_sio_port *up; - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); - i++, up++) { - up->port.iobase = old_serial_port[i].port; - up->port.irq = irq_canonicalize(old_serial_port[i].irq); - up->port.uartclk = old_serial_port[i].baud_base * 16; - up->port.flags = old_serial_port[i].flags; - up->port.membase = old_serial_port[i].iomem_base; - up->port.iotype = old_serial_port[i].io_type; - up->port.regshift = old_serial_port[i].iomem_reg_shift; - up->port.ops = &m32r_sio_pops; - } -} - -static void __init m32r_sio_register_ports(struct uart_driver *drv) -{ - int i; - - m32r_sio_init_ports(); - - for (i = 0; i < UART_NR; i++) { - struct uart_sio_port *up = &m32r_sio_ports[i]; - - up->port.line = i; - up->port.ops = &m32r_sio_pops; - init_timer(&up->timer); - up->timer.function = m32r_sio_timeout; - - /* - * ALPHA_KLUDGE_MCR needs to be killed. - */ - up->mcr_mask = ~ALPHA_KLUDGE_MCR; - up->mcr_force = ALPHA_KLUDGE_MCR; - - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_sio_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = sio_in(up, SIOSTS); - - if (--tmout == 0) - break; - udelay(1); - } while ((status & UART_EMPTY) != UART_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout) - udelay(1); - } -} - -static void m32r_sio_console_putchar(struct uart_port *port, int ch) -{ - struct uart_sio_port *up = (struct uart_sio_port *)port; - - wait_for_xmitr(up); - sio_out(up, SIOTXB, ch); -} - -/* - * 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. - */ -static void m32r_sio_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_sio_port *up = &m32r_sio_ports[co->index]; - unsigned int ier; - - /* - * First save the UER then disable the interrupts - */ - ier = sio_in(up, SIOTRCR); - sio_out(up, SIOTRCR, 0); - - uart_console_write(&up->port, s, count, m32r_sio_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - sio_out(up, SIOTRCR, ier); -} - -static int __init m32r_sio_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - 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. - */ - if (co->index >= UART_NR) - co->index = 0; - port = &m32r_sio_ports[co->index].port; - - /* - * Temporary fix. - */ - spin_lock_init(&port->lock); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver m32r_sio_reg; -static struct console m32r_sio_console = { - .name = "ttyS", - .write = m32r_sio_console_write, - .device = uart_console_device, - .setup = m32r_sio_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &m32r_sio_reg, -}; - -static int __init m32r_sio_console_init(void) -{ - sio_reset(); - sio_init(); - m32r_sio_init_ports(); - register_console(&m32r_sio_console); - return 0; -} -console_initcall(m32r_sio_console_init); - -#define M32R_SIO_CONSOLE &m32r_sio_console -#else -#define M32R_SIO_CONSOLE NULL -#endif - -static struct uart_driver m32r_sio_reg = { - .owner = THIS_MODULE, - .driver_name = "sio", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = UART_NR, - .cons = M32R_SIO_CONSOLE, -}; - -/** - * m32r_sio_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -void m32r_sio_suspend_port(int line) -{ - uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -/** - * m32r_sio_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -void m32r_sio_resume_port(int line) -{ - uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); -} - -static int __init m32r_sio_init(void) -{ - int ret, i; - - printk(KERN_INFO "Serial: M32R SIO driver\n"); - - for (i = 0; i < nr_irqs; i++) - spin_lock_init(&irq_lists[i].lock); - - ret = uart_register_driver(&m32r_sio_reg); - if (ret >= 0) - m32r_sio_register_ports(&m32r_sio_reg); - - return ret; -} - -static void __exit m32r_sio_exit(void) -{ - int i; - - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port); - - uart_unregister_driver(&m32r_sio_reg); -} - -module_init(m32r_sio_init); -module_exit(m32r_sio_exit); - -EXPORT_SYMBOL(m32r_sio_suspend_port); -EXPORT_SYMBOL(m32r_sio_resume_port); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/serial/m32r_sio.h b/drivers/serial/m32r_sio.h deleted file mode 100644 index e9b7e11793b1..000000000000 --- a/drivers/serial/m32r_sio.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * m32r_sio.h - * - * Driver for M32R serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/8250.h. - * - * Copyright (C) 2001 Russell King. - * Copyright (C) 2004 Hirokazu Takata - * - * 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. - */ - - -struct m32r_sio_probe { - struct module *owner; - int (*pci_init_one)(struct pci_dev *dev); - void (*pci_remove_one)(struct pci_dev *dev); - void (*pnp_init)(void); -}; - -int m32r_sio_register_probe(struct m32r_sio_probe *probe); -void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); -void m32r_sio_get_irq_map(unsigned int *map); -void m32r_sio_suspend_port(int line); -void m32r_sio_resume_port(int line); - -struct old_serial_port { - unsigned int uart; - unsigned int baud_base; - unsigned int port; - unsigned int irq; - unsigned int flags; - unsigned char io_type; - unsigned char __iomem *iomem_base; - unsigned short iomem_reg_shift; -}; - -#define _INLINE_ inline - -#define PROBE_RSA (1 << 0) -#define PROBE_ANY (~0) - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/serial/m32r_sio_reg.h b/drivers/serial/m32r_sio_reg.h deleted file mode 100644 index 4671473793e3..000000000000 --- a/drivers/serial/m32r_sio_reg.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * m32r_sio_reg.h - * - * Copyright (C) 1992, 1994 by Theodore Ts'o. - * Copyright (C) 2004 Hirokazu Takata - * - * Redistribution of this file is permitted under the terms of the GNU - * Public License (GPL) - * - * These are the UART port assignments, expressed as offsets from the base - * register. These assignments should hold for any serial port based on - * a 8250, 16450, or 16550(A). - */ - -#ifndef _M32R_SIO_REG_H -#define _M32R_SIO_REG_H - - -#ifdef CONFIG_SERIAL_M32R_PLDSIO - -#define SIOCR 0x000 -#define SIOMOD0 0x002 -#define SIOMOD1 0x004 -#define SIOSTS 0x006 -#define SIOTRCR 0x008 -#define SIOBAUR 0x00a -// #define SIORBAUR 0x018 -#define SIOTXB 0x00c -#define SIORXB 0x00e - -#define UART_RX ((unsigned long) PLD_ESIO0RXB) - /* In: Receive buffer (DLAB=0) */ -#define UART_TX ((unsigned long) PLD_ESIO0TXB) - /* Out: Transmit buffer (DLAB=0) */ -#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ -#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx - * In: Fifo count - * Out: Fifo custom trigger levels - * XR16C85x only */ - -#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ -#define UART_IER ((unsigned long) PLD_ESIO0INTCR) - /* Out: Interrupt Enable Register */ -#define UART_FCTR 0 /* (LCR=BF) Feature Control Register - * XR16C85x only */ - -#define UART_IIR 0 /* In: Interrupt ID Register */ -#define UART_FCR 0 /* Out: FIFO Control Register */ -#define UART_EFR 0 /* I/O: Extended Features Register */ - /* (DLAB=1, 16C660 only) */ - -#define UART_LCR 0 /* Out: Line Control Register */ -#define UART_MCR 0 /* Out: Modem Control Register */ -#define UART_LSR ((unsigned long) PLD_ESIO0STS) - /* In: Line Status Register */ -#define UART_MSR 0 /* In: Modem Status Register */ -#define UART_SCR 0 /* I/O: Scratch Register */ -#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register - * FCTR bit 6 selects SCR or EMSR - * XR16c85x only */ - -#else /* not CONFIG_SERIAL_M32R_PLDSIO */ - -#define SIOCR 0x000 -#define SIOMOD0 0x004 -#define SIOMOD1 0x008 -#define SIOSTS 0x00c -#define SIOTRCR 0x010 -#define SIOBAUR 0x014 -#define SIORBAUR 0x018 -#define SIOTXB 0x01c -#define SIORXB 0x020 - -#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */ -#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */ -#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ -#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx - * In: Fifo count - * Out: Fifo custom trigger levels - * XR16C85x only */ - -#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ -#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */ -#define UART_FCTR 0 /* (LCR=BF) Feature Control Register - * XR16C85x only */ - -#define UART_IIR 0 /* In: Interrupt ID Register */ -#define UART_FCR 0 /* Out: FIFO Control Register */ -#define UART_EFR 0 /* I/O: Extended Features Register */ - /* (DLAB=1, 16C660 only) */ - -#define UART_LCR 0 /* Out: Line Control Register */ -#define UART_MCR 0 /* Out: Modem Control Register */ -#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */ -#define UART_MSR 0 /* In: Modem Status Register */ -#define UART_SCR 0 /* I/O: Scratch Register */ -#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register - * FCTR bit 6 selects SCR or EMSR - * XR16c85x only */ - -#endif /* CONFIG_SERIAL_M32R_PLDSIO */ - -#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * These are the definitions for the Line Control Register - * - * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting - * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. - */ -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ -#define UART_LCR_SBC 0x40 /* Set break control */ -#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ -#define UART_LCR_EPAR 0x10 /* Even parity select */ -#define UART_LCR_PARITY 0x08 /* Parity Enable */ -#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ -#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ -#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ -#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ -#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ - -/* - * These are the definitions for the Line Status Register - */ -#define UART_LSR_TEMT 0x02 /* Transmitter empty */ -#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x00 /* Break interrupt indicator */ -#define UART_LSR_FE 0x80 /* Frame error indicator */ -#define UART_LSR_PE 0x40 /* Parity error indicator */ -#define UART_LSR_OE 0x20 /* Overrun error indicator */ -#define UART_LSR_DR 0x04 /* Receiver data ready */ - -/* - * These are the definitions for the Interrupt Identification Register - */ -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ - -#define UART_IIR_MSI 0x00 /* Modem status interrupt */ -#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ -#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ - -/* - * These are the definitions for the Interrupt Enable Register - */ -#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */ -#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */ -#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */ -#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */ - -#endif /* _M32R_SIO_REG_H */ diff --git a/drivers/serial/max3100.c b/drivers/serial/max3100.c deleted file mode 100644 index beb1afa27d8d..000000000000 --- a/drivers/serial/max3100.c +++ /dev/null @@ -1,926 +0,0 @@ -/* - * - * Copyright (C) 2008 Christian Pellegrin - * - * 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. - * - * - * Notes: the MAX3100 doesn't provide an interrupt on CTS so we have - * to use polling for flow control. TX empty IRQ is unusable, since - * writing conf clears FIFO buffer and we cannot have this interrupt - * always asking us for attention. - * - * Example platform data: - - static struct plat_max3100 max3100_plat_data = { - .loopback = 0, - .crystal = 0, - .poll_time = 100, - }; - - static struct spi_board_info spi_board_info[] = { - { - .modalias = "max3100", - .platform_data = &max3100_plat_data, - .irq = IRQ_EINT12, - .max_speed_hz = 5*1000*1000, - .chip_select = 0, - }, - }; - - * The initial minor number is 209 in the low-density serial port: - * mknod /dev/ttyMAX0 c 204 209 - */ - -#define MAX3100_MAJOR 204 -#define MAX3100_MINOR 209 -/* 4 MAX3100s should be enough for everyone */ -#define MAX_MAX3100 4 - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define MAX3100_C (1<<14) -#define MAX3100_D (0<<14) -#define MAX3100_W (1<<15) -#define MAX3100_RX (0<<15) - -#define MAX3100_WC (MAX3100_W | MAX3100_C) -#define MAX3100_RC (MAX3100_RX | MAX3100_C) -#define MAX3100_WD (MAX3100_W | MAX3100_D) -#define MAX3100_RD (MAX3100_RX | MAX3100_D) -#define MAX3100_CMD (3 << 14) - -#define MAX3100_T (1<<14) -#define MAX3100_R (1<<15) - -#define MAX3100_FEN (1<<13) -#define MAX3100_SHDN (1<<12) -#define MAX3100_TM (1<<11) -#define MAX3100_RM (1<<10) -#define MAX3100_PM (1<<9) -#define MAX3100_RAM (1<<8) -#define MAX3100_IR (1<<7) -#define MAX3100_ST (1<<6) -#define MAX3100_PE (1<<5) -#define MAX3100_L (1<<4) -#define MAX3100_BAUD (0xf) - -#define MAX3100_TE (1<<10) -#define MAX3100_RAFE (1<<10) -#define MAX3100_RTS (1<<9) -#define MAX3100_CTS (1<<9) -#define MAX3100_PT (1<<8) -#define MAX3100_DATA (0xff) - -#define MAX3100_RT (MAX3100_R | MAX3100_T) -#define MAX3100_RTC (MAX3100_RT | MAX3100_CTS | MAX3100_RAFE) - -/* the following simulate a status reg for ignore_status_mask */ -#define MAX3100_STATUS_PE 1 -#define MAX3100_STATUS_FE 2 -#define MAX3100_STATUS_OE 4 - -struct max3100_port { - struct uart_port port; - struct spi_device *spi; - - int cts; /* last CTS received for flow ctrl */ - int tx_empty; /* last TX empty bit */ - - spinlock_t conf_lock; /* shared data */ - int conf_commit; /* need to make changes */ - int conf; /* configuration for the MAX31000 - * (bits 0-7, bits 8-11 are irqs) */ - int rts_commit; /* need to change rts */ - int rts; /* rts status */ - int baud; /* current baud rate */ - - int parity; /* keeps track if we should send parity */ -#define MAX3100_PARITY_ON 1 -#define MAX3100_PARITY_ODD 2 -#define MAX3100_7BIT 4 - int rx_enabled; /* if we should rx chars */ - - int irq; /* irq assigned to the max3100 */ - - int minor; /* minor number */ - int crystal; /* 1 if 3.6864Mhz crystal 0 for 1.8432 */ - int loopback; /* 1 if we are in loopback mode */ - - /* for handling irqs: need workqueue since we do spi_sync */ - struct workqueue_struct *workqueue; - struct work_struct work; - /* set to 1 to make the workhandler exit as soon as possible */ - int force_end_work; - /* need to know we are suspending to avoid deadlock on workqueue */ - int suspending; - - /* hook for suspending MAX3100 via dedicated pin */ - void (*max3100_hw_suspend) (int suspend); - - /* poll time (in ms) for ctrl lines */ - int poll_time; - /* and its timer */ - struct timer_list timer; -}; - -static struct max3100_port *max3100s[MAX_MAX3100]; /* the chips */ -static DEFINE_MUTEX(max3100s_lock); /* race on probe */ - -static int max3100_do_parity(struct max3100_port *s, u16 c) -{ - int parity; - - if (s->parity & MAX3100_PARITY_ODD) - parity = 1; - else - parity = 0; - - if (s->parity & MAX3100_7BIT) - c &= 0x7f; - else - c &= 0xff; - - parity = parity ^ (hweight8(c) & 1); - return parity; -} - -static int max3100_check_parity(struct max3100_port *s, u16 c) -{ - return max3100_do_parity(s, c) == ((c >> 8) & 1); -} - -static void max3100_calc_parity(struct max3100_port *s, u16 *c) -{ - if (s->parity & MAX3100_7BIT) - *c &= 0x7f; - else - *c &= 0xff; - - if (s->parity & MAX3100_PARITY_ON) - *c |= max3100_do_parity(s, *c) << 8; -} - -static void max3100_work(struct work_struct *w); - -static void max3100_dowork(struct max3100_port *s) -{ - if (!s->force_end_work && !work_pending(&s->work) && - !freezing(current) && !s->suspending) - queue_work(s->workqueue, &s->work); -} - -static void max3100_timeout(unsigned long data) -{ - struct max3100_port *s = (struct max3100_port *)data; - - if (s->port.state) { - max3100_dowork(s); - mod_timer(&s->timer, jiffies + s->poll_time); - } -} - -static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) -{ - struct spi_message message; - u16 etx, erx; - int status; - struct spi_transfer tran = { - .tx_buf = &etx, - .rx_buf = &erx, - .len = 2, - }; - - etx = cpu_to_be16(tx); - spi_message_init(&message); - spi_message_add_tail(&tran, &message); - status = spi_sync(s->spi, &message); - if (status) { - dev_warn(&s->spi->dev, "error while calling spi_sync\n"); - return -EIO; - } - *rx = be16_to_cpu(erx); - s->tx_empty = (*rx & MAX3100_T) > 0; - dev_dbg(&s->spi->dev, "%04x - %04x\n", tx, *rx); - return 0; -} - -static int max3100_handlerx(struct max3100_port *s, u16 rx) -{ - unsigned int ch, flg, status = 0; - int ret = 0, cts; - - if (rx & MAX3100_R && s->rx_enabled) { - dev_dbg(&s->spi->dev, "%s\n", __func__); - ch = rx & (s->parity & MAX3100_7BIT ? 0x7f : 0xff); - if (rx & MAX3100_RAFE) { - s->port.icount.frame++; - flg = TTY_FRAME; - status |= MAX3100_STATUS_FE; - } else { - if (s->parity & MAX3100_PARITY_ON) { - if (max3100_check_parity(s, rx)) { - s->port.icount.rx++; - flg = TTY_NORMAL; - } else { - s->port.icount.parity++; - flg = TTY_PARITY; - status |= MAX3100_STATUS_PE; - } - } else { - s->port.icount.rx++; - flg = TTY_NORMAL; - } - } - uart_insert_char(&s->port, status, MAX3100_STATUS_OE, ch, flg); - ret = 1; - } - - cts = (rx & MAX3100_CTS) > 0; - if (s->cts != cts) { - s->cts = cts; - uart_handle_cts_change(&s->port, cts ? TIOCM_CTS : 0); - } - - return ret; -} - -static void max3100_work(struct work_struct *w) -{ - struct max3100_port *s = container_of(w, struct max3100_port, work); - int rxchars; - u16 tx, rx; - int conf, cconf, rts, crts; - struct circ_buf *xmit = &s->port.state->xmit; - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - rxchars = 0; - do { - spin_lock(&s->conf_lock); - conf = s->conf; - cconf = s->conf_commit; - s->conf_commit = 0; - rts = s->rts; - crts = s->rts_commit; - s->rts_commit = 0; - spin_unlock(&s->conf_lock); - if (cconf) - max3100_sr(s, MAX3100_WC | conf, &rx); - if (crts) { - max3100_sr(s, MAX3100_WD | MAX3100_TE | - (s->rts ? MAX3100_RTS : 0), &rx); - rxchars += max3100_handlerx(s, rx); - } - - max3100_sr(s, MAX3100_RD, &rx); - rxchars += max3100_handlerx(s, rx); - - if (rx & MAX3100_T) { - tx = 0xffff; - if (s->port.x_char) { - tx = s->port.x_char; - s->port.icount.tx++; - s->port.x_char = 0; - } else if (!uart_circ_empty(xmit) && - !uart_tx_stopped(&s->port)) { - tx = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); - s->port.icount.tx++; - } - if (tx != 0xffff) { - max3100_calc_parity(s, &tx); - tx |= MAX3100_WD | (s->rts ? MAX3100_RTS : 0); - max3100_sr(s, tx, &rx); - rxchars += max3100_handlerx(s, rx); - } - } - - if (rxchars > 16 && s->port.state->port.tty != NULL) { - tty_flip_buffer_push(s->port.state->port.tty); - rxchars = 0; - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); - - } while (!s->force_end_work && - !freezing(current) && - ((rx & MAX3100_R) || - (!uart_circ_empty(xmit) && - !uart_tx_stopped(&s->port)))); - - if (rxchars > 0 && s->port.state->port.tty != NULL) - tty_flip_buffer_push(s->port.state->port.tty); -} - -static irqreturn_t max3100_irq(int irqno, void *dev_id) -{ - struct max3100_port *s = dev_id; - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - max3100_dowork(s); - return IRQ_HANDLED; -} - -static void max3100_enable_ms(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - if (s->poll_time > 0) - mod_timer(&s->timer, jiffies); - dev_dbg(&s->spi->dev, "%s\n", __func__); -} - -static void max3100_start_tx(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - max3100_dowork(s); -} - -static void max3100_stop_rx(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - s->rx_enabled = 0; - spin_lock(&s->conf_lock); - s->conf &= ~MAX3100_RM; - s->conf_commit = 1; - spin_unlock(&s->conf_lock); - max3100_dowork(s); -} - -static unsigned int max3100_tx_empty(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - /* may not be truly up-to-date */ - max3100_dowork(s); - return s->tx_empty; -} - -static unsigned int max3100_get_mctrl(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - /* may not be truly up-to-date */ - max3100_dowork(s); - /* always assert DCD and DSR since these lines are not wired */ - return (s->cts ? TIOCM_CTS : 0) | TIOCM_DSR | TIOCM_CAR; -} - -static void max3100_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - int rts; - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - rts = (mctrl & TIOCM_RTS) > 0; - - spin_lock(&s->conf_lock); - if (s->rts != rts) { - s->rts = rts; - s->rts_commit = 1; - max3100_dowork(s); - } - spin_unlock(&s->conf_lock); -} - -static void -max3100_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - int baud = 0; - unsigned cflag; - u32 param_new, param_mask, parity = 0; - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - cflag = termios->c_cflag; - param_new = 0; - param_mask = 0; - - baud = tty_termios_baud_rate(termios); - param_new = s->conf & MAX3100_BAUD; - switch (baud) { - case 300: - if (s->crystal) - baud = s->baud; - else - param_new = 15; - break; - case 600: - param_new = 14 + s->crystal; - break; - case 1200: - param_new = 13 + s->crystal; - break; - case 2400: - param_new = 12 + s->crystal; - break; - case 4800: - param_new = 11 + s->crystal; - break; - case 9600: - param_new = 10 + s->crystal; - break; - case 19200: - param_new = 9 + s->crystal; - break; - case 38400: - param_new = 8 + s->crystal; - break; - case 57600: - param_new = 1 + s->crystal; - break; - case 115200: - param_new = 0 + s->crystal; - break; - case 230400: - if (s->crystal) - param_new = 0; - else - baud = s->baud; - break; - default: - baud = s->baud; - } - tty_termios_encode_baud_rate(termios, baud, baud); - s->baud = baud; - param_mask |= MAX3100_BAUD; - - if ((cflag & CSIZE) == CS8) { - param_new &= ~MAX3100_L; - parity &= ~MAX3100_7BIT; - } else { - param_new |= MAX3100_L; - parity |= MAX3100_7BIT; - cflag = (cflag & ~CSIZE) | CS7; - } - param_mask |= MAX3100_L; - - if (cflag & CSTOPB) - param_new |= MAX3100_ST; - else - param_new &= ~MAX3100_ST; - param_mask |= MAX3100_ST; - - if (cflag & PARENB) { - param_new |= MAX3100_PE; - parity |= MAX3100_PARITY_ON; - } else { - param_new &= ~MAX3100_PE; - parity &= ~MAX3100_PARITY_ON; - } - param_mask |= MAX3100_PE; - - if (cflag & PARODD) - parity |= MAX3100_PARITY_ODD; - else - parity &= ~MAX3100_PARITY_ODD; - - /* mask termios capabilities we don't support */ - cflag &= ~CMSPAR; - termios->c_cflag = cflag; - - s->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - s->port.ignore_status_mask |= - MAX3100_STATUS_PE | MAX3100_STATUS_FE | - MAX3100_STATUS_OE; - - /* we are sending char from a workqueue so enable */ - s->port.state->port.tty->low_latency = 1; - - if (s->poll_time > 0) - del_timer_sync(&s->timer); - - uart_update_timeout(port, termios->c_cflag, baud); - - spin_lock(&s->conf_lock); - s->conf = (s->conf & ~param_mask) | (param_new & param_mask); - s->conf_commit = 1; - s->parity = parity; - spin_unlock(&s->conf_lock); - max3100_dowork(s); - - if (UART_ENABLE_MS(&s->port, termios->c_cflag)) - max3100_enable_ms(&s->port); -} - -static void max3100_shutdown(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - if (s->suspending) - return; - - s->force_end_work = 1; - - if (s->poll_time > 0) - del_timer_sync(&s->timer); - - if (s->workqueue) { - flush_workqueue(s->workqueue); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - } - if (s->irq) - free_irq(s->irq, s); - - /* set shutdown mode to save power */ - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(1); - else { - u16 tx, rx; - - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(s, tx, &rx); - } -} - -static int max3100_startup(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - char b[12]; - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - s->conf = MAX3100_RM; - s->baud = s->crystal ? 230400 : 115200; - s->rx_enabled = 1; - - if (s->suspending) - return 0; - - s->force_end_work = 0; - s->parity = 0; - s->rts = 0; - - sprintf(b, "max3100-%d", s->minor); - s->workqueue = create_freezeable_workqueue(b); - if (!s->workqueue) { - dev_warn(&s->spi->dev, "cannot create workqueue\n"); - return -EBUSY; - } - INIT_WORK(&s->work, max3100_work); - - if (request_irq(s->irq, max3100_irq, - IRQF_TRIGGER_FALLING, "max3100", s) < 0) { - dev_warn(&s->spi->dev, "cannot allocate irq %d\n", s->irq); - s->irq = 0; - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - return -EBUSY; - } - - if (s->loopback) { - u16 tx, rx; - tx = 0x4001; - max3100_sr(s, tx, &rx); - } - - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(0); - s->conf_commit = 1; - max3100_dowork(s); - /* wait for clock to settle */ - msleep(50); - - max3100_enable_ms(&s->port); - - return 0; -} - -static const char *max3100_type(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - return s->port.type == PORT_MAX3100 ? "MAX3100" : NULL; -} - -static void max3100_release_port(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); -} - -static void max3100_config_port(struct uart_port *port, int flags) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - if (flags & UART_CONFIG_TYPE) - s->port.type = PORT_MAX3100; -} - -static int max3100_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - int ret = -EINVAL; - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3100) - ret = 0; - return ret; -} - -static void max3100_stop_tx(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); -} - -static int max3100_request_port(struct uart_port *port) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - return 0; -} - -static void max3100_break_ctl(struct uart_port *port, int break_state) -{ - struct max3100_port *s = container_of(port, - struct max3100_port, - port); - - dev_dbg(&s->spi->dev, "%s\n", __func__); -} - -static struct uart_ops max3100_ops = { - .tx_empty = max3100_tx_empty, - .set_mctrl = max3100_set_mctrl, - .get_mctrl = max3100_get_mctrl, - .stop_tx = max3100_stop_tx, - .start_tx = max3100_start_tx, - .stop_rx = max3100_stop_rx, - .enable_ms = max3100_enable_ms, - .break_ctl = max3100_break_ctl, - .startup = max3100_startup, - .shutdown = max3100_shutdown, - .set_termios = max3100_set_termios, - .type = max3100_type, - .release_port = max3100_release_port, - .request_port = max3100_request_port, - .config_port = max3100_config_port, - .verify_port = max3100_verify_port, -}; - -static struct uart_driver max3100_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "ttyMAX", - .dev_name = "ttyMAX", - .major = MAX3100_MAJOR, - .minor = MAX3100_MINOR, - .nr = MAX_MAX3100, -}; -static int uart_driver_registered; - -static int __devinit max3100_probe(struct spi_device *spi) -{ - int i, retval; - struct plat_max3100 *pdata; - u16 tx, rx; - - mutex_lock(&max3100s_lock); - - if (!uart_driver_registered) { - uart_driver_registered = 1; - retval = uart_register_driver(&max3100_uart_driver); - if (retval) { - printk(KERN_ERR "Couldn't register max3100 uart driver\n"); - mutex_unlock(&max3100s_lock); - return retval; - } - } - - for (i = 0; i < MAX_MAX3100; i++) - if (!max3100s[i]) - break; - if (i == MAX_MAX3100) { - dev_warn(&spi->dev, "too many MAX3100 chips\n"); - mutex_unlock(&max3100s_lock); - return -ENOMEM; - } - - max3100s[i] = kzalloc(sizeof(struct max3100_port), GFP_KERNEL); - if (!max3100s[i]) { - dev_warn(&spi->dev, - "kmalloc for max3100 structure %d failed!\n", i); - mutex_unlock(&max3100s_lock); - return -ENOMEM; - } - max3100s[i]->spi = spi; - max3100s[i]->irq = spi->irq; - spin_lock_init(&max3100s[i]->conf_lock); - dev_set_drvdata(&spi->dev, max3100s[i]); - pdata = spi->dev.platform_data; - max3100s[i]->crystal = pdata->crystal; - max3100s[i]->loopback = pdata->loopback; - max3100s[i]->poll_time = pdata->poll_time * HZ / 1000; - if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0) - max3100s[i]->poll_time = 1; - max3100s[i]->max3100_hw_suspend = pdata->max3100_hw_suspend; - max3100s[i]->minor = i; - init_timer(&max3100s[i]->timer); - max3100s[i]->timer.function = max3100_timeout; - max3100s[i]->timer.data = (unsigned long) max3100s[i]; - - dev_dbg(&spi->dev, "%s: adding port %d\n", __func__, i); - max3100s[i]->port.irq = max3100s[i]->irq; - max3100s[i]->port.uartclk = max3100s[i]->crystal ? 3686400 : 1843200; - max3100s[i]->port.fifosize = 16; - max3100s[i]->port.ops = &max3100_ops; - max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - max3100s[i]->port.line = i; - max3100s[i]->port.type = PORT_MAX3100; - max3100s[i]->port.dev = &spi->dev; - retval = uart_add_one_port(&max3100_uart_driver, &max3100s[i]->port); - if (retval < 0) - dev_warn(&spi->dev, - "uart_add_one_port failed for line %d with error %d\n", - i, retval); - - /* set shutdown mode to save power. Will be woken-up on open */ - if (max3100s[i]->max3100_hw_suspend) - max3100s[i]->max3100_hw_suspend(1); - else { - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(max3100s[i], tx, &rx); - } - mutex_unlock(&max3100s_lock); - return 0; -} - -static int __devexit max3100_remove(struct spi_device *spi) -{ - struct max3100_port *s = dev_get_drvdata(&spi->dev); - int i; - - mutex_lock(&max3100s_lock); - - /* find out the index for the chip we are removing */ - for (i = 0; i < MAX_MAX3100; i++) - if (max3100s[i] == s) - break; - - dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i); - uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port); - kfree(max3100s[i]); - max3100s[i] = NULL; - - /* check if this is the last chip we have */ - for (i = 0; i < MAX_MAX3100; i++) - if (max3100s[i]) { - mutex_unlock(&max3100s_lock); - return 0; - } - pr_debug("removing max3100 driver\n"); - uart_unregister_driver(&max3100_uart_driver); - - mutex_unlock(&max3100s_lock); - return 0; -} - -#ifdef CONFIG_PM - -static int max3100_suspend(struct spi_device *spi, pm_message_t state) -{ - struct max3100_port *s = dev_get_drvdata(&spi->dev); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - disable_irq(s->irq); - - s->suspending = 1; - uart_suspend_port(&max3100_uart_driver, &s->port); - - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(1); - else { - /* no HW suspend, so do SW one */ - u16 tx, rx; - - tx = MAX3100_WC | MAX3100_SHDN; - max3100_sr(s, tx, &rx); - } - return 0; -} - -static int max3100_resume(struct spi_device *spi) -{ - struct max3100_port *s = dev_get_drvdata(&spi->dev); - - dev_dbg(&s->spi->dev, "%s\n", __func__); - - if (s->max3100_hw_suspend) - s->max3100_hw_suspend(0); - uart_resume_port(&max3100_uart_driver, &s->port); - s->suspending = 0; - - enable_irq(s->irq); - - s->conf_commit = 1; - if (s->workqueue) - max3100_dowork(s); - - return 0; -} - -#else -#define max3100_suspend NULL -#define max3100_resume NULL -#endif - -static struct spi_driver max3100_driver = { - .driver = { - .name = "max3100", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - - .probe = max3100_probe, - .remove = __devexit_p(max3100_remove), - .suspend = max3100_suspend, - .resume = max3100_resume, -}; - -static int __init max3100_init(void) -{ - return spi_register_driver(&max3100_driver); -} -module_init(max3100_init); - -static void __exit max3100_exit(void) -{ - spi_unregister_driver(&max3100_driver); -} -module_exit(max3100_exit); - -MODULE_DESCRIPTION("MAX3100 driver"); -MODULE_AUTHOR("Christian Pellegrin "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:max3100"); diff --git a/drivers/serial/max3107-aava.c b/drivers/serial/max3107-aava.c deleted file mode 100644 index a1fe304f2f52..000000000000 --- a/drivers/serial/max3107-aava.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * max3107.c - spi uart protocol driver for Maxim 3107 - * Based on max3100.c - * by Christian Pellegrin - * and max3110.c - * by Feng Tang - * - * Copyright (C) Aavamobile 2009 - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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 -#include "max3107.h" - -/* GPIO direction to input function */ -static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[1]; /* Buffer for SPI transfer */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return -EINVAL; - } - - /* Read current GPIO configuration register */ - buf[0] = MAX3107_GPIOCFG_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n"); - return -EIO; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - - /* Set GPIO to input */ - buf[0] &= ~(0x0001 << offset); - - /* Write new GPIO configuration register value */ - buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 2)) { - dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n"); - return -EIO; - } - return 0; -} - -/* GPIO direction to output function */ -static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[2]; /* Buffer for SPI transfers */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return -EINVAL; - } - - /* Read current GPIO configuration and data registers */ - buf[0] = MAX3107_GPIOCFG_REG; - buf[1] = MAX3107_GPIODATA_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { - dev_err(&s->spi->dev, "SPI transfer gpio failed\n"); - return -EIO; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - buf[1] &= MAX3107_SPI_RX_DATA_MASK; - - /* Set GPIO to output */ - buf[0] |= (0x0001 << offset); - /* Set value */ - if (value) - buf[1] |= (0x0001 << offset); - else - buf[1] &= ~(0x0001 << offset); - - /* Write new GPIO configuration and data register values */ - buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); - buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 4)) { - dev_err(&s->spi->dev, - "SPI transfer for GPIO conf data w failed\n"); - return -EIO; - } - return 0; -} - -/* GPIO value query function */ -static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[1]; /* Buffer for SPI transfer */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return -EINVAL; - } - - /* Read current GPIO data register */ - buf[0] = MAX3107_GPIODATA_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n"); - return -EIO; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - - /* Return value */ - return buf[0] & (0x0001 << offset); -} - -/* GPIO value set function */ -static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - struct max3107_port *s = container_of(chip, struct max3107_port, chip); - u16 buf[2]; /* Buffer for SPI transfers */ - - if (offset >= MAX3107_GPIO_COUNT) { - dev_err(&s->spi->dev, "Invalid GPIO\n"); - return; - } - - /* Read current GPIO configuration registers*/ - buf[0] = MAX3107_GPIODATA_REG; - buf[1] = MAX3107_GPIOCFG_REG; - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { - dev_err(&s->spi->dev, - "SPI transfer for GPIO data and config read failed\n"); - return; - } - buf[0] &= MAX3107_SPI_RX_DATA_MASK; - buf[1] &= MAX3107_SPI_RX_DATA_MASK; - - if (!(buf[1] & (0x0001 << offset))) { - /* Configured as input, can't set value */ - dev_warn(&s->spi->dev, - "Trying to set value for input GPIO\n"); - return; - } - - /* Set value */ - if (value) - buf[0] |= (0x0001 << offset); - else - buf[0] &= ~(0x0001 << offset); - - /* Write new GPIO data register value */ - buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n"); -} - -/* GPIO chip data */ -static struct gpio_chip max3107_gpio_chip = { - .owner = THIS_MODULE, - .direction_input = max3107_gpio_direction_in, - .direction_output = max3107_gpio_direction_out, - .get = max3107_gpio_get, - .set = max3107_gpio_set, - .can_sleep = 1, - .base = MAX3107_GPIO_BASE, - .ngpio = MAX3107_GPIO_COUNT, -}; - -/** - * max3107_aava_reset - reset on AAVA systems - * @spi: The SPI device we are probing - * - * Reset the device ready for probing. - */ - -static int max3107_aava_reset(struct spi_device *spi) -{ - /* Reset the chip */ - if (gpio_request(MAX3107_RESET_GPIO, "max3107")) { - pr_err("Requesting RESET GPIO failed\n"); - return -EIO; - } - if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) { - pr_err("Setting RESET GPIO to 0 failed\n"); - gpio_free(MAX3107_RESET_GPIO); - return -EIO; - } - msleep(MAX3107_RESET_DELAY); - if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) { - pr_err("Setting RESET GPIO to 1 failed\n"); - gpio_free(MAX3107_RESET_GPIO); - return -EIO; - } - gpio_free(MAX3107_RESET_GPIO); - msleep(MAX3107_WAKEUP_DELAY); - return 0; -} - -static int max3107_aava_configure(struct max3107_port *s) -{ - int retval; - - /* Initialize GPIO chip data */ - s->chip = max3107_gpio_chip; - s->chip.label = s->spi->modalias; - s->chip.dev = &s->spi->dev; - - /* Add GPIO chip */ - retval = gpiochip_add(&s->chip); - if (retval) { - dev_err(&s->spi->dev, "Adding GPIO chip failed\n"); - return retval; - } - - /* Temporary fix for EV2 boot problems, set modem reset to 0 */ - max3107_gpio_direction_out(&s->chip, 3, 0); - return 0; -} - -#if 0 -/* This will get enabled once we have the board stuff merged for this - specific case */ - -static const struct baud_table brg13_ext[] = { - { 300, MAX3107_BRG13_B300 }, - { 600, MAX3107_BRG13_B600 }, - { 1200, MAX3107_BRG13_B1200 }, - { 2400, MAX3107_BRG13_B2400 }, - { 4800, MAX3107_BRG13_B4800 }, - { 9600, MAX3107_BRG13_B9600 }, - { 19200, MAX3107_BRG13_B19200 }, - { 57600, MAX3107_BRG13_B57600 }, - { 115200, MAX3107_BRG13_B115200 }, - { 230400, MAX3107_BRG13_B230400 }, - { 460800, MAX3107_BRG13_B460800 }, - { 921600, MAX3107_BRG13_B921600 }, - { 0, 0 } -}; - -static void max3107_aava_init(struct max3107_port *s) -{ - /*override for AAVA SC specific*/ - if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) { - if (get_koski_build_id() <= KOSKI_EV2) - if (s->ext_clk) { - s->brg_cfg = MAX3107_BRG13_B9600; - s->baud_tbl = (struct baud_table *)brg13_ext; - } - } -} -#endif - -static int __devexit max3107_aava_remove(struct spi_device *spi) -{ - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - /* Remove GPIO chip */ - if (gpiochip_remove(&s->chip)) - dev_warn(&spi->dev, "Removing GPIO chip failed\n"); - - /* Then do the default remove */ - return max3107_remove(spi); -} - -/* Platform data */ -static struct max3107_plat aava_plat_data = { - .loopback = 0, - .ext_clk = 1, -/* .init = max3107_aava_init, */ - .configure = max3107_aava_configure, - .hw_suspend = max3107_hw_susp, - .polled_mode = 0, - .poll_time = 0, -}; - - -static int __devinit max3107_probe_aava(struct spi_device *spi) -{ - int err = max3107_aava_reset(spi); - if (err < 0) - return err; - return max3107_probe(spi, &aava_plat_data); -} - -/* Spi driver data */ -static struct spi_driver max3107_driver = { - .driver = { - .name = "aava-max3107", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = max3107_probe_aava, - .remove = __devexit_p(max3107_aava_remove), - .suspend = max3107_suspend, - .resume = max3107_resume, -}; - -/* Driver init function */ -static int __init max3107_init(void) -{ - return spi_register_driver(&max3107_driver); -} - -/* Driver exit function */ -static void __exit max3107_exit(void) -{ - spi_unregister_driver(&max3107_driver); -} - -module_init(max3107_init); -module_exit(max3107_exit); - -MODULE_DESCRIPTION("MAX3107 driver"); -MODULE_AUTHOR("Aavamobile"); -MODULE_ALIAS("aava-max3107-spi"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/serial/max3107.c b/drivers/serial/max3107.c deleted file mode 100644 index 910870edf708..000000000000 --- a/drivers/serial/max3107.c +++ /dev/null @@ -1,1213 +0,0 @@ -/* - * max3107.c - spi uart protocol driver for Maxim 3107 - * Based on max3100.c - * by Christian Pellegrin - * and max3110.c - * by Feng Tang - * - * Copyright (C) Aavamobile 2009 - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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 "max3107.h" - -static const struct baud_table brg26_ext[] = { - { 300, MAX3107_BRG26_B300 }, - { 600, MAX3107_BRG26_B600 }, - { 1200, MAX3107_BRG26_B1200 }, - { 2400, MAX3107_BRG26_B2400 }, - { 4800, MAX3107_BRG26_B4800 }, - { 9600, MAX3107_BRG26_B9600 }, - { 19200, MAX3107_BRG26_B19200 }, - { 57600, MAX3107_BRG26_B57600 }, - { 115200, MAX3107_BRG26_B115200 }, - { 230400, MAX3107_BRG26_B230400 }, - { 460800, MAX3107_BRG26_B460800 }, - { 921600, MAX3107_BRG26_B921600 }, - { 0, 0 } -}; - -static const struct baud_table brg13_int[] = { - { 300, MAX3107_BRG13_IB300 }, - { 600, MAX3107_BRG13_IB600 }, - { 1200, MAX3107_BRG13_IB1200 }, - { 2400, MAX3107_BRG13_IB2400 }, - { 4800, MAX3107_BRG13_IB4800 }, - { 9600, MAX3107_BRG13_IB9600 }, - { 19200, MAX3107_BRG13_IB19200 }, - { 57600, MAX3107_BRG13_IB57600 }, - { 115200, MAX3107_BRG13_IB115200 }, - { 230400, MAX3107_BRG13_IB230400 }, - { 460800, MAX3107_BRG13_IB460800 }, - { 921600, MAX3107_BRG13_IB921600 }, - { 0, 0 } -}; - -static u32 get_new_brg(int baud, struct max3107_port *s) -{ - int i; - const struct baud_table *baud_tbl = s->baud_tbl; - - for (i = 0; i < 13; i++) { - if (baud == baud_tbl[i].baud) - return baud_tbl[i].new_brg; - } - - return 0; -} - -/* Perform SPI transfer for write/read of device register(s) */ -int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len) -{ - struct spi_message spi_msg; - struct spi_transfer spi_xfer; - - /* Initialize SPI ,message */ - spi_message_init(&spi_msg); - - /* Initialize SPI transfer */ - memset(&spi_xfer, 0, sizeof spi_xfer); - spi_xfer.len = len; - spi_xfer.tx_buf = tx; - spi_xfer.rx_buf = rx; - spi_xfer.speed_hz = MAX3107_SPI_SPEED; - - /* Add SPI transfer to SPI message */ - spi_message_add_tail(&spi_xfer, &spi_msg); - -#ifdef DBG_TRACE_SPI_DATA - { - int i; - pr_info("tx len %d:\n", spi_xfer.len); - for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) - pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]); - pr_info("\n"); - } -#endif - - /* Perform synchronous SPI transfer */ - if (spi_sync(s->spi, &spi_msg)) { - dev_err(&s->spi->dev, "spi_sync failure\n"); - return -EIO; - } - -#ifdef DBG_TRACE_SPI_DATA - if (spi_xfer.rx_buf) { - int i; - pr_info("rx len %d:\n", spi_xfer.len); - for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) - pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]); - pr_info("\n"); - } -#endif - return 0; -} -EXPORT_SYMBOL_GPL(max3107_rw); - -/* Puts received data to circular buffer */ -static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data, - int len) -{ - struct uart_port *port = &s->port; - struct tty_struct *tty; - - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - /* Insert received data */ - tty_insert_flip_string(tty, data, len); - /* Update RX counter */ - port->icount.rx += len; -} - -/* Handle data receiving */ -static void max3107_handlerx(struct max3107_port *s, u16 rxlvl) -{ - int i; - int j; - int len; /* SPI transfer buffer length */ - u16 *buf; - u8 *valid_str; - - if (!s->rx_enabled) - /* RX is disabled */ - return; - - if (rxlvl == 0) { - /* RX fifo is empty */ - return; - } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) { - dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl); - /* Ensure sanity of RX level */ - rxlvl = MAX3107_RX_FIFO_SIZE; - } - if ((s->rxbuf == 0) || (s->rxstr == 0)) { - dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n"); - return; - } - buf = s->rxbuf; - valid_str = s->rxstr; - while (rxlvl) { - pr_debug("rxlvl %d\n", rxlvl); - /* Clear buffer */ - memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2)); - len = 0; - if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) { - /* First disable RX FIFO interrupt */ - pr_debug("Disabling RX INT\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT; - buf[0] |= s->irqen_reg; - len++; - } - /* Just increase the length by amount of words in FIFO since - * buffer was zeroed and SPI transfer of 0x0000 means reading - * from RX FIFO - */ - len += rxlvl; - /* Append RX level query */ - buf[len] = MAX3107_RXFIFOLVL_REG; - len++; - - /* Perform the SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) { - dev_err(&s->spi->dev, "SPI transfer for RX h failed\n"); - return; - } - - /* Skip RX FIFO interrupt disabling word if it was added */ - j = ((len - 1) - rxlvl); - /* Read received words */ - for (i = 0; i < rxlvl; i++, j++) - valid_str[i] = (u8)buf[j]; - put_data_to_circ_buf(s, valid_str, rxlvl); - /* Get new RX level */ - rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK); - } - - if (s->rx_enabled) { - /* RX still enabled, re-enable RX FIFO interrupt */ - pr_debug("Enabling RX INT\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; - buf[0] |= s->irqen_reg; - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n"); - } - - /* Push the received data to receivers */ - if (s->port.state->port.tty) - tty_flip_buffer_push(s->port.state->port.tty); -} - - -/* Handle data sending */ -static void max3107_handletx(struct max3107_port *s) -{ - struct circ_buf *xmit = &s->port.state->xmit; - int i; - unsigned long flags; - int len; /* SPI transfer buffer length */ - u16 *buf; - - if (!s->tx_fifo_empty) - /* Don't send more data before previous data is sent */ - return; - - if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) - /* No data to send or TX is stopped */ - return; - - if (!s->txbuf) { - dev_warn(&s->spi->dev, "Txbuf isn't ready\n"); - return; - } - buf = s->txbuf; - /* Get length of data pending in circular buffer */ - len = uart_circ_chars_pending(xmit); - if (len) { - /* Limit to size of TX FIFO */ - if (len > MAX3107_TX_FIFO_SIZE) - len = MAX3107_TX_FIFO_SIZE; - - pr_debug("txlen %d\n", len); - - /* Update TX counter */ - s->port.icount.tx += len; - - /* TX FIFO will no longer be empty */ - s->tx_fifo_empty = 0; - - i = 0; - if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) { - /* First disable TX empty interrupt */ - pr_debug("Disabling TE INT\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT; - buf[i] |= s->irqen_reg; - i++; - len++; - } - /* Add data to send */ - spin_lock_irqsave(&s->port.lock, flags); - for ( ; i < len ; i++) { - buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG); - buf[i] |= ((u16)xmit->buf[xmit->tail] & - MAX3107_SPI_TX_DATA_MASK); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } - spin_unlock_irqrestore(&s->port.lock, flags); - if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) { - /* Enable TX empty interrupt */ - pr_debug("Enabling TE INT\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); - s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT; - buf[i] |= s->irqen_reg; - i++; - len++; - } - if (!s->tx_enabled) { - /* Enable TX */ - pr_debug("Enable TX\n"); - buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT; - buf[i] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - s->tx_enabled = 1; - i++; - len++; - } - - /* Perform the SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, len*2)) { - dev_err(&s->spi->dev, - "SPI transfer TX handling failed\n"); - return; - } - } - - /* Indicate wake up if circular buffer is getting low on data */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); - -} - -/* Handle interrupts - * Also reads and returns current RX FIFO level - */ -static u16 handle_interrupt(struct max3107_port *s) -{ - u16 buf[4]; /* Buffer for SPI transfers */ - u8 irq_status; - u16 rx_level; - unsigned long flags; - - /* Read IRQ status register */ - buf[0] = MAX3107_IRQSTS_REG; - /* Read status IRQ status register */ - buf[1] = MAX3107_STS_IRQSTS_REG; - /* Read LSR IRQ status register */ - buf[2] = MAX3107_LSR_IRQSTS_REG; - /* Query RX level */ - buf[3] = MAX3107_RXFIFOLVL_REG; - - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) { - dev_err(&s->spi->dev, - "SPI transfer for INTR handling failed\n"); - return 0; - } - - irq_status = (u8)buf[0]; - pr_debug("IRQSTS %x\n", irq_status); - rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK); - - if (irq_status & MAX3107_IRQ_LSR_BIT) { - /* LSR interrupt */ - if (buf[2] & MAX3107_LSR_RXTO_BIT) - /* RX timeout interrupt, - * handled by normal RX handling - */ - pr_debug("RX TO INT\n"); - } - - if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) { - /* Tx empty interrupt, - * disable TX and set tx_fifo_empty flag - */ - pr_debug("TE INT, disabling TX\n"); - buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; - buf[0] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer TX dis failed\n"); - s->tx_enabled = 0; - s->tx_fifo_empty = 1; - } - - if (irq_status & MAX3107_IRQ_RXFIFO_BIT) - /* RX FIFO interrupt, - * handled by normal RX handling - */ - pr_debug("RFIFO INT\n"); - - /* Return RX level */ - return rx_level; -} - -/* Trigger work thread*/ -static void max3107_dowork(struct max3107_port *s) -{ - if (!work_pending(&s->work) && !freezing(current) && !s->suspended) - queue_work(s->workqueue, &s->work); - else - dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n"); -} - -/* Work thread */ -static void max3107_work(struct work_struct *w) -{ - struct max3107_port *s = container_of(w, struct max3107_port, work); - u16 rxlvl = 0; - int len; /* SPI transfer buffer length */ - u16 buf[5]; /* Buffer for SPI transfers */ - unsigned long flags; - - /* Start by reading current RX FIFO level */ - buf[0] = MAX3107_RXFIFOLVL_REG; - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer RX lev failed\n"); - rxlvl = 0; - } else { - rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK); - } - - do { - pr_debug("rxlvl %d\n", rxlvl); - - /* Handle RX */ - max3107_handlerx(s, rxlvl); - rxlvl = 0; - - if (s->handle_irq) { - /* Handle pending interrupts - * We also get new RX FIFO level since new data may - * have been received while pushing received data to - * receivers - */ - s->handle_irq = 0; - rxlvl = handle_interrupt(s); - } - - /* Handle TX */ - max3107_handletx(s); - - /* Handle configuration changes */ - len = 0; - spin_lock_irqsave(&s->data_lock, flags); - if (s->mode1_commit) { - pr_debug("mode1_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - buf[len++] |= s->mode1_reg; - s->mode1_commit = 0; - } - if (s->lcr_commit) { - pr_debug("lcr_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); - buf[len++] |= s->lcr_reg; - s->lcr_commit = 0; - } - if (s->brg_commit) { - pr_debug("brg_commit\n"); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); - buf[len++] |= ((s->brg_cfg >> 16) & - MAX3107_SPI_TX_DATA_MASK); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); - buf[len++] |= ((s->brg_cfg >> 8) & - MAX3107_SPI_TX_DATA_MASK); - buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); - buf[len++] |= ((s->brg_cfg) & 0xff); - s->brg_commit = 0; - } - spin_unlock_irqrestore(&s->data_lock, flags); - - if (len > 0) { - if (max3107_rw(s, (u8 *)buf, NULL, len * 2)) - dev_err(&s->spi->dev, - "SPI transfer config failed\n"); - } - - /* Reloop if interrupt handling indicated data in RX FIFO */ - } while (rxlvl); - -} - -/* Set sleep mode */ -static void max3107_set_sleep(struct max3107_port *s, int mode) -{ - u16 buf[1]; /* Buffer for SPI transfer */ - unsigned long flags; - pr_debug("enter, mode %d\n", mode); - - buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); - spin_lock_irqsave(&s->data_lock, flags); - switch (mode) { - case MAX3107_DISABLE_FORCED_SLEEP: - s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT; - break; - case MAX3107_ENABLE_FORCED_SLEEP: - s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT; - break; - case MAX3107_DISABLE_AUTOSLEEP: - s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT; - break; - case MAX3107_ENABLE_AUTOSLEEP: - s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT; - break; - default: - spin_unlock_irqrestore(&s->data_lock, flags); - dev_warn(&s->spi->dev, "invalid sleep mode\n"); - return; - } - buf[0] |= s->mode1_reg; - spin_unlock_irqrestore(&s->data_lock, flags); - - if (max3107_rw(s, (u8 *)buf, NULL, 2)) - dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n"); - - if (mode == MAX3107_DISABLE_AUTOSLEEP || - mode == MAX3107_DISABLE_FORCED_SLEEP) - msleep(MAX3107_WAKEUP_DELAY); -} - -/* Perform full register initialization */ -static void max3107_register_init(struct max3107_port *s) -{ - u16 buf[11]; /* Buffer for SPI transfers */ - - /* 1. Configure baud rate, 9600 as default */ - s->baud = 9600; - /* the below is default*/ - if (s->ext_clk) { - s->brg_cfg = MAX3107_BRG26_B9600; - s->baud_tbl = (struct baud_table *)brg26_ext; - } else { - s->brg_cfg = MAX3107_BRG13_IB9600; - s->baud_tbl = (struct baud_table *)brg13_int; - } - - if (s->pdata->init) - s->pdata->init(s); - - buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG) - | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK); - buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG) - | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK); - buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG) - | ((s->brg_cfg) & 0xff); - - /* 2. Configure LCR register, 8N1 mode by default */ - s->lcr_reg = MAX3107_LCR_WORD_LEN_8; - buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG) - | s->lcr_reg; - - /* 3. Configure MODE 1 register */ - s->mode1_reg = 0; - /* Enable IRQ pin */ - s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT; - /* Disable TX */ - s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; - s->tx_enabled = 0; - /* RX is enabled */ - s->rx_enabled = 1; - buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG) - | s->mode1_reg; - - /* 4. Configure MODE 2 register */ - buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); - if (s->loopback) { - /* Enable loopback */ - buf[5] |= MAX3107_MODE2_LOOPBACK_BIT; - } - /* Reset FIFOs */ - buf[5] |= MAX3107_MODE2_FIFORST_BIT; - s->tx_fifo_empty = 1; - - /* 5. Configure FIFO trigger level register */ - buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG); - /* RX FIFO trigger for 16 words, TX FIFO trigger not used */ - buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0)); - - /* 6. Configure flow control levels */ - buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG); - /* Flow control halt level 96, resume level 48 */ - buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96)); - - /* 7. Configure flow control */ - buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG); - /* Enable auto CTS and auto RTS flow control */ - buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT); - - /* 8. Configure RX timeout register */ - buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG); - /* Timeout after 48 character intervals */ - buf[9] |= 0x0030; - - /* 9. Configure LSR interrupt enable register */ - buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG); - /* Enable RX timeout interrupt */ - buf[10] |= MAX3107_LSR_RXTO_BIT; - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 22)) - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - - /* 10. Clear IRQ status register by reading it */ - buf[0] = MAX3107_IRQSTS_REG; - - /* 11. Configure interrupt enable register */ - /* Enable LSR interrupt */ - s->irqen_reg = MAX3107_IRQ_LSR_BIT; - /* Enable RX FIFO interrupt */ - s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; - buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG) - | s->irqen_reg; - - /* 12. Clear FIFO reset that was set in step 6 */ - buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); - if (s->loopback) { - /* Keep loopback enabled */ - buf[2] |= MAX3107_MODE2_LOOPBACK_BIT; - } - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6)) - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - -} - -/* IRQ handler */ -static irqreturn_t max3107_irq(int irqno, void *dev_id) -{ - struct max3107_port *s = dev_id; - - if (irqno != s->spi->irq) { - /* Unexpected IRQ */ - return IRQ_NONE; - } - - /* Indicate irq */ - s->handle_irq = 1; - - /* Trigger work thread */ - max3107_dowork(s); - - return IRQ_HANDLED; -} - -/* HW suspension function - * - * Currently autosleep is used to decrease current consumption, alternative - * approach would be to set the chip to reset mode if UART is not being - * used but that would mess the GPIOs - * - */ -void max3107_hw_susp(struct max3107_port *s, int suspend) -{ - pr_debug("enter, suspend %d\n", suspend); - - if (suspend) { - /* Suspend requested, - * enable autosleep to decrease current consumption - */ - s->suspended = 1; - max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP); - } else { - /* Resume requested, - * disable autosleep - */ - s->suspended = 0; - max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP); - } -} -EXPORT_SYMBOL_GPL(max3107_hw_susp); - -/* Modem status IRQ enabling */ -static void max3107_enable_ms(struct uart_port *port) -{ - /* Modem status not supported */ -} - -/* Data send function */ -static void max3107_start_tx(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - /* Trigger work thread for sending data */ - max3107_dowork(s); -} - -/* Function for checking that there is no pending transfers */ -static unsigned int max3107_tx_empty(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - pr_debug("returning %d\n", - (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit))); - return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit); -} - -/* Function for stopping RX */ -static void max3107_stop_rx(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - unsigned long flags; - - /* Set RX disabled in MODE 1 register */ - spin_lock_irqsave(&s->data_lock, flags); - s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT; - s->mode1_commit = 1; - spin_unlock_irqrestore(&s->data_lock, flags); - /* Set RX disabled */ - s->rx_enabled = 0; - /* Trigger work thread for doing the actual configuration change */ - max3107_dowork(s); -} - -/* Function for returning control pin states */ -static unsigned int max3107_get_mctrl(struct uart_port *port) -{ - /* DCD and DSR are not wired and CTS/RTS is handled automatically - * so just indicate DSR and CAR asserted - */ - return TIOCM_DSR | TIOCM_CAR; -} - -/* Function for setting control pin states */ -static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* DCD and DSR are not wired and CTS/RTS is hadnled automatically - * so do nothing - */ -} - -/* Function for configuring UART parameters */ -static void max3107_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - struct tty_struct *tty; - int baud; - u16 new_lcr = 0; - u32 new_brg = 0; - unsigned long flags; - - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - /* Get new LCR register values */ - /* Word size */ - if ((termios->c_cflag & CSIZE) == CS7) - new_lcr |= MAX3107_LCR_WORD_LEN_7; - else - new_lcr |= MAX3107_LCR_WORD_LEN_8; - - /* Parity */ - if (termios->c_cflag & PARENB) { - new_lcr |= MAX3107_LCR_PARITY_BIT; - if (!(termios->c_cflag & PARODD)) - new_lcr |= MAX3107_LCR_EVENPARITY_BIT; - } - - /* Stop bits */ - if (termios->c_cflag & CSTOPB) { - /* 2 stop bits */ - new_lcr |= MAX3107_LCR_STOPLEN_BIT; - } - - /* Mask termios capabilities we don't support */ - termios->c_cflag &= ~CMSPAR; - - /* Set status ignore mask */ - s->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - s->port.ignore_status_mask |= MAX3107_ALL_ERRORS; - - /* Set low latency to immediately handle pushed data */ - s->port.state->port.tty->low_latency = 1; - - /* Get new baud rate generator configuration */ - baud = tty_get_baud_rate(tty); - - spin_lock_irqsave(&s->data_lock, flags); - new_brg = get_new_brg(baud, s); - /* if can't find the corrent config, use previous */ - if (!new_brg) { - baud = s->baud; - new_brg = s->brg_cfg; - } - spin_unlock_irqrestore(&s->data_lock, flags); - tty_termios_encode_baud_rate(termios, baud, baud); - s->baud = baud; - - /* Update timeout according to new baud rate */ - uart_update_timeout(port, termios->c_cflag, baud); - - spin_lock_irqsave(&s->data_lock, flags); - if (s->lcr_reg != new_lcr) { - s->lcr_reg = new_lcr; - s->lcr_commit = 1; - } - if (s->brg_cfg != new_brg) { - s->brg_cfg = new_brg; - s->brg_commit = 1; - } - spin_unlock_irqrestore(&s->data_lock, flags); - - /* Trigger work thread for doing the actual configuration change */ - max3107_dowork(s); -} - -/* Port shutdown function */ -static void max3107_shutdown(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - if (s->suspended && s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Free the interrupt */ - free_irq(s->spi->irq, s); - - if (s->workqueue) { - /* Flush and destroy work queue */ - flush_workqueue(s->workqueue); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - } - - /* Suspend HW */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 1); -} - -/* Port startup function */ -static int max3107_startup(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - - /* Initialize work queue */ - s->workqueue = create_freezeable_workqueue("max3107"); - if (!s->workqueue) { - dev_err(&s->spi->dev, "Workqueue creation failed\n"); - return -EBUSY; - } - INIT_WORK(&s->work, max3107_work); - - /* Setup IRQ */ - if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING, - "max3107", s)) { - dev_err(&s->spi->dev, "IRQ reguest failed\n"); - destroy_workqueue(s->workqueue); - s->workqueue = NULL; - return -EBUSY; - } - - /* Resume HW */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Init registers */ - max3107_register_init(s); - - return 0; -} - -/* Port type function */ -static const char *max3107_type(struct uart_port *port) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - return s->spi->modalias; -} - -/* Port release function */ -static void max3107_release_port(struct uart_port *port) -{ - /* Do nothing */ -} - -/* Port request function */ -static int max3107_request_port(struct uart_port *port) -{ - /* Do nothing */ - return 0; -} - -/* Port config function */ -static void max3107_config_port(struct uart_port *port, int flags) -{ - struct max3107_port *s = container_of(port, struct max3107_port, port); - s->port.type = PORT_MAX3107; -} - -/* Port verify function */ -static int max3107_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107) - return 0; - - return -EINVAL; -} - -/* Port stop TX function */ -static void max3107_stop_tx(struct uart_port *port) -{ - /* Do nothing */ -} - -/* Port break control function */ -static void max3107_break_ctl(struct uart_port *port, int break_state) -{ - /* We don't support break control, do nothing */ -} - - -/* Port functions */ -static struct uart_ops max3107_ops = { - .tx_empty = max3107_tx_empty, - .set_mctrl = max3107_set_mctrl, - .get_mctrl = max3107_get_mctrl, - .stop_tx = max3107_stop_tx, - .start_tx = max3107_start_tx, - .stop_rx = max3107_stop_rx, - .enable_ms = max3107_enable_ms, - .break_ctl = max3107_break_ctl, - .startup = max3107_startup, - .shutdown = max3107_shutdown, - .set_termios = max3107_set_termios, - .type = max3107_type, - .release_port = max3107_release_port, - .request_port = max3107_request_port, - .config_port = max3107_config_port, - .verify_port = max3107_verify_port, -}; - -/* UART driver data */ -static struct uart_driver max3107_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "ttyMAX", - .dev_name = "ttyMAX", - .nr = 1, -}; - -static int driver_registered = 0; - - - -/* 'Generic' platform data */ -static struct max3107_plat generic_plat_data = { - .loopback = 0, - .ext_clk = 1, - .hw_suspend = max3107_hw_susp, - .polled_mode = 0, - .poll_time = 0, -}; - - -/*******************************************************************/ - -/** - * max3107_probe - SPI bus probe entry point - * @spi: the spi device - * - * SPI wants us to probe this device and if appropriate claim it. - * Perform any platform specific requirements and then initialise - * the device. - */ - -int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata) -{ - struct max3107_port *s; - u16 buf[2]; /* Buffer for SPI transfers */ - int retval; - - pr_info("enter max3107 probe\n"); - - /* Allocate port structure */ - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (!s) { - pr_err("Allocating port structure failed\n"); - return -ENOMEM; - } - - s->pdata = pdata; - - /* SPI Rx buffer - * +2 for RX FIFO interrupt - * disabling and RX level query - */ - s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL); - if (!s->rxbuf) { - pr_err("Allocating RX buffer failed\n"); - retval = -ENOMEM; - goto err_free4; - } - s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL); - if (!s->rxstr) { - pr_err("Allocating RX buffer failed\n"); - retval = -ENOMEM; - goto err_free3; - } - /* SPI Tx buffer - * SPI transfer buffer - * +3 for TX FIFO empty - * interrupt disabling and - * enabling and TX enabling - */ - s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL); - if (!s->txbuf) { - pr_err("Allocating TX buffer failed\n"); - retval = -ENOMEM; - goto err_free2; - } - /* Initialize shared data lock */ - spin_lock_init(&s->data_lock); - - /* SPI intializations */ - dev_set_drvdata(&spi->dev, s); - spi->mode = SPI_MODE_0; - spi->dev.platform_data = pdata; - spi->bits_per_word = 16; - s->ext_clk = pdata->ext_clk; - s->loopback = pdata->loopback; - spi_setup(spi); - s->spi = spi; - - /* Check REV ID to ensure we are talking to what we expect */ - buf[0] = MAX3107_REVID_REG; - if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { - dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n"); - retval = -EIO; - goto err_free1; - } - if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 && - (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) { - dev_err(&s->spi->dev, "REVID %x does not match\n", - (buf[0] & MAX3107_SPI_RX_DATA_MASK)); - retval = -ENODEV; - goto err_free1; - } - - /* Disable all interrupts */ - buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000); - buf[0] |= 0x0000; - - /* Configure clock source */ - buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG); - if (s->ext_clk) { - /* External clock */ - buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT; - } - - /* PLL bypass ON */ - buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT; - - /* Perform SPI transfer */ - if (max3107_rw(s, (u8 *)buf, NULL, 4)) { - dev_err(&s->spi->dev, "SPI transfer for init failed\n"); - retval = -EIO; - goto err_free1; - } - - /* Register UART driver */ - if (!driver_registered) { - retval = uart_register_driver(&max3107_uart_driver); - if (retval) { - dev_err(&s->spi->dev, "Registering UART driver failed\n"); - goto err_free1; - } - driver_registered = 1; - } - - /* Initialize UART port data */ - s->port.fifosize = 128; - s->port.ops = &max3107_ops; - s->port.line = 0; - s->port.dev = &spi->dev; - s->port.uartclk = 9600; - s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; - s->port.irq = s->spi->irq; - s->port.type = PORT_MAX3107; - - /* Add UART port */ - retval = uart_add_one_port(&max3107_uart_driver, &s->port); - if (retval < 0) { - dev_err(&s->spi->dev, "Adding UART port failed\n"); - goto err_free1; - } - - if (pdata->configure) { - retval = pdata->configure(s); - if (retval < 0) - goto err_free1; - } - - /* Go to suspend mode */ - if (pdata->hw_suspend) - pdata->hw_suspend(s, 1); - - return 0; - -err_free1: - kfree(s->txbuf); -err_free2: - kfree(s->rxstr); -err_free3: - kfree(s->rxbuf); -err_free4: - kfree(s); - return retval; -} -EXPORT_SYMBOL_GPL(max3107_probe); - -/* Driver remove function */ -int max3107_remove(struct spi_device *spi) -{ - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_info("enter max3107 remove\n"); - - /* Remove port */ - if (uart_remove_one_port(&max3107_uart_driver, &s->port)) - dev_warn(&s->spi->dev, "Removing UART port failed\n"); - - - /* Free TxRx buffer */ - kfree(s->rxbuf); - kfree(s->rxstr); - kfree(s->txbuf); - - /* Free port structure */ - kfree(s); - - return 0; -} -EXPORT_SYMBOL_GPL(max3107_remove); - -/* Driver suspend function */ -int max3107_suspend(struct spi_device *spi, pm_message_t state) -{ -#ifdef CONFIG_PM - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_debug("enter suspend\n"); - - /* Suspend UART port */ - uart_suspend_port(&max3107_uart_driver, &s->port); - - /* Go to suspend mode */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 1); -#endif /* CONFIG_PM */ - return 0; -} -EXPORT_SYMBOL_GPL(max3107_suspend); - -/* Driver resume function */ -int max3107_resume(struct spi_device *spi) -{ -#ifdef CONFIG_PM - struct max3107_port *s = dev_get_drvdata(&spi->dev); - - pr_debug("enter resume\n"); - - /* Resume from suspend */ - if (s->pdata->hw_suspend) - s->pdata->hw_suspend(s, 0); - - /* Resume UART port */ - uart_resume_port(&max3107_uart_driver, &s->port); -#endif /* CONFIG_PM */ - return 0; -} -EXPORT_SYMBOL_GPL(max3107_resume); - -static int max3107_probe_generic(struct spi_device *spi) -{ - return max3107_probe(spi, &generic_plat_data); -} - -/* Spi driver data */ -static struct spi_driver max3107_driver = { - .driver = { - .name = "max3107", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = max3107_probe_generic, - .remove = __devexit_p(max3107_remove), - .suspend = max3107_suspend, - .resume = max3107_resume, -}; - -/* Driver init function */ -static int __init max3107_init(void) -{ - pr_info("enter max3107 init\n"); - return spi_register_driver(&max3107_driver); -} - -/* Driver exit function */ -static void __exit max3107_exit(void) -{ - pr_info("enter max3107 exit\n"); - /* Unregister UART driver */ - if (driver_registered) - uart_unregister_driver(&max3107_uart_driver); - spi_unregister_driver(&max3107_driver); -} - -module_init(max3107_init); -module_exit(max3107_exit); - -MODULE_DESCRIPTION("MAX3107 driver"); -MODULE_AUTHOR("Aavamobile"); -MODULE_ALIAS("max3107-spi"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/serial/max3107.h b/drivers/serial/max3107.h deleted file mode 100644 index 7ab632392502..000000000000 --- a/drivers/serial/max3107.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - * max3107.h - spi uart protocol driver header for Maxim 3107 - * - * Copyright (C) Aavamobile 2009 - * Based on serial_max3100.h by Christian Pellegrin - * - * 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 _MAX3107_H -#define _MAX3107_H - -/* Serial error status definitions */ -#define MAX3107_PARITY_ERROR 1 -#define MAX3107_FRAME_ERROR 2 -#define MAX3107_OVERRUN_ERROR 4 -#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \ - MAX3107_FRAME_ERROR | \ - MAX3107_OVERRUN_ERROR) - -/* GPIO definitions */ -#define MAX3107_GPIO_BASE 88 -#define MAX3107_GPIO_COUNT 4 - - -/* GPIO connected to chip's reset pin */ -#define MAX3107_RESET_GPIO 87 - - -/* Chip reset delay */ -#define MAX3107_RESET_DELAY 10 - -/* Chip wakeup delay */ -#define MAX3107_WAKEUP_DELAY 50 - - -/* Sleep mode definitions */ -#define MAX3107_DISABLE_FORCED_SLEEP 0 -#define MAX3107_ENABLE_FORCED_SLEEP 1 -#define MAX3107_DISABLE_AUTOSLEEP 2 -#define MAX3107_ENABLE_AUTOSLEEP 3 - - -/* Definitions for register access with SPI transfers - * - * SPI transfer format: - * - * Master to slave bits xzzzzzzzyyyyyyyy - * Slave to master bits aaaaaaaabbbbbbbb - * - * where: - * x = 0 for reads, 1 for writes - * z = register address - * y = new register value if write, 0 if read - * a = unspecified - * b = register value if read, unspecified if write - */ - -/* SPI speed */ -#define MAX3107_SPI_SPEED (3125000 * 2) - -/* Write bit */ -#define MAX3107_WRITE_BIT (1 << 15) - -/* SPI TX data mask */ -#define MAX3107_SPI_RX_DATA_MASK (0x00ff) - -/* SPI RX data mask */ -#define MAX3107_SPI_TX_DATA_MASK (0x00ff) - -/* Register access masks */ -#define MAX3107_RHR_REG (0x0000) /* RX FIFO */ -#define MAX3107_THR_REG (0x0000) /* TX FIFO */ -#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */ -#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */ -#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */ -#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */ -#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */ -#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */ -#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */ -#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */ -#define MAX3107_MODE1_REG (0x0900) /* MODE1 */ -#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */ -#define MAX3107_LCR_REG (0x0b00) /* LCR */ -#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */ -#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */ -#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */ -#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */ -#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */ -#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */ -#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */ -#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */ -#define MAX3107_XON1_REG (0x1400) /* XON1 character */ -#define MAX3107_XON2_REG (0x1500) /* XON2 character */ -#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */ -#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */ -#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */ -#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */ -#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */ -#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */ -#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */ -#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */ -#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */ -#define MAX3107_REVID_REG (0x1f00) /* Revision identification */ - -/* IRQ register bits */ -#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ -#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ -#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ -#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ -#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ -#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ -#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ -#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ - -/* LSR register bits */ -#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ -#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ -#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ -#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ -#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ -#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ -#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ - -/* Special character register bits */ -#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ -#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ -#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ -#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ -#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ -#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ -#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Status register bits */ -#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ -#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ -#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ -#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ -#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */ -#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ -#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ -#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* MODE1 register bits */ -#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ -#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ -#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ -#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ -#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ -#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ -#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ -#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ - -/* MODE2 register bits */ -#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ -#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ -#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ -#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ -#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ -#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ -#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ -#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ - -/* LCR register bits */ -#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ -#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 - * - * Word length bits table: - * 00 -> 5 bit words - * 01 -> 6 bit words - * 10 -> 7 bit words - * 11 -> 8 bit words - */ -#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit - * - * STOP length bit table: - * 0 -> 1 stop bit - * 1 -> 1-1.5 stop bits if - * word length is 5, - * 2 stop bits otherwise - */ -#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ -#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ -#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ -#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ -#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ -#define MAX3107_LCR_WORD_LEN_5 (0x0000) -#define MAX3107_LCR_WORD_LEN_6 (0x0001) -#define MAX3107_LCR_WORD_LEN_7 (0x0002) -#define MAX3107_LCR_WORD_LEN_8 (0x0003) - - -/* IRDA register bits */ -#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ -#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ -#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ -#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ -#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ -#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ -#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Flow control trigger level register masks */ -#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ -#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ -#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f) -#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4) - -/* FIFO interrupt trigger level register masks */ -#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */ -#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */ -#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f) -#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4) - -/* Flow control register bits */ -#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ -#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ -#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs - * are used in conjunction with - * XOFF2 for definition of - * special character */ -#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ -#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ -#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 - * - * SWFLOW bits 1 & 0 table: - * 00 -> no transmitter flow - * control - * 01 -> receiver compares - * XON2 and XOFF2 - * and controls - * transmitter - * 10 -> receiver compares - * XON1 and XOFF1 - * and controls - * transmitter - * 11 -> receiver compares - * XON1, XON2, XOFF1 and - * XOFF2 and controls - * transmitter - */ -#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ -#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 - * - * SWFLOW bits 3 & 2 table: - * 00 -> no received flow - * control - * 01 -> transmitter generates - * XON2 and XOFF2 - * 10 -> transmitter generates - * XON1 and XOFF1 - * 11 -> transmitter generates - * XON1, XON2, XOFF1 and - * XOFF2 - */ - -/* GPIO configuration register bits */ -#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ -#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ -#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ -#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ -#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ -#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ -#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ -#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ - -/* GPIO DATA register bits */ -#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ -#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ -#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ -#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ -#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ -#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ -#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ -#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ - -/* PLL configuration register masks */ -#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */ -#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */ - -/* Baud rate generator configuration register masks and bits */ -#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of - * Baud rate generator divisor - */ -#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ -#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ -#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */ - -/* Clock source register bits */ -#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */ -#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ -#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ -#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ -#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ -#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */ -#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */ -#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ - - -/* HW definitions */ -#define MAX3107_RX_FIFO_SIZE 128 -#define MAX3107_TX_FIFO_SIZE 128 -#define MAX3107_REVID1 0x00a0 -#define MAX3107_REVID2 0x00a1 - - -/* Baud rate generator configuration values for external clock 13MHz */ -#define MAX3107_BRG13_B300 (0x0A9400 | 0x05) -#define MAX3107_BRG13_B600 (0x054A00 | 0x03) -#define MAX3107_BRG13_B1200 (0x02A500 | 0x01) -#define MAX3107_BRG13_B2400 (0x015200 | 0x09) -#define MAX3107_BRG13_B4800 (0x00A900 | 0x04) -#define MAX3107_BRG13_B9600 (0x005400 | 0x0A) -#define MAX3107_BRG13_B19200 (0x002A00 | 0x05) -#define MAX3107_BRG13_B38400 (0x001500 | 0x03) -#define MAX3107_BRG13_B57600 (0x000E00 | 0x02) -#define MAX3107_BRG13_B115200 (0x000700 | 0x01) -#define MAX3107_BRG13_B230400 (0x000300 | 0x08) -#define MAX3107_BRG13_B460800 (0x000100 | 0x0c) -#define MAX3107_BRG13_B921600 (0x000100 | 0x1c) - -/* Baud rate generator configuration values for external clock 26MHz */ -#define MAX3107_BRG26_B300 (0x152800 | 0x0A) -#define MAX3107_BRG26_B600 (0x0A9400 | 0x05) -#define MAX3107_BRG26_B1200 (0x054A00 | 0x03) -#define MAX3107_BRG26_B2400 (0x02A500 | 0x01) -#define MAX3107_BRG26_B4800 (0x015200 | 0x09) -#define MAX3107_BRG26_B9600 (0x00A900 | 0x04) -#define MAX3107_BRG26_B19200 (0x005400 | 0x0A) -#define MAX3107_BRG26_B38400 (0x002A00 | 0x05) -#define MAX3107_BRG26_B57600 (0x001C00 | 0x03) -#define MAX3107_BRG26_B115200 (0x000E00 | 0x02) -#define MAX3107_BRG26_B230400 (0x000700 | 0x01) -#define MAX3107_BRG26_B460800 (0x000300 | 0x08) -#define MAX3107_BRG26_B921600 (0x000100 | 0x0C) - -/* Baud rate generator configuration values for internal clock */ -#define MAX3107_BRG13_IB300 (0x008000 | 0x00) -#define MAX3107_BRG13_IB600 (0x004000 | 0x00) -#define MAX3107_BRG13_IB1200 (0x002000 | 0x00) -#define MAX3107_BRG13_IB2400 (0x001000 | 0x00) -#define MAX3107_BRG13_IB4800 (0x000800 | 0x00) -#define MAX3107_BRG13_IB9600 (0x000400 | 0x00) -#define MAX3107_BRG13_IB19200 (0x000200 | 0x00) -#define MAX3107_BRG13_IB38400 (0x000100 | 0x00) -#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B) -#define MAX3107_BRG13_IB115200 (0x000000 | 0x05) -#define MAX3107_BRG13_IB230400 (0x000000 | 0x03) -#define MAX3107_BRG13_IB460800 (0x000000 | 0x00) -#define MAX3107_BRG13_IB921600 (0x000000 | 0x00) - - -struct baud_table { - int baud; - u32 new_brg; -}; - -struct max3107_port { - /* UART port structure */ - struct uart_port port; - - /* SPI device structure */ - struct spi_device *spi; - -#if defined(CONFIG_GPIOLIB) - /* GPIO chip stucture */ - struct gpio_chip chip; -#endif - - /* Workqueue that does all the magic */ - struct workqueue_struct *workqueue; - struct work_struct work; - - /* Lock for shared data */ - spinlock_t data_lock; - - /* Device configuration */ - int ext_clk; /* 1 if external clock used */ - int loopback; /* Current loopback mode state */ - int baud; /* Current baud rate */ - - /* State flags */ - int suspended; /* Indicates suspend mode */ - int tx_fifo_empty; /* Flag for TX FIFO state */ - int rx_enabled; /* Flag for receiver state */ - int tx_enabled; /* Flag for transmitter state */ - - u16 irqen_reg; /* Current IRQ enable register value */ - /* Shared data */ - u16 mode1_reg; /* Current mode1 register value*/ - int mode1_commit; /* Flag for setting new mode1 register value */ - u16 lcr_reg; /* Current LCR register value */ - int lcr_commit; /* Flag for setting new LCR register value */ - u32 brg_cfg; /* Current Baud rate generator config */ - int brg_commit; /* Flag for setting new baud rate generator - * config - */ - struct baud_table *baud_tbl; - int handle_irq; /* Indicates that IRQ should be handled */ - - /* Rx buffer and str*/ - u16 *rxbuf; - u8 *rxstr; - /* Tx buffer*/ - u16 *txbuf; - - struct max3107_plat *pdata; /* Platform data */ -}; - -/* Platform data structure */ -struct max3107_plat { - /* Loopback mode enable */ - int loopback; - /* External clock enable */ - int ext_clk; - /* Called during the register initialisation */ - void (*init)(struct max3107_port *s); - /* Called when the port is found and configured */ - int (*configure)(struct max3107_port *s); - /* HW suspend function */ - void (*hw_suspend) (struct max3107_port *s, int suspend); - /* Polling mode enable */ - int polled_mode; - /* Polling period if polling mode enabled */ - int poll_time; -}; - -extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len); -extern void max3107_hw_susp(struct max3107_port *s, int suspend); -extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata); -extern int max3107_remove(struct spi_device *spi); -extern int max3107_suspend(struct spi_device *spi, pm_message_t state); -extern int max3107_resume(struct spi_device *spi); - -#endif /* _LINUX_SERIAL_MAX3107_H */ diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c deleted file mode 100644 index 3394b7cc1722..000000000000 --- a/drivers/serial/mcf.c +++ /dev/null @@ -1,662 +0,0 @@ -/****************************************************************************/ - -/* - * mcf.c -- Freescale ColdFire UART driver - * - * (C) Copyright 2003-2007, Greg Ungerer - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -/****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/****************************************************************************/ - -/* - * Some boards implement the DTR/DCD lines using GPIO lines, most - * don't. Dummy out the access macros for those that don't. Those - * that do should define these macros somewhere in there board - * specific inlude files. - */ -#if !defined(mcf_getppdcd) -#define mcf_getppdcd(p) (1) -#endif -#if !defined(mcf_getppdtr) -#define mcf_getppdtr(p) (1) -#endif -#if !defined(mcf_setppdtr) -#define mcf_setppdtr(p, v) do { } while (0) -#endif - -/****************************************************************************/ - -/* - * Local per-uart structure. - */ -struct mcf_uart { - struct uart_port port; - unsigned int sigs; /* Local copy of line sigs */ - unsigned char imr; /* Local IMR mirror */ -}; - -/****************************************************************************/ - -static unsigned int mcf_tx_empty(struct uart_port *port) -{ - return (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXEMPTY) ? - TIOCSER_TEMT : 0; -} - -/****************************************************************************/ - -static unsigned int mcf_get_mctrl(struct uart_port *port) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned int sigs; - - sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ? - 0 : TIOCM_CTS; - sigs |= (pp->sigs & TIOCM_RTS); - sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0); - sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0); - - return sigs; -} - -/****************************************************************************/ - -static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - - pp->sigs = sigs; - mcf_setppdtr(port->line, (sigs & TIOCM_DTR)); - if (sigs & TIOCM_RTS) - writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); - else - writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0); -} - -/****************************************************************************/ - -static void mcf_start_tx(struct uart_port *port) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - - pp->imr |= MCFUART_UIR_TXREADY; - writeb(pp->imr, port->membase + MCFUART_UIMR); -} - -/****************************************************************************/ - -static void mcf_stop_tx(struct uart_port *port) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - - pp->imr &= ~MCFUART_UIR_TXREADY; - writeb(pp->imr, port->membase + MCFUART_UIMR); -} - -/****************************************************************************/ - -static void mcf_stop_rx(struct uart_port *port) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - - pp->imr &= ~MCFUART_UIR_RXREADY; - writeb(pp->imr, port->membase + MCFUART_UIMR); -} - -/****************************************************************************/ - -static void mcf_break_ctl(struct uart_port *port, int break_state) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - if (break_state == -1) - writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR); - else - writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR); - spin_unlock_irqrestore(&port->lock, flags); -} - -/****************************************************************************/ - -static void mcf_enable_ms(struct uart_port *port) -{ -} - -/****************************************************************************/ - -static int mcf_startup(struct uart_port *port) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - /* Reset UART, get it into known state... */ - writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); - writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); - - /* Enable the UART transmitter and receiver */ - writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, - port->membase + MCFUART_UCR); - - /* Enable RX interrupts now */ - pp->imr = MCFUART_UIR_RXREADY; - writeb(pp->imr, port->membase + MCFUART_UIMR); - - spin_unlock_irqrestore(&port->lock, flags); - - return 0; -} - -/****************************************************************************/ - -static void mcf_shutdown(struct uart_port *port) -{ - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - /* Disable all interrupts now */ - pp->imr = 0; - writeb(pp->imr, port->membase + MCFUART_UIMR); - - /* Disable UART transmitter and receiver */ - writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); - writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); - - spin_unlock_irqrestore(&port->lock, flags); -} - -/****************************************************************************/ - -static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - unsigned int baud, baudclk; -#if defined(CONFIG_M5272) - unsigned int baudfr; -#endif - unsigned char mr1, mr2; - - baud = uart_get_baud_rate(port, termios, old, 0, 230400); -#if defined(CONFIG_M5272) - baudclk = (MCF_BUSCLK / baud) / 32; - baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16; -#else - baudclk = ((MCF_BUSCLK / baud) + 16) / 32; -#endif - - mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; - mr2 = 0; - - switch (termios->c_cflag & CSIZE) { - case CS5: mr1 |= MCFUART_MR1_CS5; break; - case CS6: mr1 |= MCFUART_MR1_CS6; break; - case CS7: mr1 |= MCFUART_MR1_CS7; break; - case CS8: - default: mr1 |= MCFUART_MR1_CS8; break; - } - - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - mr1 |= MCFUART_MR1_PARITYMARK; - else - mr1 |= MCFUART_MR1_PARITYSPACE; - } else { - if (termios->c_cflag & PARODD) - mr1 |= MCFUART_MR1_PARITYODD; - else - mr1 |= MCFUART_MR1_PARITYEVEN; - } - } else { - mr1 |= MCFUART_MR1_PARITYNONE; - } - - if (termios->c_cflag & CSTOPB) - mr2 |= MCFUART_MR2_STOP2; - else - mr2 |= MCFUART_MR2_STOP1; - - if (termios->c_cflag & CRTSCTS) { - mr1 |= MCFUART_MR1_RXRTS; - mr2 |= MCFUART_MR2_TXCTS; - } - - spin_lock_irqsave(&port->lock, flags); - uart_update_timeout(port, termios->c_cflag, baud); - writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); - writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); - writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); - writeb(mr1, port->membase + MCFUART_UMR); - writeb(mr2, port->membase + MCFUART_UMR); - writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1); - writeb((baudclk & 0xff), port->membase + MCFUART_UBG2); -#if defined(CONFIG_M5272) - writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD); -#endif - writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER, - port->membase + MCFUART_UCSR); - writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, - port->membase + MCFUART_UCR); - spin_unlock_irqrestore(&port->lock, flags); -} - -/****************************************************************************/ - -static void mcf_rx_chars(struct mcf_uart *pp) -{ - struct uart_port *port = &pp->port; - unsigned char status, ch, flag; - - while ((status = readb(port->membase + MCFUART_USR)) & MCFUART_USR_RXREADY) { - ch = readb(port->membase + MCFUART_URB); - flag = TTY_NORMAL; - port->icount.rx++; - - if (status & MCFUART_USR_RXERR) { - writeb(MCFUART_UCR_CMDRESETERR, - port->membase + MCFUART_UCR); - - if (status & MCFUART_USR_RXBREAK) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (status & MCFUART_USR_RXPARITY) { - port->icount.parity++; - } else if (status & MCFUART_USR_RXOVERRUN) { - port->icount.overrun++; - } else if (status & MCFUART_USR_RXFRAMING) { - port->icount.frame++; - } - - status &= port->read_status_mask; - - if (status & MCFUART_USR_RXBREAK) - flag = TTY_BREAK; - else if (status & MCFUART_USR_RXPARITY) - flag = TTY_PARITY; - else if (status & MCFUART_USR_RXFRAMING) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - continue; - uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); - } - - tty_flip_buffer_push(port->state->port.tty); -} - -/****************************************************************************/ - -static void mcf_tx_chars(struct mcf_uart *pp) -{ - struct uart_port *port = &pp->port; - struct circ_buf *xmit = &port->state->xmit; - - if (port->x_char) { - /* Send special char - probably flow control */ - writeb(port->x_char, port->membase + MCFUART_UTB); - port->x_char = 0; - port->icount.tx++; - return; - } - - while (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) { - if (xmit->head == xmit->tail) - break; - writeb(xmit->buf[xmit->tail], port->membase + MCFUART_UTB); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); - port->icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (xmit->head == xmit->tail) { - pp->imr &= ~MCFUART_UIR_TXREADY; - writeb(pp->imr, port->membase + MCFUART_UIMR); - } -} - -/****************************************************************************/ - -static irqreturn_t mcf_interrupt(int irq, void *data) -{ - struct uart_port *port = data; - struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned int isr; - irqreturn_t ret = IRQ_NONE; - - isr = readb(port->membase + MCFUART_UISR) & pp->imr; - - spin_lock(&port->lock); - if (isr & MCFUART_UIR_RXREADY) { - mcf_rx_chars(pp); - ret = IRQ_HANDLED; - } - if (isr & MCFUART_UIR_TXREADY) { - mcf_tx_chars(pp); - ret = IRQ_HANDLED; - } - spin_unlock(&port->lock); - - return ret; -} - -/****************************************************************************/ - -static void mcf_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_MCF; - port->fifosize = MCFUART_TXFIFOSIZE; - - /* Clear mask, so no surprise interrupts. */ - writeb(0, port->membase + MCFUART_UIMR); - - if (request_irq(port->irq, mcf_interrupt, IRQF_DISABLED, "UART", port)) - printk(KERN_ERR "MCF: unable to attach ColdFire UART %d " - "interrupt vector=%d\n", port->line, port->irq); -} - -/****************************************************************************/ - -static const char *mcf_type(struct uart_port *port) -{ - return (port->type == PORT_MCF) ? "ColdFire UART" : NULL; -} - -/****************************************************************************/ - -static int mcf_request_port(struct uart_port *port) -{ - /* UARTs always present */ - return 0; -} - -/****************************************************************************/ - -static void mcf_release_port(struct uart_port *port) -{ - /* Nothing to release... */ -} - -/****************************************************************************/ - -static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_MCF)) - return -EINVAL; - return 0; -} - -/****************************************************************************/ - -/* - * Define the basic serial functions we support. - */ -static const struct uart_ops mcf_uart_ops = { - .tx_empty = mcf_tx_empty, - .get_mctrl = mcf_get_mctrl, - .set_mctrl = mcf_set_mctrl, - .start_tx = mcf_start_tx, - .stop_tx = mcf_stop_tx, - .stop_rx = mcf_stop_rx, - .enable_ms = mcf_enable_ms, - .break_ctl = mcf_break_ctl, - .startup = mcf_startup, - .shutdown = mcf_shutdown, - .set_termios = mcf_set_termios, - .type = mcf_type, - .request_port = mcf_request_port, - .release_port = mcf_release_port, - .config_port = mcf_config_port, - .verify_port = mcf_verify_port, -}; - -static struct mcf_uart mcf_ports[4]; - -#define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) - -/****************************************************************************/ -#if defined(CONFIG_SERIAL_MCF_CONSOLE) -/****************************************************************************/ - -int __init early_mcf_setup(struct mcf_platform_uart *platp) -{ - struct uart_port *port; - int i; - - for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { - port = &mcf_ports[i].port; - - port->line = i; - port->type = PORT_MCF; - port->mapbase = platp[i].mapbase; - port->membase = (platp[i].membase) ? platp[i].membase : - (unsigned char __iomem *) port->mapbase; - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->uartclk = MCF_BUSCLK; - port->flags = ASYNC_BOOT_AUTOCONF; - port->ops = &mcf_uart_ops; - } - - return 0; -} - -/****************************************************************************/ - -static void mcf_console_putc(struct console *co, const char c) -{ - struct uart_port *port = &(mcf_ports + co->index)->port; - int i; - - for (i = 0; (i < 0x10000); i++) { - if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) - break; - } - writeb(c, port->membase + MCFUART_UTB); - for (i = 0; (i < 0x10000); i++) { - if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) - break; - } -} - -/****************************************************************************/ - -static void mcf_console_write(struct console *co, const char *s, unsigned int count) -{ - for (; (count); count--, s++) { - mcf_console_putc(co, *s); - if (*s == '\n') - mcf_console_putc(co, '\r'); - } -} - -/****************************************************************************/ - -static int __init mcf_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = CONFIG_SERIAL_MCF_BAUDRATE; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if ((co->index < 0) || (co->index >= MCF_MAXPORTS)) - co->index = 0; - port = &mcf_ports[co->index].port; - if (port->membase == 0) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -/****************************************************************************/ - -static struct uart_driver mcf_driver; - -static struct console mcf_console = { - .name = "ttyS", - .write = mcf_console_write, - .device = uart_console_device, - .setup = mcf_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &mcf_driver, -}; - -static int __init mcf_console_init(void) -{ - register_console(&mcf_console); - return 0; -} - -console_initcall(mcf_console_init); - -#define MCF_CONSOLE &mcf_console - -/****************************************************************************/ -#else -/****************************************************************************/ - -#define MCF_CONSOLE NULL - -/****************************************************************************/ -#endif /* CONFIG_MCF_CONSOLE */ -/****************************************************************************/ - -/* - * Define the mcf UART driver structure. - */ -static struct uart_driver mcf_driver = { - .owner = THIS_MODULE, - .driver_name = "mcf", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = MCF_MAXPORTS, - .cons = MCF_CONSOLE, -}; - -/****************************************************************************/ - -static int __devinit mcf_probe(struct platform_device *pdev) -{ - struct mcf_platform_uart *platp = pdev->dev.platform_data; - struct uart_port *port; - int i; - - for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { - port = &mcf_ports[i].port; - - port->line = i; - port->type = PORT_MCF; - port->mapbase = platp[i].mapbase; - port->membase = (platp[i].membase) ? platp[i].membase : - (unsigned char __iomem *) platp[i].mapbase; - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->uartclk = MCF_BUSCLK; - port->ops = &mcf_uart_ops; - port->flags = ASYNC_BOOT_AUTOCONF; - - uart_add_one_port(&mcf_driver, port); - } - - return 0; -} - -/****************************************************************************/ - -static int __devexit mcf_remove(struct platform_device *pdev) -{ - struct uart_port *port; - int i; - - for (i = 0; (i < MCF_MAXPORTS); i++) { - port = &mcf_ports[i].port; - if (port) - uart_remove_one_port(&mcf_driver, port); - } - - return 0; -} - -/****************************************************************************/ - -static struct platform_driver mcf_platform_driver = { - .probe = mcf_probe, - .remove = __devexit_p(mcf_remove), - .driver = { - .name = "mcfuart", - .owner = THIS_MODULE, - }, -}; - -/****************************************************************************/ - -static int __init mcf_init(void) -{ - int rc; - - printk("ColdFire internal UART serial driver\n"); - - rc = uart_register_driver(&mcf_driver); - if (rc) - return rc; - rc = platform_driver_register(&mcf_platform_driver); - if (rc) - return rc; - return 0; -} - -/****************************************************************************/ - -static void __exit mcf_exit(void) -{ - platform_driver_unregister(&mcf_platform_driver); - uart_unregister_driver(&mcf_driver); -} - -/****************************************************************************/ - -module_init(mcf_init); -module_exit(mcf_exit); - -MODULE_AUTHOR("Greg Ungerer "); -MODULE_DESCRIPTION("Freescale ColdFire UART driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:mcfuart"); - -/****************************************************************************/ diff --git a/drivers/serial/mfd.c b/drivers/serial/mfd.c deleted file mode 100644 index d40010a22ecd..000000000000 --- a/drivers/serial/mfd.c +++ /dev/null @@ -1,1513 +0,0 @@ -/* - * mfd.c: driver for High Speed UART device of Intel Medfield platform - * - * Refer pxa.c, 8250.c and some other drivers in drivers/serial/ - * - * (C) Copyright 2010 Intel Corporation - * - * 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; version 2 - * of the License. - */ - -/* Notes: - * 1. DMA channel allocation: 0/1 channel are assigned to port 0, - * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans - * are used for RX, odd chans for TX - * - * 2. In A0 stepping, UART will not support TX half empty flag - * - * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always - * asserted, only when the HW is reset the DDCD and DDSR will - * be triggered - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MFD_HSU_A0_STEPPING 1 - -#define HSU_DMA_BUF_SIZE 2048 - -#define chan_readl(chan, offset) readl(chan->reg + offset) -#define chan_writel(chan, offset, val) writel(val, chan->reg + offset) - -#define mfd_readl(obj, offset) readl(obj->reg + offset) -#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) - -#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10) - -struct hsu_dma_buffer { - u8 *buf; - dma_addr_t dma_addr; - u32 dma_size; - u32 ofs; -}; - -struct hsu_dma_chan { - u32 id; - enum dma_data_direction dirt; - struct uart_hsu_port *uport; - void __iomem *reg; - struct timer_list rx_timer; /* only needed by RX channel */ -}; - -struct uart_hsu_port { - struct uart_port port; - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned int lsr_break_flag; - char name[12]; - int index; - struct device *dev; - - struct hsu_dma_chan *txc; - struct hsu_dma_chan *rxc; - struct hsu_dma_buffer txbuf; - struct hsu_dma_buffer rxbuf; - int use_dma; /* flag for DMA/PIO */ - int running; - int dma_tx_on; -}; - -/* Top level data structure of HSU */ -struct hsu_port { - void __iomem *reg; - unsigned long paddr; - unsigned long iolen; - u32 irq; - - struct uart_hsu_port port[3]; - struct hsu_dma_chan chans[10]; - - struct dentry *debugfs; -}; - -static inline unsigned int serial_in(struct uart_hsu_port *up, int offset) -{ - unsigned int val; - - if (offset > UART_MSR) { - offset <<= 2; - val = readl(up->port.membase + offset); - } else - val = (unsigned int)readb(up->port.membase + offset); - - return val; -} - -static inline void serial_out(struct uart_hsu_port *up, int offset, int value) -{ - if (offset > UART_MSR) { - offset <<= 2; - writel(value, up->port.membase + offset); - } else { - unsigned char val = value & 0xff; - writeb(val, up->port.membase + offset); - } -} - -#ifdef CONFIG_DEBUG_FS - -#define HSU_REGS_BUFSIZE 1024 - -static int hsu_show_regs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t port_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct uart_hsu_port *up = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MFD HSU port[%d] regs:\n", up->index); - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "=================================\n"); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "IER: \t\t0x%08x\n", serial_in(up, UART_IER)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "PS: \t\t0x%08x\n", serial_in(up, UART_PS)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV)); - - if (len > HSU_REGS_BUFSIZE) - len = HSU_REGS_BUFSIZE; - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; -} - -static ssize_t dma_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hsu_dma_chan *chan = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MFD HSU DMA channel [%d] regs:\n", chan->id); - - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "=================================\n"); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR)); - len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, - "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR)); - - if (len > HSU_REGS_BUFSIZE) - len = HSU_REGS_BUFSIZE; - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; -} - -static const struct file_operations port_regs_ops = { - .owner = THIS_MODULE, - .open = hsu_show_regs_open, - .read = port_show_regs, - .llseek = default_llseek, -}; - -static const struct file_operations dma_regs_ops = { - .owner = THIS_MODULE, - .open = hsu_show_regs_open, - .read = dma_show_regs, - .llseek = default_llseek, -}; - -static int hsu_debugfs_init(struct hsu_port *hsu) -{ - int i; - char name[32]; - - hsu->debugfs = debugfs_create_dir("hsu", NULL); - if (!hsu->debugfs) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - snprintf(name, sizeof(name), "port_%d_regs", i); - debugfs_create_file(name, S_IFREG | S_IRUGO, - hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops); - } - - for (i = 0; i < 6; i++) { - snprintf(name, sizeof(name), "dma_chan_%d_regs", i); - debugfs_create_file(name, S_IFREG | S_IRUGO, - hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops); - } - - return 0; -} - -static void hsu_debugfs_remove(struct hsu_port *hsu) -{ - if (hsu->debugfs) - debugfs_remove_recursive(hsu->debugfs); -} - -#else -static inline int hsu_debugfs_init(struct hsu_port *hsu) -{ - return 0; -} - -static inline void hsu_debugfs_remove(struct hsu_port *hsu) -{ -} -#endif /* CONFIG_DEBUG_FS */ - -static void serial_hsu_enable_ms(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -void hsu_dma_tx(struct uart_hsu_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - struct hsu_dma_buffer *dbuf = &up->txbuf; - int count; - - /* test_and_set_bit may be better, but anyway it's in lock protected mode */ - if (up->dma_tx_on) - return; - - /* Update the circ buf info */ - xmit->tail += dbuf->ofs; - xmit->tail &= UART_XMIT_SIZE - 1; - - up->port.icount.tx += dbuf->ofs; - dbuf->ofs = 0; - - /* Disable the channel */ - chan_writel(up->txc, HSU_CH_CR, 0x0); - - if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) { - dma_sync_single_for_device(up->port.dev, - dbuf->dma_addr, - dbuf->dma_size, - DMA_TO_DEVICE); - - count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - dbuf->ofs = count; - - /* Reprogram the channel */ - chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail); - chan_writel(up->txc, HSU_CH_D0TSR, count); - - /* Reenable the channel */ - chan_writel(up->txc, HSU_CH_DCR, 0x1 - | (0x1 << 8) - | (0x1 << 16) - | (0x1 << 24)); - up->dma_tx_on = 1; - chan_writel(up->txc, HSU_CH_CR, 0x1); - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); -} - -/* The buffer is already cache coherent */ -void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf) -{ - dbuf->ofs = 0; - - chan_writel(rxc, HSU_CH_BSR, 32); - chan_writel(rxc, HSU_CH_MOTSR, 4); - - chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr); - chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size); - chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8) - | (0x1 << 16) - | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ - ); - chan_writel(rxc, HSU_CH_CR, 0x3); - - mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); -} - -/* Protected by spin_lock_irqsave(port->lock) */ -static void serial_hsu_start_tx(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - - if (up->use_dma) { - hsu_dma_tx(up); - } else if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial_hsu_stop_tx(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - struct hsu_dma_chan *txc = up->txc; - - if (up->use_dma) - chan_writel(txc, HSU_CH_CR, 0x0); - else if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -/* This is always called in spinlock protected mode, so - * modify timeout timer is safe here */ -void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) -{ - struct hsu_dma_buffer *dbuf = &up->rxbuf; - struct hsu_dma_chan *chan = up->rxc; - struct uart_port *port = &up->port; - struct tty_struct *tty = port->state->port.tty; - int count; - - if (!tty) - return; - - /* - * First need to know how many is already transferred, - * then check if its a timeout DMA irq, and return - * the trail bytes out, push them up and reenable the - * channel - */ - - /* Timeout IRQ, need wait some time, see Errata 2 */ - if (int_sts & 0xf00) - udelay(2); - - /* Stop the channel */ - chan_writel(chan, HSU_CH_CR, 0x0); - - count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; - if (!count) { - /* Restart the channel before we leave */ - chan_writel(chan, HSU_CH_CR, 0x3); - return; - } - del_timer(&chan->rx_timer); - - dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, - dbuf->dma_size, DMA_FROM_DEVICE); - - /* - * Head will only wrap around when we recycle - * the DMA buffer, and when that happens, we - * explicitly set tail to 0. So head will - * always be greater than tail. - */ - tty_insert_flip_string(tty, dbuf->buf, count); - port->icount.rx += count; - - dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, - dbuf->dma_size, DMA_FROM_DEVICE); - - /* Reprogram the channel */ - chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr); - chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size); - chan_writel(chan, HSU_CH_DCR, 0x1 - | (0x1 << 8) - | (0x1 << 16) - | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ - ); - tty_flip_buffer_push(tty); - - chan_writel(chan, HSU_CH_CR, 0x3); - chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ; - add_timer(&chan->rx_timer); - -} - -static void serial_hsu_stop_rx(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - struct hsu_dma_chan *chan = up->rxc; - - if (up->use_dma) - chan_writel(chan, HSU_CH_CR, 0x2); - else { - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); - } -} - -static inline void receive_chars(struct uart_hsu_port *up, int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned int ch, flag; - unsigned int max_count = 256; - - if (!tty) - return; - - do { - ch = serial_in(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - - dev_warn(up->dev, "We really rush into ERR/BI case" - "status = 0x%02x", *status); - /* For statistics only */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* Mask off conditions which should be ignored. */ - *status &= up->port.read_status_mask; - -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - if (up->port.cons && - up->port.cons->index == up->port.line) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } -#endif - if (*status & UART_LSR_BI) { - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - - uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); - ignore_char: - *status = serial_in(up, UART_LSR); - } while ((*status & UART_LSR_DR) && max_count--); - tty_flip_buffer_push(tty); -} - -static void transmit_chars(struct uart_hsu_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_out(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - serial_hsu_stop_tx(&up->port); - return; - } - -#ifndef MFD_HSU_A0_STEPPING - count = up->port.fifosize / 2; -#else - /* - * A0 only supports fully empty IRQ, and the first char written - * into it won't clear the EMPT bit, so we may need be cautious - * by useing a shorter buffer - */ - count = up->port.fifosize - 4; -#endif - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) - serial_hsu_stop_tx(&up->port); -} - -static inline void check_modem_status(struct uart_hsu_port *up) -{ - int status; - - status = serial_in(up, UART_MSR); - - if ((status & UART_MSR_ANY_DELTA) == 0) - return; - - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - /* We may only get DDCD when HW init and reset */ - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - /* Will start/stop_tx accordingly */ - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); -} - -/* - * This handles the interrupt from one port. - */ -static irqreturn_t port_irq(int irq, void *dev_id) -{ - struct uart_hsu_port *up = dev_id; - unsigned int iir, lsr; - unsigned long flags; - - if (unlikely(!up->running)) - return IRQ_NONE; - - spin_lock_irqsave(&up->port.lock, flags); - if (up->use_dma) { - lsr = serial_in(up, UART_LSR); - if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) - dev_warn(up->dev, - "Got lsr irq while using DMA, lsr = 0x%2x\n", - lsr); - check_modem_status(up); - spin_unlock_irqrestore(&up->port.lock, flags); - return IRQ_HANDLED; - } - - iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) { - spin_unlock_irqrestore(&up->port.lock, flags); - return IRQ_NONE; - } - - lsr = serial_in(up, UART_LSR); - if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); - check_modem_status(up); - - /* lsr will be renewed during the receive_chars */ - if (lsr & UART_LSR_THRE) - transmit_chars(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - return IRQ_HANDLED; -} - -static inline void dma_chan_irq(struct hsu_dma_chan *chan) -{ - struct uart_hsu_port *up = chan->uport; - unsigned long flags; - u32 int_sts; - - spin_lock_irqsave(&up->port.lock, flags); - - if (!up->use_dma || !up->running) - goto exit; - - /* - * No matter what situation, need read clear the IRQ status - * There is a bug, see Errata 5, HSD 2900918 - */ - int_sts = chan_readl(chan, HSU_CH_SR); - - /* Rx channel */ - if (chan->dirt == DMA_FROM_DEVICE) - hsu_dma_rx(up, int_sts); - - /* Tx channel */ - if (chan->dirt == DMA_TO_DEVICE) { - chan_writel(chan, HSU_CH_CR, 0x0); - up->dma_tx_on = 0; - hsu_dma_tx(up); - } - -exit: - spin_unlock_irqrestore(&up->port.lock, flags); - return; -} - -static irqreturn_t dma_irq(int irq, void *dev_id) -{ - struct hsu_port *hsu = dev_id; - u32 int_sts, i; - - int_sts = mfd_readl(hsu, HSU_GBL_DMAISR); - - /* Currently we only have 6 channels may be used */ - for (i = 0; i < 6; i++) { - if (int_sts & 0x1) - dma_chan_irq(&hsu->chans[i]); - int_sts >>= 1; - } - - return IRQ_HANDLED; -} - -static unsigned int serial_hsu_tx_empty(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int serial_hsu_get_mctrl(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned char status; - unsigned int ret; - - status = serial_in(up, 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 serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned char mcr = 0; - - 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; - - mcr |= up->mcr; - - serial_out(up, UART_MCR, mcr); -} - -static void serial_hsu_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* - * What special to do: - * 1. chose the 64B fifo mode - * 2. make sure not to select half empty mode for A0 stepping - * 3. start dma or pio depends on configuration - * 4. we only allocate dma memory when needed - */ -static int serial_hsu_startup(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); - - /* Clear the interrupt registers. */ - (void) serial_in(up, UART_LSR); - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - /* Now, initialize the UART, default is 8n1 */ - serial_out(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - - up->port.mctrl |= TIOCM_OUT2; - serial_hsu_set_mctrl(&up->port, up->port.mctrl); - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - if (!up->use_dma) - up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE; - else - up->ier = 0; - serial_out(up, UART_IER, up->ier); - - spin_unlock_irqrestore(&up->port.lock, flags); - - /* DMA init */ - if (up->use_dma) { - struct hsu_dma_buffer *dbuf; - struct circ_buf *xmit = &port->state->xmit; - - up->dma_tx_on = 0; - - /* First allocate the RX buffer */ - dbuf = &up->rxbuf; - dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL); - if (!dbuf->buf) { - up->use_dma = 0; - goto exit; - } - dbuf->dma_addr = dma_map_single(port->dev, - dbuf->buf, - HSU_DMA_BUF_SIZE, - DMA_FROM_DEVICE); - dbuf->dma_size = HSU_DMA_BUF_SIZE; - - /* Start the RX channel right now */ - hsu_dma_start_rx_chan(up->rxc, dbuf); - - /* Next init the TX DMA */ - dbuf = &up->txbuf; - dbuf->buf = xmit->buf; - dbuf->dma_addr = dma_map_single(port->dev, - dbuf->buf, - UART_XMIT_SIZE, - DMA_TO_DEVICE); - dbuf->dma_size = UART_XMIT_SIZE; - - /* This should not be changed all around */ - chan_writel(up->txc, HSU_CH_BSR, 32); - chan_writel(up->txc, HSU_CH_MOTSR, 4); - dbuf->ofs = 0; - } - -exit: - /* And clear the interrupt registers again for luck. */ - (void) serial_in(up, UART_LSR); - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - up->running = 1; - return 0; -} - -static void serial_hsu_shutdown(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - unsigned long flags; - - del_timer_sync(&up->rxc->rx_timer); - - /* Disable interrupts from this port */ - up->ier = 0; - serial_out(up, UART_IER, 0); - up->running = 0; - - spin_lock_irqsave(&up->port.lock, flags); - up->port.mctrl &= ~TIOCM_OUT2; - serial_hsu_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* Disable break condition and FIFOs */ - serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); -} - -static void -serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - struct tty_struct *tty = port->state->port.tty; - unsigned char cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - u32 ps, mul; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - /* CMSPAR isn't supported by this driver */ - if (tty) - tty->termios->c_cflag &= ~CMSPAR; - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; - - /* - * The base clk is 50Mhz, and the baud rate come from: - * baud = 50M * MUL / (DIV * PS * DLAB) - * - * For those basic low baud rate we can get the direct - * scalar from 2746800, like 115200 = 2746800/24. For those - * higher baud rate, we handle them case by case, mainly by - * adjusting the MUL/PS registers, and DIV register is kept - * as default value 0x3d09 to make things simple - */ - baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - - quot = 1; - ps = 0x10; - mul = 0x3600; - switch (baud) { - case 3500000: - mul = 0x3345; - ps = 0xC; - break; - case 1843200: - mul = 0x2400; - break; - case 3000000: - case 2500000: - case 2000000: - case 1500000: - case 1000000: - case 500000: - /* mul/ps/quot = 0x9C4/0x10/0x1 will make a 500000 bps */ - mul = baud / 500000 * 0x9C4; - break; - default: - /* Use uart_get_divisor to get quot for other baud rates */ - quot = 0; - } - - if (!quot) - quot = uart_get_divisor(port, baud); - - if ((up->port.uartclk / quot) < (2400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B; - else if ((up->port.uartclk / quot) < (230400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B; - else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B; - - fcr |= UART_FCR_HSU_64B_FIFO; -#ifdef MFD_HSU_A0_STEPPING - /* A0 doesn't support half empty IRQ */ - fcr |= UART_FCR_FULL_EMPT_TXI; -#endif - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* Update the per-port timeout */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* Characters to ignore */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* Ignore all characters if CREAD is not set */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts, disable - * MSI by default - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - if (termios->c_cflag & CRTSCTS) - up->mcr |= UART_MCR_AFE | UART_MCR_RTS; - else - up->mcr &= ~UART_MCR_AFE; - - serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ - serial_out(up, UART_LCR, cval); /* reset DLAB */ - serial_out(up, UART_MUL, mul); /* set MUL */ - serial_out(up, UART_PS, ps); /* set PS */ - up->lcr = cval; /* Save LCR */ - serial_hsu_set_mctrl(&up->port, up->port.mctrl); - serial_out(up, UART_FCR, fcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void -serial_hsu_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ -} - -static void serial_hsu_release_port(struct uart_port *port) -{ -} - -static int serial_hsu_request_port(struct uart_port *port) -{ - return 0; -} - -static void serial_hsu_config_port(struct uart_port *port, int flags) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - up->port.type = PORT_MFD; -} - -static int -serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* We don't want the core code to modify any port params */ - return -EINVAL; -} - -static const char * -serial_hsu_type(struct uart_port *port) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - return up->name; -} - -/* Mainly for uart console use */ -static struct uart_hsu_port *serial_hsu_ports[3]; -static struct uart_driver serial_hsu_reg; - -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct uart_hsu_port *up) -{ - unsigned int status, tmout = 1000; - - /* Wait up to 1ms for the character to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while (!(status & BOTH_EMPTY)); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) - udelay(1); - } -} - -static void serial_hsu_console_putchar(struct uart_port *port, int ch) -{ - struct uart_hsu_port *up = - container_of(port, struct uart_hsu_port, port); - - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -/* - * 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. - */ -static void -serial_hsu_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_hsu_port *up = serial_hsu_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - /* First save the IER then disable the interrupts */ - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, serial_hsu_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - serial_out(up, UART_IER, ier); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static struct console serial_hsu_console; - -static int __init -serial_hsu_console_setup(struct console *co, char *options) -{ - struct uart_hsu_port *up; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - if (co->index == -1 || co->index >= serial_hsu_reg.nr) - co->index = 0; - up = serial_hsu_ports[co->index]; - if (!up) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - ret = uart_set_options(&up->port, co, baud, parity, bits, flow); - - return ret; -} - -static struct console serial_hsu_console = { - .name = "ttyMFD", - .write = serial_hsu_console_write, - .device = uart_console_device, - .setup = serial_hsu_console_setup, - .flags = CON_PRINTBUFFER, - .index = 2, - .data = &serial_hsu_reg, -}; -#endif - -struct uart_ops serial_hsu_pops = { - .tx_empty = serial_hsu_tx_empty, - .set_mctrl = serial_hsu_set_mctrl, - .get_mctrl = serial_hsu_get_mctrl, - .stop_tx = serial_hsu_stop_tx, - .start_tx = serial_hsu_start_tx, - .stop_rx = serial_hsu_stop_rx, - .enable_ms = serial_hsu_enable_ms, - .break_ctl = serial_hsu_break_ctl, - .startup = serial_hsu_startup, - .shutdown = serial_hsu_shutdown, - .set_termios = serial_hsu_set_termios, - .pm = serial_hsu_pm, - .type = serial_hsu_type, - .release_port = serial_hsu_release_port, - .request_port = serial_hsu_request_port, - .config_port = serial_hsu_config_port, - .verify_port = serial_hsu_verify_port, -}; - -static struct uart_driver serial_hsu_reg = { - .owner = THIS_MODULE, - .driver_name = "MFD serial", - .dev_name = "ttyMFD", - .major = TTY_MAJOR, - .minor = 128, - .nr = 3, -}; - -#ifdef CONFIG_PM -static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state) -{ - void *priv = pci_get_drvdata(pdev); - struct uart_hsu_port *up; - - /* Make sure this is not the internal dma controller */ - if (priv && (pdev->device != 0x081E)) { - up = priv; - uart_suspend_port(&serial_hsu_reg, &up->port); - } - - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int serial_hsu_resume(struct pci_dev *pdev) -{ - void *priv = pci_get_drvdata(pdev); - struct uart_hsu_port *up; - int ret; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - - ret = pci_enable_device(pdev); - if (ret) - dev_warn(&pdev->dev, - "HSU: can't re-enable device, try to continue\n"); - - if (priv && (pdev->device != 0x081E)) { - up = priv; - uart_resume_port(&serial_hsu_reg, &up->port); - } - return 0; -} -#else -#define serial_hsu_suspend NULL -#define serial_hsu_resume NULL -#endif - -/* temp global pointer before we settle down on using one or four PCI dev */ -static struct hsu_port *phsu; - -static int serial_hsu_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct uart_hsu_port *uport; - int index, ret; - - printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n", - pdev->vendor, pdev->device); - - switch (pdev->device) { - case 0x081B: - index = 0; - break; - case 0x081C: - index = 1; - break; - case 0x081D: - index = 2; - break; - case 0x081E: - /* internal DMA controller */ - index = 3; - break; - default: - dev_err(&pdev->dev, "HSU: out of index!"); - return -ENODEV; - } - - ret = pci_enable_device(pdev); - if (ret) - return ret; - - if (index == 3) { - /* DMA controller */ - ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu); - if (ret) { - dev_err(&pdev->dev, "can not get IRQ\n"); - goto err_disable; - } - pci_set_drvdata(pdev, phsu); - } else { - /* UART port 0~2 */ - uport = &phsu->port[index]; - uport->port.irq = pdev->irq; - uport->port.dev = &pdev->dev; - uport->dev = &pdev->dev; - - ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport); - if (ret) { - dev_err(&pdev->dev, "can not get IRQ\n"); - goto err_disable; - } - uart_add_one_port(&serial_hsu_reg, &uport->port); - -#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE - if (index == 2) { - register_console(&serial_hsu_console); - uport->port.cons = &serial_hsu_console; - } -#endif - pci_set_drvdata(pdev, uport); - } - - return 0; - -err_disable: - pci_disable_device(pdev); - return ret; -} - -static void hsu_dma_rx_timeout(unsigned long data) -{ - struct hsu_dma_chan *chan = (void *)data; - struct uart_hsu_port *up = chan->uport; - struct hsu_dma_buffer *dbuf = &up->rxbuf; - int count = 0; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; - - if (!count) { - mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); - goto exit; - } - - hsu_dma_rx(up, 0); -exit: - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void hsu_global_init(void) -{ - struct hsu_port *hsu; - struct uart_hsu_port *uport; - struct hsu_dma_chan *dchan; - int i, ret; - - hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL); - if (!hsu) - return; - - /* Get basic io resource and map it */ - hsu->paddr = 0xffa28000; - hsu->iolen = 0x1000; - - if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global"))) - pr_warning("HSU: error in request mem region\n"); - - hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen); - if (!hsu->reg) { - pr_err("HSU: error in ioremap\n"); - ret = -ENOMEM; - goto err_free_region; - } - - /* Initialise the 3 UART ports */ - uport = hsu->port; - for (i = 0; i < 3; i++) { - uport->port.type = PORT_MFD; - uport->port.iotype = UPIO_MEM; - uport->port.mapbase = (resource_size_t)hsu->paddr - + HSU_PORT_REG_OFFSET - + i * HSU_PORT_REG_LENGTH; - uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET - + i * HSU_PORT_REG_LENGTH; - - sprintf(uport->name, "hsu_port%d", i); - uport->port.fifosize = 64; - uport->port.ops = &serial_hsu_pops; - uport->port.line = i; - uport->port.flags = UPF_IOREMAP; - /* set the scalable maxim support rate to 2746800 bps */ - uport->port.uartclk = 115200 * 24 * 16; - - uport->running = 0; - uport->txc = &hsu->chans[i * 2]; - uport->rxc = &hsu->chans[i * 2 + 1]; - - serial_hsu_ports[i] = uport; - uport->index = i; - uport++; - } - - /* Initialise 6 dma channels */ - dchan = hsu->chans; - for (i = 0; i < 6; i++) { - dchan->id = i; - dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - dchan->uport = &hsu->port[i/2]; - dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + - i * HSU_DMA_CHANS_REG_LENGTH; - - /* Work around for RX */ - if (dchan->dirt == DMA_FROM_DEVICE) { - init_timer(&dchan->rx_timer); - dchan->rx_timer.function = hsu_dma_rx_timeout; - dchan->rx_timer.data = (unsigned long)dchan; - } - dchan++; - } - - phsu = hsu; - hsu_debugfs_init(hsu); - return; - -err_free_region: - release_mem_region(hsu->paddr, hsu->iolen); - kfree(hsu); - return; -} - -static void serial_hsu_remove(struct pci_dev *pdev) -{ - void *priv = pci_get_drvdata(pdev); - struct uart_hsu_port *up; - - if (!priv) - return; - - /* For port 0/1/2, priv is the address of uart_hsu_port */ - if (pdev->device != 0x081E) { - up = priv; - uart_remove_one_port(&serial_hsu_reg, &up->port); - } - - pci_set_drvdata(pdev, NULL); - free_irq(pdev->irq, priv); - pci_disable_device(pdev); -} - -/* First 3 are UART ports, and the 4th is the DMA */ -static const struct pci_device_id pci_ids[] __devinitdata = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) }, - {}, -}; - -static struct pci_driver hsu_pci_driver = { - .name = "HSU serial", - .id_table = pci_ids, - .probe = serial_hsu_probe, - .remove = __devexit_p(serial_hsu_remove), - .suspend = serial_hsu_suspend, - .resume = serial_hsu_resume, -}; - -static int __init hsu_pci_init(void) -{ - int ret; - - hsu_global_init(); - - ret = uart_register_driver(&serial_hsu_reg); - if (ret) - return ret; - - return pci_register_driver(&hsu_pci_driver); -} - -static void __exit hsu_pci_exit(void) -{ - pci_unregister_driver(&hsu_pci_driver); - uart_unregister_driver(&serial_hsu_reg); - - hsu_debugfs_remove(phsu); - - kfree(phsu); -} - -module_init(hsu_pci_init); -module_exit(hsu_pci_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:medfield-hsu"); diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c deleted file mode 100644 index 126ec7f568ec..000000000000 --- a/drivers/serial/mpc52xx_uart.c +++ /dev/null @@ -1,1527 +0,0 @@ -/* - * Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs. - * - * FIXME According to the usermanual the status bits in the status register - * are only updated when the peripherals access the FIFO and not when the - * CPU access them. So since we use this bits to know when we stop writing - * and reading, they may not be updated in-time and a race condition may - * exists. But I haven't be able to prove this and I don't care. But if - * any problem arises, it might worth checking. The TX/RX FIFO Stats - * registers should be used in addition. - * Update: Actually, they seem updated ... At least the bits we use. - * - * - * Maintainer : Sylvain Munaut - * - * Some of the code has been inspired/copied from the 2.4 code written - * by Dale Farnsworth . - * - * Copyright (C) 2008 Freescale Semiconductor Inc. - * John Rigby - * Added support for MPC5121 - * Copyright (C) 2006 Secret Lab Technologies Ltd. - * Grant Likely - * Copyright (C) 2004-2006 Sylvain Munaut - * Copyright (C) 2003 MontaVista, Software, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - - -/* We've been assigned a range on the "Low-density serial ports" major */ -#define SERIAL_PSC_MAJOR 204 -#define SERIAL_PSC_MINOR 148 - - -#define ISR_PASS_LIMIT 256 /* Max number of iteration in the interrupt */ - - -static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM]; - /* Rem: - We use the read_status_mask as a shadow of - * psc->mpc52xx_psc_imr - * - It's important that is array is all zero on start as we - * use it to know if it's initialized or not ! If it's not sure - * it's cleared, then a memset(...,0,...) should be added to - * the console_init - */ - -/* lookup table for matching device nodes to index numbers */ -static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM]; - -static void mpc52xx_uart_of_enumerate(void); - - -#define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase)) - - -/* Forward declaration of the interruption handling routine */ -static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); -static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); - - -/* Simple macro to test if a port is console or not. This one is taken - * for serial_core.c and maybe should be moved to serial_core.h ? */ -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) \ - ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - -/* ======================================================================== */ -/* PSC fifo operations for isolating differences between 52xx and 512x */ -/* ======================================================================== */ - -struct psc_ops { - void (*fifo_init)(struct uart_port *port); - int (*raw_rx_rdy)(struct uart_port *port); - int (*raw_tx_rdy)(struct uart_port *port); - int (*rx_rdy)(struct uart_port *port); - int (*tx_rdy)(struct uart_port *port); - int (*tx_empty)(struct uart_port *port); - void (*stop_rx)(struct uart_port *port); - void (*start_tx)(struct uart_port *port); - void (*stop_tx)(struct uart_port *port); - void (*rx_clr_irq)(struct uart_port *port); - void (*tx_clr_irq)(struct uart_port *port); - void (*write_char)(struct uart_port *port, unsigned char c); - unsigned char (*read_char)(struct uart_port *port); - void (*cw_disable_ints)(struct uart_port *port); - void (*cw_restore_ints)(struct uart_port *port); - unsigned int (*set_baudrate)(struct uart_port *port, - struct ktermios *new, - struct ktermios *old); - int (*clock)(struct uart_port *port, int enable); - int (*fifoc_init)(void); - void (*fifoc_uninit)(void); - void (*get_irq)(struct uart_port *, struct device_node *); - irqreturn_t (*handle_irq)(struct uart_port *port); -}; - -/* setting the prescaler and divisor reg is common for all chips */ -static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, - u16 prescaler, unsigned int divisor) -{ - /* select prescaler */ - out_be16(&psc->mpc52xx_psc_clock_select, prescaler); - out_8(&psc->ctur, divisor >> 8); - out_8(&psc->ctlr, divisor & 0xff); -} - -#ifdef CONFIG_PPC_MPC52xx -#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) -static void mpc52xx_psc_fifo_init(struct uart_port *port) -{ - struct mpc52xx_psc __iomem *psc = PSC(port); - struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); - - out_8(&fifo->rfcntl, 0x00); - out_be16(&fifo->rfalarm, 0x1ff); - out_8(&fifo->tfcntl, 0x07); - out_be16(&fifo->tfalarm, 0x80); - - port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); -} - -static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port) -{ - return in_be16(&PSC(port)->mpc52xx_psc_status) - & MPC52xx_PSC_SR_RXRDY; -} - -static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port) -{ - return in_be16(&PSC(port)->mpc52xx_psc_status) - & MPC52xx_PSC_SR_TXRDY; -} - - -static int mpc52xx_psc_rx_rdy(struct uart_port *port) -{ - return in_be16(&PSC(port)->mpc52xx_psc_isr) - & port->read_status_mask - & MPC52xx_PSC_IMR_RXRDY; -} - -static int mpc52xx_psc_tx_rdy(struct uart_port *port) -{ - return in_be16(&PSC(port)->mpc52xx_psc_isr) - & port->read_status_mask - & MPC52xx_PSC_IMR_TXRDY; -} - -static int mpc52xx_psc_tx_empty(struct uart_port *port) -{ - return in_be16(&PSC(port)->mpc52xx_psc_status) - & MPC52xx_PSC_SR_TXEMP; -} - -static void mpc52xx_psc_start_tx(struct uart_port *port) -{ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); -} - -static void mpc52xx_psc_stop_tx(struct uart_port *port) -{ - port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); -} - -static void mpc52xx_psc_stop_rx(struct uart_port *port) -{ - port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); -} - -static void mpc52xx_psc_rx_clr_irq(struct uart_port *port) -{ -} - -static void mpc52xx_psc_tx_clr_irq(struct uart_port *port) -{ -} - -static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c) -{ - out_8(&PSC(port)->mpc52xx_psc_buffer_8, c); -} - -static unsigned char mpc52xx_psc_read_char(struct uart_port *port) -{ - return in_8(&PSC(port)->mpc52xx_psc_buffer_8); -} - -static void mpc52xx_psc_cw_disable_ints(struct uart_port *port) -{ - out_be16(&PSC(port)->mpc52xx_psc_imr, 0); -} - -static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) -{ - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); -} - -static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port, - struct ktermios *new, - struct ktermios *old) -{ - unsigned int baud; - unsigned int divisor; - - /* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */ - baud = uart_get_baud_rate(port, new, old, - port->uartclk / (32 * 0xffff) + 1, - port->uartclk / 32); - divisor = (port->uartclk + 16 * baud) / (32 * baud); - - /* enable the /32 prescaler and set the divisor */ - mpc52xx_set_divisor(PSC(port), 0xdd00, divisor); - return baud; -} - -static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port, - struct ktermios *new, - struct ktermios *old) -{ - unsigned int baud; - unsigned int divisor; - u16 prescaler; - - /* The 5200B has a selectable /4 or /32 prescaler, uartclk contains the - * ipb freq */ - baud = uart_get_baud_rate(port, new, old, - port->uartclk / (32 * 0xffff) + 1, - port->uartclk / 4); - divisor = (port->uartclk + 2 * baud) / (4 * baud); - - /* select the proper prescaler and set the divisor */ - if (divisor > 0xffff) { - divisor = (divisor + 4) / 8; - prescaler = 0xdd00; /* /32 */ - } else - prescaler = 0xff00; /* /4 */ - mpc52xx_set_divisor(PSC(port), prescaler, divisor); - return baud; -} - -static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np) -{ - port->irqflags = IRQF_DISABLED; - port->irq = irq_of_parse_and_map(np, 0); -} - -/* 52xx specific interrupt handler. The caller holds the port lock */ -static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port) -{ - return mpc5xxx_uart_process_int(port); -} - -static struct psc_ops mpc52xx_psc_ops = { - .fifo_init = mpc52xx_psc_fifo_init, - .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, - .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, - .rx_rdy = mpc52xx_psc_rx_rdy, - .tx_rdy = mpc52xx_psc_tx_rdy, - .tx_empty = mpc52xx_psc_tx_empty, - .stop_rx = mpc52xx_psc_stop_rx, - .start_tx = mpc52xx_psc_start_tx, - .stop_tx = mpc52xx_psc_stop_tx, - .rx_clr_irq = mpc52xx_psc_rx_clr_irq, - .tx_clr_irq = mpc52xx_psc_tx_clr_irq, - .write_char = mpc52xx_psc_write_char, - .read_char = mpc52xx_psc_read_char, - .cw_disable_ints = mpc52xx_psc_cw_disable_ints, - .cw_restore_ints = mpc52xx_psc_cw_restore_ints, - .set_baudrate = mpc5200_psc_set_baudrate, - .get_irq = mpc52xx_psc_get_irq, - .handle_irq = mpc52xx_psc_handle_irq, -}; - -static struct psc_ops mpc5200b_psc_ops = { - .fifo_init = mpc52xx_psc_fifo_init, - .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, - .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, - .rx_rdy = mpc52xx_psc_rx_rdy, - .tx_rdy = mpc52xx_psc_tx_rdy, - .tx_empty = mpc52xx_psc_tx_empty, - .stop_rx = mpc52xx_psc_stop_rx, - .start_tx = mpc52xx_psc_start_tx, - .stop_tx = mpc52xx_psc_stop_tx, - .rx_clr_irq = mpc52xx_psc_rx_clr_irq, - .tx_clr_irq = mpc52xx_psc_tx_clr_irq, - .write_char = mpc52xx_psc_write_char, - .read_char = mpc52xx_psc_read_char, - .cw_disable_ints = mpc52xx_psc_cw_disable_ints, - .cw_restore_ints = mpc52xx_psc_cw_restore_ints, - .set_baudrate = mpc5200b_psc_set_baudrate, - .get_irq = mpc52xx_psc_get_irq, - .handle_irq = mpc52xx_psc_handle_irq, -}; - -#endif /* CONFIG_MPC52xx */ - -#ifdef CONFIG_PPC_MPC512x -#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) - -/* PSC FIFO Controller for mpc512x */ -struct psc_fifoc { - u32 fifoc_cmd; - u32 fifoc_int; - u32 fifoc_dma; - u32 fifoc_axe; - u32 fifoc_debug; -}; - -static struct psc_fifoc __iomem *psc_fifoc; -static unsigned int psc_fifoc_irq; - -static void mpc512x_psc_fifo_init(struct uart_port *port) -{ - /* /32 prescaler */ - out_be16(&PSC(port)->mpc52xx_psc_clock_select, 0xdd00); - - out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); - out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); - out_be32(&FIFO_512x(port)->txalarm, 1); - out_be32(&FIFO_512x(port)->tximr, 0); - - out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); - out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); - out_be32(&FIFO_512x(port)->rxalarm, 1); - out_be32(&FIFO_512x(port)->rximr, 0); - - out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM); - out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM); -} - -static int mpc512x_psc_raw_rx_rdy(struct uart_port *port) -{ - return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); -} - -static int mpc512x_psc_raw_tx_rdy(struct uart_port *port) -{ - return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL); -} - -static int mpc512x_psc_rx_rdy(struct uart_port *port) -{ - return in_be32(&FIFO_512x(port)->rxsr) - & in_be32(&FIFO_512x(port)->rximr) - & MPC512x_PSC_FIFO_ALARM; -} - -static int mpc512x_psc_tx_rdy(struct uart_port *port) -{ - return in_be32(&FIFO_512x(port)->txsr) - & in_be32(&FIFO_512x(port)->tximr) - & MPC512x_PSC_FIFO_ALARM; -} - -static int mpc512x_psc_tx_empty(struct uart_port *port) -{ - return in_be32(&FIFO_512x(port)->txsr) - & MPC512x_PSC_FIFO_EMPTY; -} - -static void mpc512x_psc_stop_rx(struct uart_port *port) -{ - unsigned long rx_fifo_imr; - - rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr); - rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; - out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr); -} - -static void mpc512x_psc_start_tx(struct uart_port *port) -{ - unsigned long tx_fifo_imr; - - tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); - tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; - out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); -} - -static void mpc512x_psc_stop_tx(struct uart_port *port) -{ - unsigned long tx_fifo_imr; - - tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); - tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; - out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); -} - -static void mpc512x_psc_rx_clr_irq(struct uart_port *port) -{ - out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr)); -} - -static void mpc512x_psc_tx_clr_irq(struct uart_port *port) -{ - out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr)); -} - -static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c) -{ - out_8(&FIFO_512x(port)->txdata_8, c); -} - -static unsigned char mpc512x_psc_read_char(struct uart_port *port) -{ - return in_8(&FIFO_512x(port)->rxdata_8); -} - -static void mpc512x_psc_cw_disable_ints(struct uart_port *port) -{ - port->read_status_mask = - in_be32(&FIFO_512x(port)->tximr) << 16 | - in_be32(&FIFO_512x(port)->rximr); - out_be32(&FIFO_512x(port)->tximr, 0); - out_be32(&FIFO_512x(port)->rximr, 0); -} - -static void mpc512x_psc_cw_restore_ints(struct uart_port *port) -{ - out_be32(&FIFO_512x(port)->tximr, - (port->read_status_mask >> 16) & 0x7f); - out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); -} - -static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port, - struct ktermios *new, - struct ktermios *old) -{ - unsigned int baud; - unsigned int divisor; - - /* - * The "MPC5121e Microcontroller Reference Manual, Rev. 3" says on - * pg. 30-10 that the chip supports a /32 and a /10 prescaler. - * Furthermore, it states that "After reset, the prescaler by 10 - * for the UART mode is selected", but the reset register value is - * 0x0000 which means a /32 prescaler. This is wrong. - * - * In reality using /32 prescaler doesn't work, as it is not supported! - * Use /16 or /10 prescaler, see "MPC5121e Hardware Design Guide", - * Chapter 4.1 PSC in UART Mode. - * Calculate with a /16 prescaler here. - */ - - /* uartclk contains the ips freq */ - baud = uart_get_baud_rate(port, new, old, - port->uartclk / (16 * 0xffff) + 1, - port->uartclk / 16); - divisor = (port->uartclk + 8 * baud) / (16 * baud); - - /* enable the /16 prescaler and set the divisor */ - mpc52xx_set_divisor(PSC(port), 0xdd00, divisor); - return baud; -} - -/* Init PSC FIFO Controller */ -static int __init mpc512x_psc_fifoc_init(void) -{ - struct device_node *np; - - np = of_find_compatible_node(NULL, NULL, - "fsl,mpc5121-psc-fifo"); - if (!np) { - pr_err("%s: Can't find FIFOC node\n", __func__); - return -ENODEV; - } - - psc_fifoc = of_iomap(np, 0); - if (!psc_fifoc) { - pr_err("%s: Can't map FIFOC\n", __func__); - of_node_put(np); - return -ENODEV; - } - - psc_fifoc_irq = irq_of_parse_and_map(np, 0); - of_node_put(np); - if (psc_fifoc_irq == NO_IRQ) { - pr_err("%s: Can't get FIFOC irq\n", __func__); - iounmap(psc_fifoc); - return -ENODEV; - } - - return 0; -} - -static void __exit mpc512x_psc_fifoc_uninit(void) -{ - iounmap(psc_fifoc); -} - -/* 512x specific interrupt handler. The caller holds the port lock */ -static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) -{ - unsigned long fifoc_int; - int psc_num; - - /* Read pending PSC FIFOC interrupts */ - fifoc_int = in_be32(&psc_fifoc->fifoc_int); - - /* Check if it is an interrupt for this port */ - psc_num = (port->mapbase & 0xf00) >> 8; - if (test_bit(psc_num, &fifoc_int) || - test_bit(psc_num + 16, &fifoc_int)) - return mpc5xxx_uart_process_int(port); - - return IRQ_NONE; -} - -static int mpc512x_psc_clock(struct uart_port *port, int enable) -{ - struct clk *psc_clk; - int psc_num; - char clk_name[10]; - - if (uart_console(port)) - return 0; - - psc_num = (port->mapbase & 0xf00) >> 8; - snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num); - psc_clk = clk_get(port->dev, clk_name); - if (IS_ERR(psc_clk)) { - dev_err(port->dev, "Failed to get PSC clock entry!\n"); - return -ENODEV; - } - - dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis"); - - if (enable) - clk_enable(psc_clk); - else - clk_disable(psc_clk); - - return 0; -} - -static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) -{ - port->irqflags = IRQF_SHARED; - port->irq = psc_fifoc_irq; -} - -static struct psc_ops mpc512x_psc_ops = { - .fifo_init = mpc512x_psc_fifo_init, - .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, - .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, - .rx_rdy = mpc512x_psc_rx_rdy, - .tx_rdy = mpc512x_psc_tx_rdy, - .tx_empty = mpc512x_psc_tx_empty, - .stop_rx = mpc512x_psc_stop_rx, - .start_tx = mpc512x_psc_start_tx, - .stop_tx = mpc512x_psc_stop_tx, - .rx_clr_irq = mpc512x_psc_rx_clr_irq, - .tx_clr_irq = mpc512x_psc_tx_clr_irq, - .write_char = mpc512x_psc_write_char, - .read_char = mpc512x_psc_read_char, - .cw_disable_ints = mpc512x_psc_cw_disable_ints, - .cw_restore_ints = mpc512x_psc_cw_restore_ints, - .set_baudrate = mpc512x_psc_set_baudrate, - .clock = mpc512x_psc_clock, - .fifoc_init = mpc512x_psc_fifoc_init, - .fifoc_uninit = mpc512x_psc_fifoc_uninit, - .get_irq = mpc512x_psc_get_irq, - .handle_irq = mpc512x_psc_handle_irq, -}; -#endif - -static struct psc_ops *psc_ops; - -/* ======================================================================== */ -/* UART operations */ -/* ======================================================================== */ - -static unsigned int -mpc52xx_uart_tx_empty(struct uart_port *port) -{ - return psc_ops->tx_empty(port) ? TIOCSER_TEMT : 0; -} - -static void -mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - if (mctrl & TIOCM_RTS) - out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); - else - out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); -} - -static unsigned int -mpc52xx_uart_get_mctrl(struct uart_port *port) -{ - unsigned int ret = TIOCM_DSR; - u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr); - - if (!(status & MPC52xx_PSC_CTS)) - ret |= TIOCM_CTS; - if (!(status & MPC52xx_PSC_DCD)) - ret |= TIOCM_CAR; - - return ret; -} - -static void -mpc52xx_uart_stop_tx(struct uart_port *port) -{ - /* port->lock taken by caller */ - psc_ops->stop_tx(port); -} - -static void -mpc52xx_uart_start_tx(struct uart_port *port) -{ - /* port->lock taken by caller */ - psc_ops->start_tx(port); -} - -static void -mpc52xx_uart_send_xchar(struct uart_port *port, char ch) -{ - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - - port->x_char = ch; - if (ch) { - /* Make sure tx interrupts are on */ - /* Truly necessary ??? They should be anyway */ - psc_ops->start_tx(port); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void -mpc52xx_uart_stop_rx(struct uart_port *port) -{ - /* port->lock taken by caller */ - psc_ops->stop_rx(port); -} - -static void -mpc52xx_uart_enable_ms(struct uart_port *port) -{ - struct mpc52xx_psc __iomem *psc = PSC(port); - - /* clear D_*-bits by reading them */ - in_8(&psc->mpc52xx_psc_ipcr); - /* enable CTS and DCD as IPC interrupts */ - out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); - - port->read_status_mask |= MPC52xx_PSC_IMR_IPC; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); -} - -static void -mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) -{ - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - - if (ctl == -1) - out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK); - else - out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static int -mpc52xx_uart_startup(struct uart_port *port) -{ - struct mpc52xx_psc __iomem *psc = PSC(port); - int ret; - - if (psc_ops->clock) { - ret = psc_ops->clock(port, 1); - if (ret) - return ret; - } - - /* Request IRQ */ - ret = request_irq(port->irq, mpc52xx_uart_int, - port->irqflags, "mpc52xx_psc_uart", port); - if (ret) - return ret; - - /* Reset/activate the port, clear and enable interrupts */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); - - out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ - - psc_ops->fifo_init(port); - - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); - - return 0; -} - -static void -mpc52xx_uart_shutdown(struct uart_port *port) -{ - struct mpc52xx_psc __iomem *psc = PSC(port); - - /* Shut down the port. Leave TX active if on a console port */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - if (!uart_console(port)) - out_8(&psc->command, MPC52xx_PSC_RST_TX); - - port->read_status_mask = 0; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); - - if (psc_ops->clock) - psc_ops->clock(port, 0); - - /* Release interrupt */ - free_irq(port->irq, port); -} - -static void -mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, - struct ktermios *old) -{ - struct mpc52xx_psc __iomem *psc = PSC(port); - unsigned long flags; - unsigned char mr1, mr2; - unsigned int j; - unsigned int baud; - - /* Prepare what we're gonna write */ - mr1 = 0; - - switch (new->c_cflag & CSIZE) { - case CS5: mr1 |= MPC52xx_PSC_MODE_5_BITS; - break; - case CS6: mr1 |= MPC52xx_PSC_MODE_6_BITS; - break; - case CS7: mr1 |= MPC52xx_PSC_MODE_7_BITS; - break; - case CS8: - default: mr1 |= MPC52xx_PSC_MODE_8_BITS; - } - - if (new->c_cflag & PARENB) { - mr1 |= (new->c_cflag & PARODD) ? - MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN; - } else - mr1 |= MPC52xx_PSC_MODE_PARNONE; - - - mr2 = 0; - - if (new->c_cflag & CSTOPB) - mr2 |= MPC52xx_PSC_MODE_TWO_STOP; - else - mr2 |= ((new->c_cflag & CSIZE) == CS5) ? - MPC52xx_PSC_MODE_ONE_STOP_5_BITS : - MPC52xx_PSC_MODE_ONE_STOP; - - if (new->c_cflag & CRTSCTS) { - mr1 |= MPC52xx_PSC_MODE_RXRTS; - mr2 |= MPC52xx_PSC_MODE_TXCTS; - } - - /* Get the lock */ - spin_lock_irqsave(&port->lock, flags); - - /* Do our best to flush TX & RX, so we don't lose anything */ - /* But we don't wait indefinitely ! */ - j = 5000000; /* Maximum wait */ - /* FIXME Can't receive chars since set_termios might be called at early - * boot for the console, all stuff is not yet ready to receive at that - * time and that just makes the kernel oops */ - /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */ - while (!mpc52xx_uart_tx_empty(port) && --j) - udelay(1); - - if (!j) - printk(KERN_ERR "mpc52xx_uart.c: " - "Unable to flush RX & TX fifos in-time in set_termios." - "Some chars may have been lost.\n"); - - /* Reset the TX & RX */ - out_8(&psc->command, MPC52xx_PSC_RST_RX); - out_8(&psc->command, MPC52xx_PSC_RST_TX); - - /* Send new mode settings */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - out_8(&psc->mode, mr1); - out_8(&psc->mode, mr2); - baud = psc_ops->set_baudrate(port, new, old); - - /* Update the per-port timeout */ - uart_update_timeout(port, new->c_cflag, baud); - - if (UART_ENABLE_MS(port, new->c_cflag)) - mpc52xx_uart_enable_ms(port); - - /* Reenable TX & RX */ - out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); - out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); - - /* We're all set, release the lock */ - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char * -mpc52xx_uart_type(struct uart_port *port) -{ - /* - * We keep using PORT_MPC52xx for historic reasons although it applies - * for MPC512x, too, but print "MPC5xxx" to not irritate users - */ - return port->type == PORT_MPC52xx ? "MPC5xxx PSC" : NULL; -} - -static void -mpc52xx_uart_release_port(struct uart_port *port) -{ - /* remapped by us ? */ - if (port->flags & UPF_IOREMAP) { - iounmap(port->membase); - port->membase = NULL; - } - - release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc)); -} - -static int -mpc52xx_uart_request_port(struct uart_port *port) -{ - int err; - - if (port->flags & UPF_IOREMAP) /* Need to remap ? */ - port->membase = ioremap(port->mapbase, - sizeof(struct mpc52xx_psc)); - - if (!port->membase) - return -EINVAL; - - err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc), - "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY; - - if (err && (port->flags & UPF_IOREMAP)) { - iounmap(port->membase); - port->membase = NULL; - } - - return err; -} - -static void -mpc52xx_uart_config_port(struct uart_port *port, int flags) -{ - if ((flags & UART_CONFIG_TYPE) - && (mpc52xx_uart_request_port(port) == 0)) - port->type = PORT_MPC52xx; -} - -static int -mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPC52xx) - return -EINVAL; - - if ((ser->irq != port->irq) || - (ser->io_type != UPIO_MEM) || - (ser->baud_base != port->uartclk) || - (ser->iomem_base != (void *)port->mapbase) || - (ser->hub6 != 0)) - return -EINVAL; - - return 0; -} - - -static struct uart_ops mpc52xx_uart_ops = { - .tx_empty = mpc52xx_uart_tx_empty, - .set_mctrl = mpc52xx_uart_set_mctrl, - .get_mctrl = mpc52xx_uart_get_mctrl, - .stop_tx = mpc52xx_uart_stop_tx, - .start_tx = mpc52xx_uart_start_tx, - .send_xchar = mpc52xx_uart_send_xchar, - .stop_rx = mpc52xx_uart_stop_rx, - .enable_ms = mpc52xx_uart_enable_ms, - .break_ctl = mpc52xx_uart_break_ctl, - .startup = mpc52xx_uart_startup, - .shutdown = mpc52xx_uart_shutdown, - .set_termios = mpc52xx_uart_set_termios, -/* .pm = mpc52xx_uart_pm, Not supported yet */ -/* .set_wake = mpc52xx_uart_set_wake, Not supported yet */ - .type = mpc52xx_uart_type, - .release_port = mpc52xx_uart_release_port, - .request_port = mpc52xx_uart_request_port, - .config_port = mpc52xx_uart_config_port, - .verify_port = mpc52xx_uart_verify_port -}; - - -/* ======================================================================== */ -/* Interrupt handling */ -/* ======================================================================== */ - -static inline int -mpc52xx_uart_int_rx_chars(struct uart_port *port) -{ - struct tty_struct *tty = port->state->port.tty; - unsigned char ch, flag; - unsigned short status; - - /* While we can read, do so ! */ - while (psc_ops->raw_rx_rdy(port)) { - /* Get the char */ - ch = psc_ops->read_char(port); - - /* Handle sysreq char */ -#ifdef SUPPORT_SYSRQ - if (uart_handle_sysrq_char(port, ch)) { - port->sysrq = 0; - continue; - } -#endif - - /* Store it */ - - flag = TTY_NORMAL; - port->icount.rx++; - - status = in_be16(&PSC(port)->mpc52xx_psc_status); - - if (status & (MPC52xx_PSC_SR_PE | - MPC52xx_PSC_SR_FE | - MPC52xx_PSC_SR_RB)) { - - if (status & MPC52xx_PSC_SR_RB) { - flag = TTY_BREAK; - uart_handle_break(port); - port->icount.brk++; - } else if (status & MPC52xx_PSC_SR_PE) { - flag = TTY_PARITY; - port->icount.parity++; - } - else if (status & MPC52xx_PSC_SR_FE) { - flag = TTY_FRAME; - port->icount.frame++; - } - - /* Clear error condition */ - out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT); - - } - tty_insert_flip_char(tty, ch, flag); - if (status & MPC52xx_PSC_SR_OE) { - /* - * Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - port->icount.overrun++; - } - } - - spin_unlock(&port->lock); - tty_flip_buffer_push(tty); - spin_lock(&port->lock); - - return psc_ops->raw_rx_rdy(port); -} - -static inline int -mpc52xx_uart_int_tx_chars(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - /* Process out of band chars */ - if (port->x_char) { - psc_ops->write_char(port, port->x_char); - port->icount.tx++; - port->x_char = 0; - return 1; - } - - /* Nothing to do ? */ - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - mpc52xx_uart_stop_tx(port); - return 0; - } - - /* Send chars */ - while (psc_ops->raw_tx_rdy(port)) { - psc_ops->write_char(port, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - /* Wake up */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - /* Maybe we're done after all */ - if (uart_circ_empty(xmit)) { - mpc52xx_uart_stop_tx(port); - return 0; - } - - return 1; -} - -static irqreturn_t -mpc5xxx_uart_process_int(struct uart_port *port) -{ - unsigned long pass = ISR_PASS_LIMIT; - unsigned int keepgoing; - u8 status; - - /* While we have stuff to do, we continue */ - do { - /* If we don't find anything to do, we stop */ - keepgoing = 0; - - psc_ops->rx_clr_irq(port); - if (psc_ops->rx_rdy(port)) - keepgoing |= mpc52xx_uart_int_rx_chars(port); - - psc_ops->tx_clr_irq(port); - if (psc_ops->tx_rdy(port)) - keepgoing |= mpc52xx_uart_int_tx_chars(port); - - status = in_8(&PSC(port)->mpc52xx_psc_ipcr); - if (status & MPC52xx_PSC_D_DCD) - uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD)); - - if (status & MPC52xx_PSC_D_CTS) - uart_handle_cts_change(port, !(status & MPC52xx_PSC_CTS)); - - /* Limit number of iteration */ - if (!(--pass)) - keepgoing = 0; - - } while (keepgoing); - - return IRQ_HANDLED; -} - -static irqreturn_t -mpc52xx_uart_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - irqreturn_t ret; - - spin_lock(&port->lock); - - ret = psc_ops->handle_irq(port); - - spin_unlock(&port->lock); - - return ret; -} - -/* ======================================================================== */ -/* Console ( if applicable ) */ -/* ======================================================================== */ - -#ifdef CONFIG_SERIAL_MPC52xx_CONSOLE - -static void __init -mpc52xx_console_get_options(struct uart_port *port, - int *baud, int *parity, int *bits, int *flow) -{ - struct mpc52xx_psc __iomem *psc = PSC(port); - unsigned char mr1; - - pr_debug("mpc52xx_console_get_options(port=%p)\n", port); - - /* Read the mode registers */ - out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); - mr1 = in_8(&psc->mode); - - /* CT{U,L}R are write-only ! */ - *baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; - - /* Parse them */ - switch (mr1 & MPC52xx_PSC_MODE_BITS_MASK) { - case MPC52xx_PSC_MODE_5_BITS: - *bits = 5; - break; - case MPC52xx_PSC_MODE_6_BITS: - *bits = 6; - break; - case MPC52xx_PSC_MODE_7_BITS: - *bits = 7; - break; - case MPC52xx_PSC_MODE_8_BITS: - default: - *bits = 8; - } - - if (mr1 & MPC52xx_PSC_MODE_PARNONE) - *parity = 'n'; - else - *parity = mr1 & MPC52xx_PSC_MODE_PARODD ? 'o' : 'e'; -} - -static void -mpc52xx_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_port *port = &mpc52xx_uart_ports[co->index]; - unsigned int i, j; - - /* Disable interrupts */ - psc_ops->cw_disable_ints(port); - - /* Wait the TX buffer to be empty */ - j = 5000000; /* Maximum wait */ - while (!mpc52xx_uart_tx_empty(port) && --j) - udelay(1); - - /* Write all the chars */ - for (i = 0; i < count; i++, s++) { - /* Line return handling */ - if (*s == '\n') - psc_ops->write_char(port, '\r'); - - /* Send the char */ - psc_ops->write_char(port, *s); - - /* Wait the TX buffer to be empty */ - j = 20000; /* Maximum wait */ - while (!mpc52xx_uart_tx_empty(port) && --j) - udelay(1); - } - - /* Restore interrupt state */ - psc_ops->cw_restore_ints(port); -} - - -static int __init -mpc52xx_console_setup(struct console *co, char *options) -{ - struct uart_port *port = &mpc52xx_uart_ports[co->index]; - struct device_node *np = mpc52xx_uart_nodes[co->index]; - unsigned int uartclk; - struct resource res; - int ret; - - int baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - pr_debug("mpc52xx_console_setup co=%p, co->index=%i, options=%s\n", - co, co->index, options); - - if ((co->index < 0) || (co->index >= MPC52xx_PSC_MAXNUM)) { - pr_debug("PSC%x out of range\n", co->index); - return -EINVAL; - } - - if (!np) { - pr_debug("PSC%x not found in device tree\n", co->index); - return -EINVAL; - } - - pr_debug("Console on ttyPSC%x is %s\n", - co->index, mpc52xx_uart_nodes[co->index]->full_name); - - /* Fetch register locations */ - ret = of_address_to_resource(np, 0, &res); - if (ret) { - pr_debug("Could not get resources for PSC%x\n", co->index); - return ret; - } - - uartclk = mpc5xxx_get_bus_frequency(np); - if (uartclk == 0) { - pr_debug("Could not find uart clock frequency!\n"); - return -EINVAL; - } - - /* Basic port init. Needed since we use some uart_??? func before - * real init for early access */ - spin_lock_init(&port->lock); - port->uartclk = uartclk; - port->ops = &mpc52xx_uart_ops; - port->mapbase = res.start; - port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc)); - port->irq = irq_of_parse_and_map(np, 0); - - if (port->membase == NULL) - return -EINVAL; - - pr_debug("mpc52xx-psc uart at %p, mapped to %p, irq=%x, freq=%i\n", - (void *)port->mapbase, port->membase, - port->irq, port->uartclk); - - /* Setup the port parameters accoding to options */ - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - mpc52xx_console_get_options(port, &baud, &parity, &bits, &flow); - - pr_debug("Setting console parameters: %i %i%c1 flow=%c\n", - baud, bits, parity, flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - - -static struct uart_driver mpc52xx_uart_driver; - -static struct console mpc52xx_console = { - .name = "ttyPSC", - .write = mpc52xx_console_write, - .device = uart_console_device, - .setup = mpc52xx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, /* Specified on the cmdline (e.g. console=ttyPSC0) */ - .data = &mpc52xx_uart_driver, -}; - - -static int __init -mpc52xx_console_init(void) -{ - mpc52xx_uart_of_enumerate(); - register_console(&mpc52xx_console); - return 0; -} - -console_initcall(mpc52xx_console_init); - -#define MPC52xx_PSC_CONSOLE &mpc52xx_console -#else -#define MPC52xx_PSC_CONSOLE NULL -#endif - - -/* ======================================================================== */ -/* UART Driver */ -/* ======================================================================== */ - -static struct uart_driver mpc52xx_uart_driver = { - .driver_name = "mpc52xx_psc_uart", - .dev_name = "ttyPSC", - .major = SERIAL_PSC_MAJOR, - .minor = SERIAL_PSC_MINOR, - .nr = MPC52xx_PSC_MAXNUM, - .cons = MPC52xx_PSC_CONSOLE, -}; - -/* ======================================================================== */ -/* OF Platform Driver */ -/* ======================================================================== */ - -static struct of_device_id mpc52xx_uart_of_match[] = { -#ifdef CONFIG_PPC_MPC52xx - { .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, }, - { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, - /* binding used by old lite5200 device trees: */ - { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, - /* binding used by efika: */ - { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, -#endif -#ifdef CONFIG_PPC_MPC512x - { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, -#endif - {}, -}; - -static int __devinit -mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *match) -{ - int idx = -1; - unsigned int uartclk; - struct uart_port *port = NULL; - struct resource res; - int ret; - - dev_dbg(&op->dev, "mpc52xx_uart_probe(op=%p, match=%p)\n", op, match); - - /* Check validity & presence */ - for (idx = 0; idx < MPC52xx_PSC_MAXNUM; idx++) - if (mpc52xx_uart_nodes[idx] == op->dev.of_node) - break; - if (idx >= MPC52xx_PSC_MAXNUM) - return -EINVAL; - pr_debug("Found %s assigned to ttyPSC%x\n", - mpc52xx_uart_nodes[idx]->full_name, idx); - - /* set the uart clock to the input clock of the psc, the different - * prescalers are taken into account in the set_baudrate() methods - * of the respective chip */ - uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node); - if (uartclk == 0) { - dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); - return -EINVAL; - } - - /* Init the port structure */ - port = &mpc52xx_uart_ports[idx]; - - spin_lock_init(&port->lock); - port->uartclk = uartclk; - port->fifosize = 512; - port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF | - (uart_console(port) ? 0 : UPF_IOREMAP); - port->line = idx; - port->ops = &mpc52xx_uart_ops; - port->dev = &op->dev; - - /* Search for IRQ and mapbase */ - ret = of_address_to_resource(op->dev.of_node, 0, &res); - if (ret) - return ret; - - port->mapbase = res.start; - if (!port->mapbase) { - dev_dbg(&op->dev, "Could not allocate resources for PSC\n"); - return -EINVAL; - } - - psc_ops->get_irq(port, op->dev.of_node); - if (port->irq == NO_IRQ) { - dev_dbg(&op->dev, "Could not get irq\n"); - return -EINVAL; - } - - dev_dbg(&op->dev, "mpc52xx-psc uart at %p, irq=%x, freq=%i\n", - (void *)port->mapbase, port->irq, port->uartclk); - - /* Add the port to the uart sub-system */ - ret = uart_add_one_port(&mpc52xx_uart_driver, port); - if (ret) - return ret; - - dev_set_drvdata(&op->dev, (void *)port); - return 0; -} - -static int -mpc52xx_uart_of_remove(struct platform_device *op) -{ - struct uart_port *port = dev_get_drvdata(&op->dev); - dev_set_drvdata(&op->dev, NULL); - - if (port) - uart_remove_one_port(&mpc52xx_uart_driver, port); - - return 0; -} - -#ifdef CONFIG_PM -static int -mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) -{ - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); - - if (port) - uart_suspend_port(&mpc52xx_uart_driver, port); - - return 0; -} - -static int -mpc52xx_uart_of_resume(struct platform_device *op) -{ - struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); - - if (port) - uart_resume_port(&mpc52xx_uart_driver, port); - - return 0; -} -#endif - -static void -mpc52xx_uart_of_assign(struct device_node *np) -{ - int i; - - /* Find the first free PSC number */ - for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) { - if (mpc52xx_uart_nodes[i] == NULL) { - of_node_get(np); - mpc52xx_uart_nodes[i] = np; - return; - } - } -} - -static void -mpc52xx_uart_of_enumerate(void) -{ - static int enum_done; - struct device_node *np; - const struct of_device_id *match; - int i; - - if (enum_done) - return; - - /* Assign index to each PSC in device tree */ - for_each_matching_node(np, mpc52xx_uart_of_match) { - match = of_match_node(mpc52xx_uart_of_match, np); - psc_ops = match->data; - mpc52xx_uart_of_assign(np); - } - - enum_done = 1; - - for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) { - if (mpc52xx_uart_nodes[i]) - pr_debug("%s assigned to ttyPSC%x\n", - mpc52xx_uart_nodes[i]->full_name, i); - } -} - -MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); - -static struct of_platform_driver mpc52xx_uart_of_driver = { - .probe = mpc52xx_uart_of_probe, - .remove = mpc52xx_uart_of_remove, -#ifdef CONFIG_PM - .suspend = mpc52xx_uart_of_suspend, - .resume = mpc52xx_uart_of_resume, -#endif - .driver = { - .name = "mpc52xx-psc-uart", - .owner = THIS_MODULE, - .of_match_table = mpc52xx_uart_of_match, - }, -}; - - -/* ======================================================================== */ -/* Module */ -/* ======================================================================== */ - -static int __init -mpc52xx_uart_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: MPC52xx PSC UART driver\n"); - - ret = uart_register_driver(&mpc52xx_uart_driver); - if (ret) { - printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", - __FILE__, ret); - return ret; - } - - mpc52xx_uart_of_enumerate(); - - /* - * Map the PSC FIFO Controller and init if on MPC512x. - */ - if (psc_ops && psc_ops->fifoc_init) { - ret = psc_ops->fifoc_init(); - if (ret) - return ret; - } - - ret = of_register_platform_driver(&mpc52xx_uart_of_driver); - if (ret) { - printk(KERN_ERR "%s: of_register_platform_driver failed (%i)\n", - __FILE__, ret); - uart_unregister_driver(&mpc52xx_uart_driver); - return ret; - } - - return 0; -} - -static void __exit -mpc52xx_uart_exit(void) -{ - if (psc_ops->fifoc_uninit) - psc_ops->fifoc_uninit(); - - of_unregister_platform_driver(&mpc52xx_uart_of_driver); - uart_unregister_driver(&mpc52xx_uart_driver); -} - - -module_init(mpc52xx_uart_init); -module_exit(mpc52xx_uart_exit); - -MODULE_AUTHOR("Sylvain Munaut "); -MODULE_DESCRIPTION("Freescale MPC52xx PSC UART"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c deleted file mode 100644 index 6a9c6605666a..000000000000 --- a/drivers/serial/mpsc.c +++ /dev/null @@ -1,2159 +0,0 @@ -/* - * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240, - * GT64260, MV64340, MV64360, GT96100, ... ). - * - * Author: Mark A. Greer - * - * Based on an old MPSC driver that was in the linuxppc tree. It appears to - * have been created by Chris Zankel (formerly of MontaVista) but there - * is no proper Copyright so I'm not sure. Apparently, parts were also - * taken from PPCBoot (now U-Boot). Also based on drivers/serial/8250.c - * by Russell King. - * - * 2004 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -/* - * The MPSC interface is much like a typical network controller's interface. - * That is, you set up separate rings of descriptors for transmitting and - * receiving data. There is also a pool of buffers with (one buffer per - * descriptor) that incoming data are dma'd into or outgoing data are dma'd - * out of. - * - * The MPSC requires two other controllers to be able to work. The Baud Rate - * Generator (BRG) provides a clock at programmable frequencies which determines - * the baud rate. The Serial DMA Controller (SDMA) takes incoming data from the - * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the - * MPSC. It is actually the SDMA interrupt that the driver uses to keep the - * transmit and receive "engines" going (i.e., indicate data has been - * transmitted or received). - * - * NOTES: - * - * 1) Some chips have an erratum where several regs cannot be - * read. To work around that, we keep a local copy of those regs in - * 'mpsc_port_info'. - * - * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr - * accesses system mem with coherency enabled. For that reason, the driver - * assumes that coherency for that ctlr has been disabled. This means - * that when in a cache coherent system, the driver has to manually manage - * the data cache on the areas that it touches because the dma_* macro are - * basically no-ops. - * - * 3) There is an erratum (on PPC) where you can't use the instruction to do - * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places - * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed. - * - * 4) AFAICT, hardware flow control isn't supported by the controller --MAG. - */ - - -#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define MPSC_NUM_CTLRS 2 - -/* - * Descriptors and buffers must be cache line aligned. - * Buffers lengths must be multiple of cache line size. - * Number of Tx & Rx descriptors must be powers of 2. - */ -#define MPSC_RXR_ENTRIES 32 -#define MPSC_RXRE_SIZE dma_get_cache_alignment() -#define MPSC_RXR_SIZE (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE) -#define MPSC_RXBE_SIZE dma_get_cache_alignment() -#define MPSC_RXB_SIZE (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE) - -#define MPSC_TXR_ENTRIES 32 -#define MPSC_TXRE_SIZE dma_get_cache_alignment() -#define MPSC_TXR_SIZE (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE) -#define MPSC_TXBE_SIZE dma_get_cache_alignment() -#define MPSC_TXB_SIZE (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE) - -#define MPSC_DMA_ALLOC_SIZE (MPSC_RXR_SIZE + MPSC_RXB_SIZE + MPSC_TXR_SIZE \ - + MPSC_TXB_SIZE + dma_get_cache_alignment() /* for alignment */) - -/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */ -struct mpsc_rx_desc { - u16 bufsize; - u16 bytecnt; - u32 cmdstat; - u32 link; - u32 buf_ptr; -} __attribute((packed)); - -struct mpsc_tx_desc { - u16 bytecnt; - u16 shadow; - u32 cmdstat; - u32 link; - u32 buf_ptr; -} __attribute((packed)); - -/* - * Some regs that have the erratum that you can't read them are are shared - * between the two MPSC controllers. This struct contains those shared regs. - */ -struct mpsc_shared_regs { - phys_addr_t mpsc_routing_base_p; - phys_addr_t sdma_intr_base_p; - - void __iomem *mpsc_routing_base; - void __iomem *sdma_intr_base; - - u32 MPSC_MRR_m; - u32 MPSC_RCRR_m; - u32 MPSC_TCRR_m; - u32 SDMA_INTR_CAUSE_m; - u32 SDMA_INTR_MASK_m; -}; - -/* The main driver data structure */ -struct mpsc_port_info { - struct uart_port port; /* Overlay uart_port structure */ - - /* Internal driver state for this ctlr */ - u8 ready; - u8 rcv_data; - tcflag_t c_iflag; /* save termios->c_iflag */ - tcflag_t c_cflag; /* save termios->c_cflag */ - - /* Info passed in from platform */ - u8 mirror_regs; /* Need to mirror regs? */ - u8 cache_mgmt; /* Need manual cache mgmt? */ - u8 brg_can_tune; /* BRG has baud tuning? */ - u32 brg_clk_src; - u16 mpsc_max_idle; - int default_baud; - int default_bits; - int default_parity; - int default_flow; - - /* Physical addresses of various blocks of registers (from platform) */ - phys_addr_t mpsc_base_p; - phys_addr_t sdma_base_p; - phys_addr_t brg_base_p; - - /* Virtual addresses of various blocks of registers (from platform) */ - void __iomem *mpsc_base; - void __iomem *sdma_base; - void __iomem *brg_base; - - /* Descriptor ring and buffer allocations */ - void *dma_region; - dma_addr_t dma_region_p; - - dma_addr_t rxr; /* Rx descriptor ring */ - dma_addr_t rxr_p; /* Phys addr of rxr */ - u8 *rxb; /* Rx Ring I/O buf */ - u8 *rxb_p; /* Phys addr of rxb */ - u32 rxr_posn; /* First desc w/ Rx data */ - - dma_addr_t txr; /* Tx descriptor ring */ - dma_addr_t txr_p; /* Phys addr of txr */ - u8 *txb; /* Tx Ring I/O buf */ - u8 *txb_p; /* Phys addr of txb */ - int txr_head; /* Where new data goes */ - int txr_tail; /* Where sent data comes off */ - spinlock_t tx_lock; /* transmit lock */ - - /* Mirrored values of regs we can't read (if 'mirror_regs' set) */ - u32 MPSC_MPCR_m; - u32 MPSC_CHR_1_m; - u32 MPSC_CHR_2_m; - u32 MPSC_CHR_10_m; - u32 BRG_BCR_m; - struct mpsc_shared_regs *shared_regs; -}; - -/* Hooks to platform-specific code */ -int mpsc_platform_register_driver(void); -void mpsc_platform_unregister_driver(void); - -/* Hooks back in to mpsc common to be called by platform-specific code */ -struct mpsc_port_info *mpsc_device_probe(int index); -struct mpsc_port_info *mpsc_device_remove(int index); - -/* Main MPSC Configuration Register Offsets */ -#define MPSC_MMCRL 0x0000 -#define MPSC_MMCRH 0x0004 -#define MPSC_MPCR 0x0008 -#define MPSC_CHR_1 0x000c -#define MPSC_CHR_2 0x0010 -#define MPSC_CHR_3 0x0014 -#define MPSC_CHR_4 0x0018 -#define MPSC_CHR_5 0x001c -#define MPSC_CHR_6 0x0020 -#define MPSC_CHR_7 0x0024 -#define MPSC_CHR_8 0x0028 -#define MPSC_CHR_9 0x002c -#define MPSC_CHR_10 0x0030 -#define MPSC_CHR_11 0x0034 - -#define MPSC_MPCR_FRZ (1 << 9) -#define MPSC_MPCR_CL_5 0 -#define MPSC_MPCR_CL_6 1 -#define MPSC_MPCR_CL_7 2 -#define MPSC_MPCR_CL_8 3 -#define MPSC_MPCR_SBL_1 0 -#define MPSC_MPCR_SBL_2 1 - -#define MPSC_CHR_2_TEV (1<<1) -#define MPSC_CHR_2_TA (1<<7) -#define MPSC_CHR_2_TTCS (1<<9) -#define MPSC_CHR_2_REV (1<<17) -#define MPSC_CHR_2_RA (1<<23) -#define MPSC_CHR_2_CRD (1<<25) -#define MPSC_CHR_2_EH (1<<31) -#define MPSC_CHR_2_PAR_ODD 0 -#define MPSC_CHR_2_PAR_SPACE 1 -#define MPSC_CHR_2_PAR_EVEN 2 -#define MPSC_CHR_2_PAR_MARK 3 - -/* MPSC Signal Routing */ -#define MPSC_MRR 0x0000 -#define MPSC_RCRR 0x0004 -#define MPSC_TCRR 0x0008 - -/* Serial DMA Controller Interface Registers */ -#define SDMA_SDC 0x0000 -#define SDMA_SDCM 0x0008 -#define SDMA_RX_DESC 0x0800 -#define SDMA_RX_BUF_PTR 0x0808 -#define SDMA_SCRDP 0x0810 -#define SDMA_TX_DESC 0x0c00 -#define SDMA_SCTDP 0x0c10 -#define SDMA_SFTDP 0x0c14 - -#define SDMA_DESC_CMDSTAT_PE (1<<0) -#define SDMA_DESC_CMDSTAT_CDL (1<<1) -#define SDMA_DESC_CMDSTAT_FR (1<<3) -#define SDMA_DESC_CMDSTAT_OR (1<<6) -#define SDMA_DESC_CMDSTAT_BR (1<<9) -#define SDMA_DESC_CMDSTAT_MI (1<<10) -#define SDMA_DESC_CMDSTAT_A (1<<11) -#define SDMA_DESC_CMDSTAT_AM (1<<12) -#define SDMA_DESC_CMDSTAT_CT (1<<13) -#define SDMA_DESC_CMDSTAT_C (1<<14) -#define SDMA_DESC_CMDSTAT_ES (1<<15) -#define SDMA_DESC_CMDSTAT_L (1<<16) -#define SDMA_DESC_CMDSTAT_F (1<<17) -#define SDMA_DESC_CMDSTAT_P (1<<18) -#define SDMA_DESC_CMDSTAT_EI (1<<23) -#define SDMA_DESC_CMDSTAT_O (1<<31) - -#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O \ - | SDMA_DESC_CMDSTAT_EI) - -#define SDMA_SDC_RFT (1<<0) -#define SDMA_SDC_SFM (1<<1) -#define SDMA_SDC_BLMR (1<<6) -#define SDMA_SDC_BLMT (1<<7) -#define SDMA_SDC_POVR (1<<8) -#define SDMA_SDC_RIFB (1<<9) - -#define SDMA_SDCM_ERD (1<<7) -#define SDMA_SDCM_AR (1<<15) -#define SDMA_SDCM_STD (1<<16) -#define SDMA_SDCM_TXD (1<<23) -#define SDMA_SDCM_AT (1<<31) - -#define SDMA_0_CAUSE_RXBUF (1<<0) -#define SDMA_0_CAUSE_RXERR (1<<1) -#define SDMA_0_CAUSE_TXBUF (1<<2) -#define SDMA_0_CAUSE_TXEND (1<<3) -#define SDMA_1_CAUSE_RXBUF (1<<8) -#define SDMA_1_CAUSE_RXERR (1<<9) -#define SDMA_1_CAUSE_TXBUF (1<<10) -#define SDMA_1_CAUSE_TXEND (1<<11) - -#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR \ - | SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR) -#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND \ - | SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND) - -/* SDMA Interrupt registers */ -#define SDMA_INTR_CAUSE 0x0000 -#define SDMA_INTR_MASK 0x0080 - -/* Baud Rate Generator Interface Registers */ -#define BRG_BCR 0x0000 -#define BRG_BTR 0x0004 - -/* - * Define how this driver is known to the outside (we've been assigned a - * range on the "Low-density serial ports" major). - */ -#define MPSC_MAJOR 204 -#define MPSC_MINOR_START 44 -#define MPSC_DRIVER_NAME "MPSC" -#define MPSC_DEV_NAME "ttyMM" -#define MPSC_VERSION "1.00" - -static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS]; -static struct mpsc_shared_regs mpsc_shared_regs; -static struct uart_driver mpsc_reg; - -static void mpsc_start_rx(struct mpsc_port_info *pi); -static void mpsc_free_ring_mem(struct mpsc_port_info *pi); -static void mpsc_release_port(struct uart_port *port); -/* - ****************************************************************************** - * - * Baud Rate Generator Routines (BRG) - * - ****************************************************************************** - */ -static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18); - - if (pi->brg_can_tune) - v &= ~(1 << 25); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); - - writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000, - pi->brg_base + BRG_BTR); -} - -static void mpsc_brg_enable(struct mpsc_port_info *pi) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v |= (1 << 16); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); -} - -static void mpsc_brg_disable(struct mpsc_port_info *pi) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v &= ~(1 << 16); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); -} - -/* - * To set the baud, we adjust the CDV field in the BRG_BCR reg. - * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1. - * However, the input clock is divided by 16 in the MPSC b/c of how - * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our - * calculation by 16 to account for that. So the real calculation - * that accounts for the way the mpsc is set up is: - * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1. - */ -static void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud) -{ - u32 cdv = (pi->port.uartclk / (baud << 5)) - 1; - u32 v; - - mpsc_brg_disable(pi); - v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); - v = (v & 0xffff0000) | (cdv & 0xffff); - - if (pi->mirror_regs) - pi->BRG_BCR_m = v; - writel(v, pi->brg_base + BRG_BCR); - mpsc_brg_enable(pi); -} - -/* - ****************************************************************************** - * - * Serial DMA Routines (SDMA) - * - ****************************************************************************** - */ - -static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size) -{ - u32 v; - - pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n", - pi->port.line, burst_size); - - burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */ - - if (burst_size < 2) - v = 0x0; /* 1 64-bit word */ - else if (burst_size < 4) - v = 0x1; /* 2 64-bit words */ - else if (burst_size < 8) - v = 0x2; /* 4 64-bit words */ - else - v = 0x3; /* 8 64-bit words */ - - writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12), - pi->sdma_base + SDMA_SDC); -} - -static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size) -{ - pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line, - burst_size); - - writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f, - pi->sdma_base + SDMA_SDC); - mpsc_sdma_burstsize(pi, burst_size); -} - -static u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask) -{ - u32 old, v; - - pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask); - - old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m : - readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); - - mask &= 0xf; - if (pi->port.line) - mask <<= 8; - v &= ~mask; - - if (pi->mirror_regs) - pi->shared_regs->SDMA_INTR_MASK_m = v; - writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); - - if (pi->port.line) - old >>= 8; - return old & 0xf; -} - -static void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask) -{ - u32 v; - - pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask); - - v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m - : readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); - - mask &= 0xf; - if (pi->port.line) - mask <<= 8; - v |= mask; - - if (pi->mirror_regs) - pi->shared_regs->SDMA_INTR_MASK_m = v; - writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); -} - -static void mpsc_sdma_intr_ack(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line); - - if (pi->mirror_regs) - pi->shared_regs->SDMA_INTR_CAUSE_m = 0; - writeb(0x00, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE - + pi->port.line); -} - -static void mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi, - struct mpsc_rx_desc *rxre_p) -{ - pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n", - pi->port.line, (u32)rxre_p); - - writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP); -} - -static void mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi, - struct mpsc_tx_desc *txre_p) -{ - writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP); - writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP); -} - -static void mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val) -{ - u32 v; - - v = readl(pi->sdma_base + SDMA_SDCM); - if (val) - v |= val; - else - v = 0; - wmb(); - writel(v, pi->sdma_base + SDMA_SDCM); - wmb(); -} - -static uint mpsc_sdma_tx_active(struct mpsc_port_info *pi) -{ - return readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_TXD; -} - -static void mpsc_sdma_start_tx(struct mpsc_port_info *pi) -{ - struct mpsc_tx_desc *txre, *txre_p; - - /* If tx isn't running & there's a desc ready to go, start it */ - if (!mpsc_sdma_tx_active(pi)) { - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_tail * MPSC_TXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif - - if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) { - txre_p = (struct mpsc_tx_desc *) - (pi->txr_p + (pi->txr_tail * MPSC_TXRE_SIZE)); - - mpsc_sdma_set_tx_ring(pi, txre_p); - mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD); - } - } -} - -static void mpsc_sdma_stop(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line); - - /* Abort any SDMA transfers */ - mpsc_sdma_cmd(pi, 0); - mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT); - - /* Clear the SDMA current and first TX and RX pointers */ - mpsc_sdma_set_tx_ring(pi, NULL); - mpsc_sdma_set_rx_ring(pi, NULL); - - /* Disable interrupts */ - mpsc_sdma_intr_mask(pi, 0xf); - mpsc_sdma_intr_ack(pi); -} - -/* - ****************************************************************************** - * - * Multi-Protocol Serial Controller Routines (MPSC) - * - ****************************************************************************** - */ - -static void mpsc_hw_init(struct mpsc_port_info *pi) -{ - u32 v; - - pr_debug("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line); - - /* Set up clock routing */ - if (pi->mirror_regs) { - v = pi->shared_regs->MPSC_MRR_m; - v &= ~0x1c7; - pi->shared_regs->MPSC_MRR_m = v; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR); - - v = pi->shared_regs->MPSC_RCRR_m; - v = (v & ~0xf0f) | 0x100; - pi->shared_regs->MPSC_RCRR_m = v; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR); - - v = pi->shared_regs->MPSC_TCRR_m; - v = (v & ~0xf0f) | 0x100; - pi->shared_regs->MPSC_TCRR_m = v; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR); - } else { - v = readl(pi->shared_regs->mpsc_routing_base + MPSC_MRR); - v &= ~0x1c7; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR); - - v = readl(pi->shared_regs->mpsc_routing_base + MPSC_RCRR); - v = (v & ~0xf0f) | 0x100; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR); - - v = readl(pi->shared_regs->mpsc_routing_base + MPSC_TCRR); - v = (v & ~0xf0f) | 0x100; - writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR); - } - - /* Put MPSC in UART mode & enabel Tx/Rx egines */ - writel(0x000004c4, pi->mpsc_base + MPSC_MMCRL); - - /* No preamble, 16x divider, low-latency, */ - writel(0x04400400, pi->mpsc_base + MPSC_MMCRH); - mpsc_set_baudrate(pi, pi->default_baud); - - if (pi->mirror_regs) { - pi->MPSC_CHR_1_m = 0; - pi->MPSC_CHR_2_m = 0; - } - writel(0, pi->mpsc_base + MPSC_CHR_1); - writel(0, pi->mpsc_base + MPSC_CHR_2); - writel(pi->mpsc_max_idle, pi->mpsc_base + MPSC_CHR_3); - writel(0, pi->mpsc_base + MPSC_CHR_4); - writel(0, pi->mpsc_base + MPSC_CHR_5); - writel(0, pi->mpsc_base + MPSC_CHR_6); - writel(0, pi->mpsc_base + MPSC_CHR_7); - writel(0, pi->mpsc_base + MPSC_CHR_8); - writel(0, pi->mpsc_base + MPSC_CHR_9); - writel(0, pi->mpsc_base + MPSC_CHR_10); -} - -static void mpsc_enter_hunt(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line); - - if (pi->mirror_regs) { - writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_EH, - pi->mpsc_base + MPSC_CHR_2); - /* Erratum prevents reading CHR_2 so just delay for a while */ - udelay(100); - } else { - writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_EH, - pi->mpsc_base + MPSC_CHR_2); - - while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_EH) - udelay(10); - } -} - -static void mpsc_freeze(struct mpsc_port_info *pi) -{ - u32 v; - - pr_debug("mpsc_freeze[%d]: Freezing\n", pi->port.line); - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - v |= MPSC_MPCR_FRZ; - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); -} - -static void mpsc_unfreeze(struct mpsc_port_info *pi) -{ - u32 v; - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - v &= ~MPSC_MPCR_FRZ; - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); - - pr_debug("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line); -} - -static void mpsc_set_char_length(struct mpsc_port_info *pi, u32 len) -{ - u32 v; - - pr_debug("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line,len); - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - v = (v & ~(0x3 << 12)) | ((len & 0x3) << 12); - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); -} - -static void mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len) -{ - u32 v; - - pr_debug("mpsc_set_stop_bit_length[%d]: stop bits: %d\n", - pi->port.line, len); - - v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : - readl(pi->mpsc_base + MPSC_MPCR); - - v = (v & ~(1 << 14)) | ((len & 0x1) << 14); - - if (pi->mirror_regs) - pi->MPSC_MPCR_m = v; - writel(v, pi->mpsc_base + MPSC_MPCR); -} - -static void mpsc_set_parity(struct mpsc_port_info *pi, u32 p) -{ - u32 v; - - pr_debug("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p); - - v = (pi->mirror_regs) ? pi->MPSC_CHR_2_m : - readl(pi->mpsc_base + MPSC_CHR_2); - - p &= 0x3; - v = (v & ~0xc000c) | (p << 18) | (p << 2); - - if (pi->mirror_regs) - pi->MPSC_CHR_2_m = v; - writel(v, pi->mpsc_base + MPSC_CHR_2); -} - -/* - ****************************************************************************** - * - * Driver Init Routines - * - ****************************************************************************** - */ - -static void mpsc_init_hw(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_init_hw[%d]: Initializing\n", pi->port.line); - - mpsc_brg_init(pi, pi->brg_clk_src); - mpsc_brg_enable(pi); - mpsc_sdma_init(pi, dma_get_cache_alignment()); /* burst a cacheline */ - mpsc_sdma_stop(pi); - mpsc_hw_init(pi); -} - -static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) -{ - int rc = 0; - - pr_debug("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n", - pi->port.line); - - if (!pi->dma_region) { - if (!dma_supported(pi->port.dev, 0xffffffff)) { - printk(KERN_ERR "MPSC: Inadequate DMA support\n"); - rc = -ENXIO; - } else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev, - MPSC_DMA_ALLOC_SIZE, - &pi->dma_region_p, GFP_KERNEL)) - == NULL) { - printk(KERN_ERR "MPSC: Can't alloc Desc region\n"); - rc = -ENOMEM; - } - } - - return rc; -} - -static void mpsc_free_ring_mem(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line); - - if (pi->dma_region) { - dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE, - pi->dma_region, pi->dma_region_p); - pi->dma_region = NULL; - pi->dma_region_p = (dma_addr_t)NULL; - } -} - -static void mpsc_init_rings(struct mpsc_port_info *pi) -{ - struct mpsc_rx_desc *rxre; - struct mpsc_tx_desc *txre; - dma_addr_t dp, dp_p; - u8 *bp, *bp_p; - int i; - - pr_debug("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line); - - BUG_ON(pi->dma_region == NULL); - - memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE); - - /* - * Descriptors & buffers are multiples of cacheline size and must be - * cacheline aligned. - */ - dp = ALIGN((u32)pi->dma_region, dma_get_cache_alignment()); - dp_p = ALIGN((u32)pi->dma_region_p, dma_get_cache_alignment()); - - /* - * Partition dma region into rx ring descriptor, rx buffers, - * tx ring descriptors, and tx buffers. - */ - pi->rxr = dp; - pi->rxr_p = dp_p; - dp += MPSC_RXR_SIZE; - dp_p += MPSC_RXR_SIZE; - - pi->rxb = (u8 *)dp; - pi->rxb_p = (u8 *)dp_p; - dp += MPSC_RXB_SIZE; - dp_p += MPSC_RXB_SIZE; - - pi->rxr_posn = 0; - - pi->txr = dp; - pi->txr_p = dp_p; - dp += MPSC_TXR_SIZE; - dp_p += MPSC_TXR_SIZE; - - pi->txb = (u8 *)dp; - pi->txb_p = (u8 *)dp_p; - - pi->txr_head = 0; - pi->txr_tail = 0; - - /* Init rx ring descriptors */ - dp = pi->rxr; - dp_p = pi->rxr_p; - bp = pi->rxb; - bp_p = pi->rxb_p; - - for (i = 0; i < MPSC_RXR_ENTRIES; i++) { - rxre = (struct mpsc_rx_desc *)dp; - - rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE); - rxre->bytecnt = cpu_to_be16(0); - rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O - | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F - | SDMA_DESC_CMDSTAT_L); - rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE); - rxre->buf_ptr = cpu_to_be32(bp_p); - - dp += MPSC_RXRE_SIZE; - dp_p += MPSC_RXRE_SIZE; - bp += MPSC_RXBE_SIZE; - bp_p += MPSC_RXBE_SIZE; - } - rxre->link = cpu_to_be32(pi->rxr_p); /* Wrap last back to first */ - - /* Init tx ring descriptors */ - dp = pi->txr; - dp_p = pi->txr_p; - bp = pi->txb; - bp_p = pi->txb_p; - - for (i = 0; i < MPSC_TXR_ENTRIES; i++) { - txre = (struct mpsc_tx_desc *)dp; - - txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE); - txre->buf_ptr = cpu_to_be32(bp_p); - - dp += MPSC_TXRE_SIZE; - dp_p += MPSC_TXRE_SIZE; - bp += MPSC_TXBE_SIZE; - bp_p += MPSC_TXBE_SIZE; - } - txre->link = cpu_to_be32(pi->txr_p); /* Wrap last back to first */ - - dma_cache_sync(pi->port.dev, (void *)pi->dma_region, - MPSC_DMA_ALLOC_SIZE, DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)pi->dma_region, - (ulong)pi->dma_region - + MPSC_DMA_ALLOC_SIZE); -#endif - - return; -} - -static void mpsc_uninit_rings(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_uninit_rings[%d]: Uninitializing rings\n",pi->port.line); - - BUG_ON(pi->dma_region == NULL); - - pi->rxr = 0; - pi->rxr_p = 0; - pi->rxb = NULL; - pi->rxb_p = NULL; - pi->rxr_posn = 0; - - pi->txr = 0; - pi->txr_p = 0; - pi->txb = NULL; - pi->txb_p = NULL; - pi->txr_head = 0; - pi->txr_tail = 0; -} - -static int mpsc_make_ready(struct mpsc_port_info *pi) -{ - int rc; - - pr_debug("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line); - - if (!pi->ready) { - mpsc_init_hw(pi); - if ((rc = mpsc_alloc_ring_mem(pi))) - return rc; - mpsc_init_rings(pi); - pi->ready = 1; - } - - return 0; -} - -#ifdef CONFIG_CONSOLE_POLL -static int serial_polled; -#endif - -/* - ****************************************************************************** - * - * Interrupt Handling Routines - * - ****************************************************************************** - */ - -static int mpsc_rx_intr(struct mpsc_port_info *pi) -{ - struct mpsc_rx_desc *rxre; - struct tty_struct *tty = pi->port.state->port.tty; - u32 cmdstat, bytes_in, i; - int rc = 0; - u8 *bp; - char flag = TTY_NORMAL; - - pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); - - rxre = (struct mpsc_rx_desc *)(pi->rxr + (pi->rxr_posn*MPSC_RXRE_SIZE)); - - dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - - /* - * Loop through Rx descriptors handling ones that have been completed. - */ - while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) - & SDMA_DESC_CMDSTAT_O)) { - bytes_in = be16_to_cpu(rxre->bytecnt); -#ifdef CONFIG_CONSOLE_POLL - if (unlikely(serial_polled)) { - serial_polled = 0; - return 0; - } -#endif - /* Following use of tty struct directly is deprecated */ - if (unlikely(tty_buffer_request_room(tty, bytes_in) - < bytes_in)) { - if (tty->low_latency) - tty_flip_buffer_push(tty); - /* - * If this failed then we will throw away the bytes - * but must do so to clear interrupts. - */ - } - - bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); - dma_cache_sync(pi->port.dev, (void *)bp, MPSC_RXBE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)bp, - (ulong)bp + MPSC_RXBE_SIZE); -#endif - - /* - * Other than for parity error, the manual provides little - * info on what data will be in a frame flagged by any of - * these errors. For parity error, it is the last byte in - * the buffer that had the error. As for the rest, I guess - * we'll assume there is no data in the buffer. - * If there is...it gets lost. - */ - if (unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR - | SDMA_DESC_CMDSTAT_FR - | SDMA_DESC_CMDSTAT_OR))) { - - pi->port.icount.rx++; - - if (cmdstat & SDMA_DESC_CMDSTAT_BR) { /* Break */ - pi->port.icount.brk++; - - if (uart_handle_break(&pi->port)) - goto next_frame; - } else if (cmdstat & SDMA_DESC_CMDSTAT_FR) { - pi->port.icount.frame++; - } else if (cmdstat & SDMA_DESC_CMDSTAT_OR) { - pi->port.icount.overrun++; - } - - cmdstat &= pi->port.read_status_mask; - - if (cmdstat & SDMA_DESC_CMDSTAT_BR) - flag = TTY_BREAK; - else if (cmdstat & SDMA_DESC_CMDSTAT_FR) - flag = TTY_FRAME; - else if (cmdstat & SDMA_DESC_CMDSTAT_OR) - flag = TTY_OVERRUN; - else if (cmdstat & SDMA_DESC_CMDSTAT_PE) - flag = TTY_PARITY; - } - - if (uart_handle_sysrq_char(&pi->port, *bp)) { - bp++; - bytes_in--; -#ifdef CONFIG_CONSOLE_POLL - if (unlikely(serial_polled)) { - serial_polled = 0; - return 0; - } -#endif - goto next_frame; - } - - if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR - | SDMA_DESC_CMDSTAT_FR - | SDMA_DESC_CMDSTAT_OR))) - && !(cmdstat & pi->port.ignore_status_mask)) { - tty_insert_flip_char(tty, *bp, flag); - } else { - for (i=0; iport.icount.rx += bytes_in; - } - -next_frame: - rxre->bytecnt = cpu_to_be16(0); - wmb(); - rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O - | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F - | SDMA_DESC_CMDSTAT_L); - wmb(); - dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - - /* Advance to next descriptor */ - pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1); - rxre = (struct mpsc_rx_desc *) - (pi->rxr + (pi->rxr_posn * MPSC_RXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - rc = 1; - } - - /* Restart rx engine, if its stopped */ - if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) - mpsc_start_rx(pi); - - tty_flip_buffer_push(tty); - return rc; -} - -static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr) -{ - struct mpsc_tx_desc *txre; - - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_head * MPSC_TXRE_SIZE)); - - txre->bytecnt = cpu_to_be16(count); - txre->shadow = txre->bytecnt; - wmb(); /* ensure cmdstat is last field updated */ - txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F - | SDMA_DESC_CMDSTAT_L - | ((intr) ? SDMA_DESC_CMDSTAT_EI : 0)); - wmb(); - dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif -} - -static void mpsc_copy_tx_data(struct mpsc_port_info *pi) -{ - struct circ_buf *xmit = &pi->port.state->xmit; - u8 *bp; - u32 i; - - /* Make sure the desc ring isn't full */ - while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES) - < (MPSC_TXR_ENTRIES - 1)) { - if (pi->port.x_char) { - /* - * Ideally, we should use the TCS field in - * CHR_1 to put the x_char out immediately but - * errata prevents us from being able to read - * CHR_2 to know that its safe to write to - * CHR_1. Instead, just put it in-band with - * all the other Tx data. - */ - bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); - *bp = pi->port.x_char; - pi->port.x_char = 0; - i = 1; - } else if (!uart_circ_empty(xmit) - && !uart_tx_stopped(&pi->port)) { - i = min((u32)MPSC_TXBE_SIZE, - (u32)uart_circ_chars_pending(xmit)); - i = min(i, (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail, - UART_XMIT_SIZE)); - bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); - memcpy(bp, &xmit->buf[xmit->tail], i); - xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&pi->port); - } else { /* All tx data copied into ring bufs */ - return; - } - - dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)bp, - (ulong)bp + MPSC_TXBE_SIZE); -#endif - mpsc_setup_tx_desc(pi, i, 1); - - /* Advance to next descriptor */ - pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); - } -} - -static int mpsc_tx_intr(struct mpsc_port_info *pi) -{ - struct mpsc_tx_desc *txre; - int rc = 0; - unsigned long iflags; - - spin_lock_irqsave(&pi->tx_lock, iflags); - - if (!mpsc_sdma_tx_active(pi)) { - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_tail * MPSC_TXRE_SIZE)); - - dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, - DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif - - while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) { - rc = 1; - pi->port.icount.tx += be16_to_cpu(txre->bytecnt); - pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1); - - /* If no more data to tx, fall out of loop */ - if (pi->txr_head == pi->txr_tail) - break; - - txre = (struct mpsc_tx_desc *)(pi->txr - + (pi->txr_tail * MPSC_TXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)txre, - MPSC_TXRE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)txre, - (ulong)txre + MPSC_TXRE_SIZE); -#endif - } - - mpsc_copy_tx_data(pi); - mpsc_sdma_start_tx(pi); /* start next desc if ready */ - } - - spin_unlock_irqrestore(&pi->tx_lock, iflags); - return rc; -} - -/* - * This is the driver's interrupt handler. To avoid a race, we first clear - * the interrupt, then handle any completed Rx/Tx descriptors. When done - * handling those descriptors, we restart the Rx/Tx engines if they're stopped. - */ -static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id) -{ - struct mpsc_port_info *pi = dev_id; - ulong iflags; - int rc = IRQ_NONE; - - pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line); - - spin_lock_irqsave(&pi->port.lock, iflags); - mpsc_sdma_intr_ack(pi); - if (mpsc_rx_intr(pi)) - rc = IRQ_HANDLED; - if (mpsc_tx_intr(pi)) - rc = IRQ_HANDLED; - spin_unlock_irqrestore(&pi->port.lock, iflags); - - pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line); - return rc; -} - -/* - ****************************************************************************** - * - * serial_core.c Interface routines - * - ****************************************************************************** - */ -static uint mpsc_tx_empty(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - ulong iflags; - uint rc; - - spin_lock_irqsave(&pi->port.lock, iflags); - rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT; - spin_unlock_irqrestore(&pi->port.lock, iflags); - - return rc; -} - -static void mpsc_set_mctrl(struct uart_port *port, uint mctrl) -{ - /* Have no way to set modem control lines AFAICT */ -} - -static uint mpsc_get_mctrl(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - u32 mflags, status; - - status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m - : readl(pi->mpsc_base + MPSC_CHR_10); - - mflags = 0; - if (status & 0x1) - mflags |= TIOCM_CTS; - if (status & 0x2) - mflags |= TIOCM_CAR; - - return mflags | TIOCM_DSR; /* No way to tell if DSR asserted */ -} - -static void mpsc_stop_tx(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - - pr_debug("mpsc_stop_tx[%d]\n", port->line); - - mpsc_freeze(pi); -} - -static void mpsc_start_tx(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - unsigned long iflags; - - spin_lock_irqsave(&pi->tx_lock, iflags); - - mpsc_unfreeze(pi); - mpsc_copy_tx_data(pi); - mpsc_sdma_start_tx(pi); - - spin_unlock_irqrestore(&pi->tx_lock, iflags); - - pr_debug("mpsc_start_tx[%d]\n", port->line); -} - -static void mpsc_start_rx(struct mpsc_port_info *pi) -{ - pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line); - - if (pi->rcv_data) { - mpsc_enter_hunt(pi); - mpsc_sdma_cmd(pi, SDMA_SDCM_ERD); - } -} - -static void mpsc_stop_rx(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - - pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line); - - if (pi->mirror_regs) { - writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA, - pi->mpsc_base + MPSC_CHR_2); - /* Erratum prevents reading CHR_2 so just delay for a while */ - udelay(100); - } else { - writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA, - pi->mpsc_base + MPSC_CHR_2); - - while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA) - udelay(10); - } - - mpsc_sdma_cmd(pi, SDMA_SDCM_AR); -} - -static void mpsc_enable_ms(struct uart_port *port) -{ -} - -static void mpsc_break_ctl(struct uart_port *port, int ctl) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - ulong flags; - u32 v; - - v = ctl ? 0x00ff0000 : 0; - - spin_lock_irqsave(&pi->port.lock, flags); - if (pi->mirror_regs) - pi->MPSC_CHR_1_m = v; - writel(v, pi->mpsc_base + MPSC_CHR_1); - spin_unlock_irqrestore(&pi->port.lock, flags); -} - -static int mpsc_startup(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - u32 flag = 0; - int rc; - - pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n", - port->line, pi->port.irq); - - if ((rc = mpsc_make_ready(pi)) == 0) { - /* Setup IRQ handler */ - mpsc_sdma_intr_ack(pi); - - /* If irq's are shared, need to set flag */ - if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq) - flag = IRQF_SHARED; - - if (request_irq(pi->port.irq, mpsc_sdma_intr, flag, - "mpsc-sdma", pi)) - printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n", - pi->port.irq); - - mpsc_sdma_intr_unmask(pi, 0xf); - mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p - + (pi->rxr_posn * MPSC_RXRE_SIZE))); - } - - return rc; -} - -static void mpsc_shutdown(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - - pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line); - - mpsc_sdma_stop(pi); - free_irq(pi->port.irq, pi); -} - -static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - u32 baud; - ulong flags; - u32 chr_bits, stop_bits, par; - - pi->c_iflag = termios->c_iflag; - pi->c_cflag = termios->c_cflag; - - switch (termios->c_cflag & CSIZE) { - case CS5: - chr_bits = MPSC_MPCR_CL_5; - break; - case CS6: - chr_bits = MPSC_MPCR_CL_6; - break; - case CS7: - chr_bits = MPSC_MPCR_CL_7; - break; - case CS8: - default: - chr_bits = MPSC_MPCR_CL_8; - break; - } - - if (termios->c_cflag & CSTOPB) - stop_bits = MPSC_MPCR_SBL_2; - else - stop_bits = MPSC_MPCR_SBL_1; - - par = MPSC_CHR_2_PAR_EVEN; - if (termios->c_cflag & PARENB) - if (termios->c_cflag & PARODD) - par = MPSC_CHR_2_PAR_ODD; -#ifdef CMSPAR - if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) - par = MPSC_CHR_2_PAR_MARK; - else - par = MPSC_CHR_2_PAR_SPACE; - } -#endif - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); - - spin_lock_irqsave(&pi->port.lock, flags); - - uart_update_timeout(port, termios->c_cflag, baud); - - mpsc_set_char_length(pi, chr_bits); - mpsc_set_stop_bit_length(pi, stop_bits); - mpsc_set_parity(pi, par); - mpsc_set_baudrate(pi, baud); - - /* Characters/events to read */ - pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR; - - if (termios->c_iflag & INPCK) - pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE - | SDMA_DESC_CMDSTAT_FR; - - if (termios->c_iflag & (BRKINT | PARMRK)) - pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR; - - /* Characters/events to ignore */ - pi->port.ignore_status_mask = 0; - - if (termios->c_iflag & IGNPAR) - pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE - | SDMA_DESC_CMDSTAT_FR; - - if (termios->c_iflag & IGNBRK) { - pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR; - - if (termios->c_iflag & IGNPAR) - pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR; - } - - if ((termios->c_cflag & CREAD)) { - if (!pi->rcv_data) { - pi->rcv_data = 1; - mpsc_start_rx(pi); - } - } else if (pi->rcv_data) { - mpsc_stop_rx(port); - pi->rcv_data = 0; - } - - spin_unlock_irqrestore(&pi->port.lock, flags); -} - -static const char *mpsc_type(struct uart_port *port) -{ - pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME); - return MPSC_DRIVER_NAME; -} - -static int mpsc_request_port(struct uart_port *port) -{ - /* Should make chip/platform specific call */ - return 0; -} - -static void mpsc_release_port(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - - if (pi->ready) { - mpsc_uninit_rings(pi); - mpsc_free_ring_mem(pi); - pi->ready = 0; - } -} - -static void mpsc_config_port(struct uart_port *port, int flags) -{ -} - -static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - int rc = 0; - - pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line); - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC) - rc = -EINVAL; - else if (pi->port.irq != ser->irq) - rc = -EINVAL; - else if (ser->io_type != SERIAL_IO_MEM) - rc = -EINVAL; - else if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */ - rc = -EINVAL; - else if ((void *)pi->port.mapbase != ser->iomem_base) - rc = -EINVAL; - else if (pi->port.iobase != ser->port) - rc = -EINVAL; - else if (ser->hub6 != 0) - rc = -EINVAL; - - return rc; -} -#ifdef CONFIG_CONSOLE_POLL -/* Serial polling routines for writing and reading from the uart while - * in an interrupt or debug context. - */ - -static char poll_buf[2048]; -static int poll_ptr; -static int poll_cnt; -static void mpsc_put_poll_char(struct uart_port *port, - unsigned char c); - -static int mpsc_get_poll_char(struct uart_port *port) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - struct mpsc_rx_desc *rxre; - u32 cmdstat, bytes_in, i; - u8 *bp; - - if (!serial_polled) - serial_polled = 1; - - pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); - - if (poll_cnt) { - poll_cnt--; - return poll_buf[poll_ptr++]; - } - poll_ptr = 0; - poll_cnt = 0; - - while (poll_cnt == 0) { - rxre = (struct mpsc_rx_desc *)(pi->rxr + - (pi->rxr_posn*MPSC_RXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)rxre, - MPSC_RXRE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - /* - * Loop through Rx descriptors handling ones that have - * been completed. - */ - while (poll_cnt == 0 && - !((cmdstat = be32_to_cpu(rxre->cmdstat)) & - SDMA_DESC_CMDSTAT_O)){ - bytes_in = be16_to_cpu(rxre->bytecnt); - bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); - dma_cache_sync(pi->port.dev, (void *) bp, - MPSC_RXBE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)bp, - (ulong)bp + MPSC_RXBE_SIZE); -#endif - if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR | - SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) && - !(cmdstat & pi->port.ignore_status_mask)) { - poll_buf[poll_cnt] = *bp; - poll_cnt++; - } else { - for (i = 0; i < bytes_in; i++) { - poll_buf[poll_cnt] = *bp++; - poll_cnt++; - } - pi->port.icount.rx += bytes_in; - } - rxre->bytecnt = cpu_to_be16(0); - wmb(); - rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | - SDMA_DESC_CMDSTAT_EI | - SDMA_DESC_CMDSTAT_F | - SDMA_DESC_CMDSTAT_L); - wmb(); - dma_cache_sync(pi->port.dev, (void *)rxre, - MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - - /* Advance to next descriptor */ - pi->rxr_posn = (pi->rxr_posn + 1) & - (MPSC_RXR_ENTRIES - 1); - rxre = (struct mpsc_rx_desc *)(pi->rxr + - (pi->rxr_posn * MPSC_RXRE_SIZE)); - dma_cache_sync(pi->port.dev, (void *)rxre, - MPSC_RXRE_SIZE, DMA_FROM_DEVICE); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - invalidate_dcache_range((ulong)rxre, - (ulong)rxre + MPSC_RXRE_SIZE); -#endif - } - - /* Restart rx engine, if its stopped */ - if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) - mpsc_start_rx(pi); - } - if (poll_cnt) { - poll_cnt--; - return poll_buf[poll_ptr++]; - } - - return 0; -} - - -static void mpsc_put_poll_char(struct uart_port *port, - unsigned char c) -{ - struct mpsc_port_info *pi = (struct mpsc_port_info *)port; - u32 data; - - data = readl(pi->mpsc_base + MPSC_MPCR); - writeb(c, pi->mpsc_base + MPSC_CHR_1); - mb(); - data = readl(pi->mpsc_base + MPSC_CHR_2); - data |= MPSC_CHR_2_TTCS; - writel(data, pi->mpsc_base + MPSC_CHR_2); - mb(); - - while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS); -} -#endif - -static struct uart_ops mpsc_pops = { - .tx_empty = mpsc_tx_empty, - .set_mctrl = mpsc_set_mctrl, - .get_mctrl = mpsc_get_mctrl, - .stop_tx = mpsc_stop_tx, - .start_tx = mpsc_start_tx, - .stop_rx = mpsc_stop_rx, - .enable_ms = mpsc_enable_ms, - .break_ctl = mpsc_break_ctl, - .startup = mpsc_startup, - .shutdown = mpsc_shutdown, - .set_termios = mpsc_set_termios, - .type = mpsc_type, - .release_port = mpsc_release_port, - .request_port = mpsc_request_port, - .config_port = mpsc_config_port, - .verify_port = mpsc_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = mpsc_get_poll_char, - .poll_put_char = mpsc_put_poll_char, -#endif -}; - -/* - ****************************************************************************** - * - * Console Interface Routines - * - ****************************************************************************** - */ - -#ifdef CONFIG_SERIAL_MPSC_CONSOLE -static void mpsc_console_write(struct console *co, const char *s, uint count) -{ - struct mpsc_port_info *pi = &mpsc_ports[co->index]; - u8 *bp, *dp, add_cr = 0; - int i; - unsigned long iflags; - - spin_lock_irqsave(&pi->tx_lock, iflags); - - while (pi->txr_head != pi->txr_tail) { - while (mpsc_sdma_tx_active(pi)) - udelay(100); - mpsc_sdma_intr_ack(pi); - mpsc_tx_intr(pi); - } - - while (mpsc_sdma_tx_active(pi)) - udelay(100); - - while (count > 0) { - bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); - - for (i = 0; i < MPSC_TXBE_SIZE; i++) { - if (count == 0) - break; - - if (add_cr) { - *(dp++) = '\r'; - add_cr = 0; - } else { - *(dp++) = *s; - - if (*(s++) == '\n') { /* add '\r' after '\n' */ - add_cr = 1; - count++; - } - } - - count--; - } - - dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, - DMA_BIDIRECTIONAL); -#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) - if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ - flush_dcache_range((ulong)bp, - (ulong)bp + MPSC_TXBE_SIZE); -#endif - mpsc_setup_tx_desc(pi, i, 0); - pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); - mpsc_sdma_start_tx(pi); - - while (mpsc_sdma_tx_active(pi)) - udelay(100); - - pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1); - } - - spin_unlock_irqrestore(&pi->tx_lock, iflags); -} - -static int __init mpsc_console_setup(struct console *co, char *options) -{ - struct mpsc_port_info *pi; - int baud, bits, parity, flow; - - pr_debug("mpsc_console_setup[%d]: options: %s\n", co->index, options); - - if (co->index >= MPSC_NUM_CTLRS) - co->index = 0; - - pi = &mpsc_ports[co->index]; - - baud = pi->default_baud; - bits = pi->default_bits; - parity = pi->default_parity; - flow = pi->default_flow; - - if (!pi->port.ops) - return -ENODEV; - - spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */ - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&pi->port, co, baud, parity, bits, flow); -} - -static struct console mpsc_console = { - .name = MPSC_DEV_NAME, - .write = mpsc_console_write, - .device = uart_console_device, - .setup = mpsc_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &mpsc_reg, -}; - -static int __init mpsc_late_console_init(void) -{ - pr_debug("mpsc_late_console_init: Enter\n"); - - if (!(mpsc_console.flags & CON_ENABLED)) - register_console(&mpsc_console); - return 0; -} - -late_initcall(mpsc_late_console_init); - -#define MPSC_CONSOLE &mpsc_console -#else -#define MPSC_CONSOLE NULL -#endif -/* - ****************************************************************************** - * - * Dummy Platform Driver to extract & map shared register regions - * - ****************************************************************************** - */ -static void mpsc_resource_err(char *s) -{ - printk(KERN_WARNING "MPSC: Platform device resource error in %s\n", s); -} - -static int mpsc_shared_map_regs(struct platform_device *pd) -{ - struct resource *r; - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, - MPSC_ROUTING_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_ROUTING_REG_BLOCK_SIZE, - "mpsc_routing_regs")) { - mpsc_shared_regs.mpsc_routing_base = ioremap(r->start, - MPSC_ROUTING_REG_BLOCK_SIZE); - mpsc_shared_regs.mpsc_routing_base_p = r->start; - } else { - mpsc_resource_err("MPSC routing base"); - return -ENOMEM; - } - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, - MPSC_SDMA_INTR_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_SDMA_INTR_REG_BLOCK_SIZE, - "sdma_intr_regs")) { - mpsc_shared_regs.sdma_intr_base = ioremap(r->start, - MPSC_SDMA_INTR_REG_BLOCK_SIZE); - mpsc_shared_regs.sdma_intr_base_p = r->start; - } else { - iounmap(mpsc_shared_regs.mpsc_routing_base); - release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, - MPSC_ROUTING_REG_BLOCK_SIZE); - mpsc_resource_err("SDMA intr base"); - return -ENOMEM; - } - - return 0; -} - -static void mpsc_shared_unmap_regs(void) -{ - if (!mpsc_shared_regs.mpsc_routing_base) { - iounmap(mpsc_shared_regs.mpsc_routing_base); - release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, - MPSC_ROUTING_REG_BLOCK_SIZE); - } - if (!mpsc_shared_regs.sdma_intr_base) { - iounmap(mpsc_shared_regs.sdma_intr_base); - release_mem_region(mpsc_shared_regs.sdma_intr_base_p, - MPSC_SDMA_INTR_REG_BLOCK_SIZE); - } - - mpsc_shared_regs.mpsc_routing_base = NULL; - mpsc_shared_regs.sdma_intr_base = NULL; - - mpsc_shared_regs.mpsc_routing_base_p = 0; - mpsc_shared_regs.sdma_intr_base_p = 0; -} - -static int mpsc_shared_drv_probe(struct platform_device *dev) -{ - struct mpsc_shared_pdata *pdata; - int rc = -ENODEV; - - if (dev->id == 0) { - if (!(rc = mpsc_shared_map_regs(dev))) { - pdata = (struct mpsc_shared_pdata *) - dev->dev.platform_data; - - mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; - mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; - mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val; - mpsc_shared_regs.SDMA_INTR_CAUSE_m = - pdata->intr_cause_val; - mpsc_shared_regs.SDMA_INTR_MASK_m = - pdata->intr_mask_val; - - rc = 0; - } - } - - return rc; -} - -static int mpsc_shared_drv_remove(struct platform_device *dev) -{ - int rc = -ENODEV; - - if (dev->id == 0) { - mpsc_shared_unmap_regs(); - mpsc_shared_regs.MPSC_MRR_m = 0; - mpsc_shared_regs.MPSC_RCRR_m = 0; - mpsc_shared_regs.MPSC_TCRR_m = 0; - mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0; - mpsc_shared_regs.SDMA_INTR_MASK_m = 0; - rc = 0; - } - - return rc; -} - -static struct platform_driver mpsc_shared_driver = { - .probe = mpsc_shared_drv_probe, - .remove = mpsc_shared_drv_remove, - .driver = { - .name = MPSC_SHARED_NAME, - }, -}; - -/* - ****************************************************************************** - * - * Driver Interface Routines - * - ****************************************************************************** - */ -static struct uart_driver mpsc_reg = { - .owner = THIS_MODULE, - .driver_name = MPSC_DRIVER_NAME, - .dev_name = MPSC_DEV_NAME, - .major = MPSC_MAJOR, - .minor = MPSC_MINOR_START, - .nr = MPSC_NUM_CTLRS, - .cons = MPSC_CONSOLE, -}; - -static int mpsc_drv_map_regs(struct mpsc_port_info *pi, - struct platform_device *pd) -{ - struct resource *r; - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, MPSC_BASE_ORDER)) - && request_mem_region(r->start, MPSC_REG_BLOCK_SIZE, - "mpsc_regs")) { - pi->mpsc_base = ioremap(r->start, MPSC_REG_BLOCK_SIZE); - pi->mpsc_base_p = r->start; - } else { - mpsc_resource_err("MPSC base"); - goto err; - } - - if ((r = platform_get_resource(pd, IORESOURCE_MEM, - MPSC_SDMA_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_SDMA_REG_BLOCK_SIZE, "sdma_regs")) { - pi->sdma_base = ioremap(r->start,MPSC_SDMA_REG_BLOCK_SIZE); - pi->sdma_base_p = r->start; - } else { - mpsc_resource_err("SDMA base"); - if (pi->mpsc_base) { - iounmap(pi->mpsc_base); - pi->mpsc_base = NULL; - } - goto err; - } - - if ((r = platform_get_resource(pd,IORESOURCE_MEM,MPSC_BRG_BASE_ORDER)) - && request_mem_region(r->start, - MPSC_BRG_REG_BLOCK_SIZE, "brg_regs")) { - pi->brg_base = ioremap(r->start, MPSC_BRG_REG_BLOCK_SIZE); - pi->brg_base_p = r->start; - } else { - mpsc_resource_err("BRG base"); - if (pi->mpsc_base) { - iounmap(pi->mpsc_base); - pi->mpsc_base = NULL; - } - if (pi->sdma_base) { - iounmap(pi->sdma_base); - pi->sdma_base = NULL; - } - goto err; - } - return 0; - -err: - return -ENOMEM; -} - -static void mpsc_drv_unmap_regs(struct mpsc_port_info *pi) -{ - if (!pi->mpsc_base) { - iounmap(pi->mpsc_base); - release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE); - } - if (!pi->sdma_base) { - iounmap(pi->sdma_base); - release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE); - } - if (!pi->brg_base) { - iounmap(pi->brg_base); - release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE); - } - - pi->mpsc_base = NULL; - pi->sdma_base = NULL; - pi->brg_base = NULL; - - pi->mpsc_base_p = 0; - pi->sdma_base_p = 0; - pi->brg_base_p = 0; -} - -static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi, - struct platform_device *pd, int num) -{ - struct mpsc_pdata *pdata; - - pdata = (struct mpsc_pdata *)pd->dev.platform_data; - - pi->port.uartclk = pdata->brg_clk_freq; - pi->port.iotype = UPIO_MEM; - pi->port.line = num; - pi->port.type = PORT_MPSC; - pi->port.fifosize = MPSC_TXBE_SIZE; - pi->port.membase = pi->mpsc_base; - pi->port.mapbase = (ulong)pi->mpsc_base; - pi->port.ops = &mpsc_pops; - - pi->mirror_regs = pdata->mirror_regs; - pi->cache_mgmt = pdata->cache_mgmt; - pi->brg_can_tune = pdata->brg_can_tune; - pi->brg_clk_src = pdata->brg_clk_src; - pi->mpsc_max_idle = pdata->max_idle; - pi->default_baud = pdata->default_baud; - pi->default_bits = pdata->default_bits; - pi->default_parity = pdata->default_parity; - pi->default_flow = pdata->default_flow; - - /* Initial values of mirrored regs */ - pi->MPSC_CHR_1_m = pdata->chr_1_val; - pi->MPSC_CHR_2_m = pdata->chr_2_val; - pi->MPSC_CHR_10_m = pdata->chr_10_val; - pi->MPSC_MPCR_m = pdata->mpcr_val; - pi->BRG_BCR_m = pdata->bcr_val; - - pi->shared_regs = &mpsc_shared_regs; - - pi->port.irq = platform_get_irq(pd, 0); -} - -static int mpsc_drv_probe(struct platform_device *dev) -{ - struct mpsc_port_info *pi; - int rc = -ENODEV; - - pr_debug("mpsc_drv_probe: Adding MPSC %d\n", dev->id); - - if (dev->id < MPSC_NUM_CTLRS) { - pi = &mpsc_ports[dev->id]; - - if (!(rc = mpsc_drv_map_regs(pi, dev))) { - mpsc_drv_get_platform_data(pi, dev, dev->id); - pi->port.dev = &dev->dev; - - if (!(rc = mpsc_make_ready(pi))) { - spin_lock_init(&pi->tx_lock); - if (!(rc = uart_add_one_port(&mpsc_reg, - &pi->port))) { - rc = 0; - } else { - mpsc_release_port((struct uart_port *) - pi); - mpsc_drv_unmap_regs(pi); - } - } else { - mpsc_drv_unmap_regs(pi); - } - } - } - - return rc; -} - -static int mpsc_drv_remove(struct platform_device *dev) -{ - pr_debug("mpsc_drv_exit: Removing MPSC %d\n", dev->id); - - if (dev->id < MPSC_NUM_CTLRS) { - uart_remove_one_port(&mpsc_reg, &mpsc_ports[dev->id].port); - mpsc_release_port((struct uart_port *) - &mpsc_ports[dev->id].port); - mpsc_drv_unmap_regs(&mpsc_ports[dev->id]); - return 0; - } else { - return -ENODEV; - } -} - -static struct platform_driver mpsc_driver = { - .probe = mpsc_drv_probe, - .remove = mpsc_drv_remove, - .driver = { - .name = MPSC_CTLR_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init mpsc_drv_init(void) -{ - int rc; - - printk(KERN_INFO "Serial: MPSC driver\n"); - - memset(mpsc_ports, 0, sizeof(mpsc_ports)); - memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); - - if (!(rc = uart_register_driver(&mpsc_reg))) { - if (!(rc = platform_driver_register(&mpsc_shared_driver))) { - if ((rc = platform_driver_register(&mpsc_driver))) { - platform_driver_unregister(&mpsc_shared_driver); - uart_unregister_driver(&mpsc_reg); - } - } else { - uart_unregister_driver(&mpsc_reg); - } - } - - return rc; -} - -static void __exit mpsc_drv_exit(void) -{ - platform_driver_unregister(&mpsc_driver); - platform_driver_unregister(&mpsc_shared_driver); - uart_unregister_driver(&mpsc_reg); - memset(mpsc_ports, 0, sizeof(mpsc_ports)); - memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); -} - -module_init(mpsc_drv_init); -module_exit(mpsc_drv_exit); - -MODULE_AUTHOR("Mark A. Greer "); -MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver"); -MODULE_VERSION(MPSC_VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR); -MODULE_ALIAS("platform:" MPSC_CTLR_NAME); diff --git a/drivers/serial/mrst_max3110.c b/drivers/serial/mrst_max3110.c deleted file mode 100644 index b62857bf2fdb..000000000000 --- a/drivers/serial/mrst_max3110.c +++ /dev/null @@ -1,919 +0,0 @@ -/* - * mrst_max3110.c - spi uart protocol driver for Maxim 3110 - * - * Copyright (c) 2008-2010, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope 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., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * Note: - * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has - * 1 word. If SPI master controller doesn't support sclk frequency change, - * then the char need be sent out one by one with some delay - * - * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE - * interrupt for a low speed UART device - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "mrst_max3110.h" - -#define PR_FMT "mrst_max3110: " - -#define UART_TX_NEEDED 1 -#define CON_TX_NEEDED 2 -#define BIT_IRQ_PENDING 3 - -struct uart_max3110 { - struct uart_port port; - struct spi_device *spi; - char name[24]; - - wait_queue_head_t wq; - struct task_struct *main_thread; - struct task_struct *read_thread; - struct mutex thread_mutex;; - - u32 baud; - u16 cur_conf; - u8 clock; - u8 parity, word_7bits; - u16 irq; - - unsigned long uart_flags; - - /* console related */ - struct circ_buf con_xmit; -}; - -/* global data structure, may need be removed */ -static struct uart_max3110 *pmax; - -static void receive_chars(struct uart_max3110 *max, - unsigned char *str, int len); -static int max3110_read_multi(struct uart_max3110 *max, u8 *buf); -static void max3110_con_receive(struct uart_max3110 *max); - -static int max3110_write_then_read(struct uart_max3110 *max, - const void *txbuf, void *rxbuf, unsigned len, int always_fast) -{ - struct spi_device *spi = max->spi; - struct spi_message message; - struct spi_transfer x; - int ret; - - spi_message_init(&message); - memset(&x, 0, sizeof x); - x.len = len; - x.tx_buf = txbuf; - x.rx_buf = rxbuf; - spi_message_add_tail(&x, &message); - - if (always_fast) - x.speed_hz = spi->max_speed_hz; - else if (max->baud) - x.speed_hz = max->baud; - - /* Do the i/o */ - ret = spi_sync(spi, &message); - return ret; -} - -/* Write a 16b word to the device */ -static int max3110_out(struct uart_max3110 *max, const u16 out) -{ - void *buf; - u16 *obuf, *ibuf; - u8 ch; - int ret; - - buf = kzalloc(8, GFP_KERNEL | GFP_DMA); - if (!buf) - return -ENOMEM; - - obuf = buf; - ibuf = buf + 4; - *obuf = out; - ret = max3110_write_then_read(max, obuf, ibuf, 2, 1); - if (ret) { - pr_warning(PR_FMT "%s(): get err msg %d when sending 0x%x\n", - __func__, ret, out); - goto exit; - } - - /* If some valid data is read back */ - if (*ibuf & MAX3110_READ_DATA_AVAILABLE) { - ch = *ibuf & 0xff; - receive_chars(max, &ch, 1); - } - -exit: - kfree(buf); - return ret; -} - -/* - * This is usually used to read data from SPIC RX FIFO, which doesn't - * need any delay like flushing character out. - * - * Return how many valide bytes are read back - */ -static int max3110_read_multi(struct uart_max3110 *max, u8 *rxbuf) -{ - void *buf; - u16 *obuf, *ibuf; - u8 *pbuf, valid_str[M3110_RX_FIFO_DEPTH]; - int i, j, blen; - - blen = M3110_RX_FIFO_DEPTH * sizeof(u16); - buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); - if (!buf) { - pr_warning(PR_FMT "%s(): fail to alloc dma buffer\n", __func__); - return 0; - } - - /* tx/rx always have the same length */ - obuf = buf; - ibuf = buf + blen; - - if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) { - kfree(buf); - return 0; - } - - /* If caller doesn't provide a buffer, then handle received char */ - pbuf = rxbuf ? rxbuf : valid_str; - - for (i = 0, j = 0; i < M3110_RX_FIFO_DEPTH; i++) { - if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) - pbuf[j++] = ibuf[i] & 0xff; - } - - if (j && (pbuf == valid_str)) - receive_chars(max, valid_str, j); - - kfree(buf); - return j; -} - -static void serial_m3110_con_putchar(struct uart_port *port, int ch) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - struct circ_buf *xmit = &max->con_xmit; - - if (uart_circ_chars_free(xmit)) { - xmit->buf[xmit->head] = (char)ch; - xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); - } -} - -/* - * 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. - */ -static void serial_m3110_con_write(struct console *co, - const char *s, unsigned int count) -{ - if (!pmax) - return; - - uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); - - if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags)) - wake_up_process(pmax->main_thread); -} - -static int __init -serial_m3110_con_setup(struct console *co, char *options) -{ - struct uart_max3110 *max = pmax; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - pr_info(PR_FMT "setting up console\n"); - - if (co->index == -1) - co->index = 0; - - if (!max) { - pr_err(PR_FMT "pmax is NULL, return"); - return -ENODEV; - } - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&max->port, co, baud, parity, bits, flow); -} - -static struct tty_driver *serial_m3110_con_device(struct console *co, - int *index) -{ - struct uart_driver *p = co->data; - *index = co->index; - return p->tty_driver; -} - -static struct uart_driver serial_m3110_reg; -static struct console serial_m3110_console = { - .name = "ttyS", - .write = serial_m3110_con_write, - .device = serial_m3110_con_device, - .setup = serial_m3110_con_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial_m3110_reg, -}; - -static unsigned int serial_m3110_tx_empty(struct uart_port *port) -{ - return 1; -} - -static void serial_m3110_stop_tx(struct uart_port *port) -{ - return; -} - -/* stop_rx will be called in spin_lock env */ -static void serial_m3110_stop_rx(struct uart_port *port) -{ - return; -} - -#define WORDS_PER_XFER 128 -static void send_circ_buf(struct uart_max3110 *max, - struct circ_buf *xmit) -{ - void *buf; - u16 *obuf, *ibuf; - u8 valid_str[WORDS_PER_XFER]; - int i, j, len, blen, dma_size, left, ret = 0; - - - dma_size = WORDS_PER_XFER * sizeof(u16) * 2; - buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA); - if (!buf) - return; - obuf = buf; - ibuf = buf + dma_size/2; - - while (!uart_circ_empty(xmit)) { - left = uart_circ_chars_pending(xmit); - while (left) { - len = min(left, WORDS_PER_XFER); - blen = len * sizeof(u16); - memset(ibuf, 0, blen); - - for (i = 0; i < len; i++) { - obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); - } - - /* Fail to send msg to console is not very critical */ - ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); - if (ret) - pr_warning(PR_FMT "%s(): get err msg %d\n", - __func__, ret); - - for (i = 0, j = 0; i < len; i++) { - if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) - valid_str[j++] = ibuf[i] & 0xff; - } - - if (j) - receive_chars(max, valid_str, j); - - max->port.icount.tx += len; - left -= len; - } - } - - kfree(buf); -} - -static void transmit_char(struct uart_max3110 *max) -{ - struct uart_port *port = &max->port; - struct circ_buf *xmit = &port->state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - return; - - send_circ_buf(max, xmit); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - serial_m3110_stop_tx(port); -} - -/* - * This will be called by uart_write() and tty_write, can't - * go to sleep - */ -static void serial_m3110_start_tx(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - - if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags)) - wake_up_process(max->main_thread); -} - -static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) -{ - struct uart_port *port = &max->port; - struct tty_struct *tty; - int usable; - - /* If uart is not opened, just return */ - if (!port->state) - return; - - tty = port->state->port.tty; - if (!tty) - return; - - while (len) { - usable = tty_buffer_request_room(tty, len); - if (usable) { - tty_insert_flip_string(tty, str, usable); - str += usable; - port->icount.rx += usable; - } - len -= usable; - } - tty_flip_buffer_push(tty); -} - -/* - * This routine will be used in read_thread or RX IRQ handling, - * it will first do one round buffer read(8 words), if there is some - * valid RX data, will try to read 5 more rounds till all data - * is read out. - * - * Use stack space as data buffer to save some system load, and chose - * 504 Btyes as a threadhold to do a bulk push to upper tty layer when - * receiving bulk data, a much bigger buffer may cause stack overflow - */ -static void max3110_con_receive(struct uart_max3110 *max) -{ - int loop = 1, num, total = 0; - u8 recv_buf[512], *pbuf; - - pbuf = recv_buf; - do { - num = max3110_read_multi(max, pbuf); - - if (num) { - loop = 5; - pbuf += num; - total += num; - - if (total >= 504) { - receive_chars(max, recv_buf, total); - pbuf = recv_buf; - total = 0; - } - } - } while (--loop); - - if (total) - receive_chars(max, recv_buf, total); -} - -static int max3110_main_thread(void *_max) -{ - struct uart_max3110 *max = _max; - wait_queue_head_t *wq = &max->wq; - int ret = 0; - struct circ_buf *xmit = &max->con_xmit; - - init_waitqueue_head(wq); - pr_info(PR_FMT "start main thread\n"); - - do { - wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop()); - - mutex_lock(&max->thread_mutex); - - if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags)) - max3110_con_receive(max); - - /* first handle console output */ - if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags)) - send_circ_buf(max, xmit); - - /* handle uart output */ - if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags)) - transmit_char(max); - - mutex_unlock(&max->thread_mutex); - - } while (!kthread_should_stop()); - - return ret; -} - -static irqreturn_t serial_m3110_irq(int irq, void *dev_id) -{ - struct uart_max3110 *max = dev_id; - - /* max3110's irq is a falling edge, not level triggered, - * so no need to disable the irq */ - if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags)) - wake_up_process(max->main_thread); - - return IRQ_HANDLED; -} - -/* if don't use RX IRQ, then need a thread to polling read */ -static int max3110_read_thread(void *_max) -{ - struct uart_max3110 *max = _max; - - pr_info(PR_FMT "start read thread\n"); - do { - /* - * If can't acquire the mutex, it means the main thread - * is running which will also perform the rx job - */ - if (mutex_trylock(&max->thread_mutex)) { - max3110_con_receive(max); - mutex_unlock(&max->thread_mutex); - } - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 20); - } while (!kthread_should_stop()); - - return 0; -} - -static int serial_m3110_startup(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - u16 config = 0; - int ret = 0; - - if (port->line != 0) { - pr_err(PR_FMT "uart port startup failed\n"); - return -1; - } - - /* Disable all IRQ and config it to 115200, 8n1 */ - config = WC_TAG | WC_FIFO_ENABLE - | WC_1_STOPBITS - | WC_8BIT_WORD - | WC_BAUD_DR2; - - /* as we use thread to handle tx/rx, need set low latency */ - port->state->port.tty->low_latency = 1; - - if (max->irq) { - max->read_thread = NULL; - ret = request_irq(max->irq, serial_m3110_irq, - IRQ_TYPE_EDGE_FALLING, "max3110", max); - if (ret) { - max->irq = 0; - pr_err(PR_FMT "unable to allocate IRQ, polling\n"); - } else { - /* Enable RX IRQ only */ - config |= WC_RXA_IRQ_ENABLE; - } - } - - if (max->irq == 0) { - /* If IRQ is disabled, start a read thread for input data */ - max->read_thread = - kthread_run(max3110_read_thread, max, "max3110_read"); - if (IS_ERR(max->read_thread)) { - ret = PTR_ERR(max->read_thread); - max->read_thread = NULL; - pr_err(PR_FMT "Can't create read thread!\n"); - return ret; - } - } - - ret = max3110_out(max, config); - if (ret) { - if (max->irq) - free_irq(max->irq, max); - if (max->read_thread) - kthread_stop(max->read_thread); - max->read_thread = NULL; - return ret; - } - - max->cur_conf = config; - return 0; -} - -static void serial_m3110_shutdown(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - u16 config; - - if (max->read_thread) { - kthread_stop(max->read_thread); - max->read_thread = NULL; - } - - if (max->irq) - free_irq(max->irq, max); - - /* Disable interrupts from this port */ - config = WC_TAG | WC_SW_SHDI; - max3110_out(max, config); -} - -static void serial_m3110_release_port(struct uart_port *port) -{ -} - -static int serial_m3110_request_port(struct uart_port *port) -{ - return 0; -} - -static void serial_m3110_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_MAX3100; -} - -static int -serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* we don't want the core code to modify any port params */ - return -EINVAL; -} - - -static const char *serial_m3110_type(struct uart_port *port) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - return max->name; -} - -static void -serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_max3110 *max = - container_of(port, struct uart_max3110, port); - unsigned char cval; - unsigned int baud, parity = 0; - int clk_div = -1; - u16 new_conf = max->cur_conf; - - switch (termios->c_cflag & CSIZE) { - case CS7: - cval = UART_LCR_WLEN7; - new_conf |= WC_7BIT_WORD; - break; - default: - /* We only support CS7 & CS8 */ - termios->c_cflag &= ~CSIZE; - termios->c_cflag |= CS8; - case CS8: - cval = UART_LCR_WLEN8; - new_conf |= WC_8BIT_WORD; - break; - } - - baud = uart_get_baud_rate(port, termios, old, 0, 230400); - - /* First calc the div for 1.8MHZ clock case */ - switch (baud) { - case 300: - clk_div = WC_BAUD_DR384; - break; - case 600: - clk_div = WC_BAUD_DR192; - break; - case 1200: - clk_div = WC_BAUD_DR96; - break; - case 2400: - clk_div = WC_BAUD_DR48; - break; - case 4800: - clk_div = WC_BAUD_DR24; - break; - case 9600: - clk_div = WC_BAUD_DR12; - break; - case 19200: - clk_div = WC_BAUD_DR6; - break; - case 38400: - clk_div = WC_BAUD_DR3; - break; - case 57600: - clk_div = WC_BAUD_DR2; - break; - case 115200: - clk_div = WC_BAUD_DR1; - break; - case 230400: - if (max->clock & MAX3110_HIGH_CLK) - break; - default: - /* Pick the previous baud rate */ - baud = max->baud; - clk_div = max->cur_conf & WC_BAUD_DIV_MASK; - tty_termios_encode_baud_rate(termios, baud, baud); - } - - if (max->clock & MAX3110_HIGH_CLK) { - clk_div += 1; - /* High clk version max3110 doesn't support B300 */ - if (baud == 300) { - baud = 600; - clk_div = WC_BAUD_DR384; - } - if (baud == 230400) - clk_div = WC_BAUD_DR1; - tty_termios_encode_baud_rate(termios, baud, baud); - } - - new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; - - if (unlikely(termios->c_cflag & CMSPAR)) - termios->c_cflag &= ~CMSPAR; - - if (termios->c_cflag & CSTOPB) - new_conf |= WC_2_STOPBITS; - else - new_conf &= ~WC_2_STOPBITS; - - if (termios->c_cflag & PARENB) { - new_conf |= WC_PARITY_ENABLE; - parity |= UART_LCR_PARITY; - } else - new_conf &= ~WC_PARITY_ENABLE; - - if (!(termios->c_cflag & PARODD)) - parity |= UART_LCR_EPAR; - max->parity = parity; - - uart_update_timeout(port, termios->c_cflag, baud); - - new_conf |= WC_TAG; - if (new_conf != max->cur_conf) { - if (!max3110_out(max, new_conf)) { - max->cur_conf = new_conf; - max->baud = baud; - } - } -} - -/* Don't handle hw handshaking */ -static unsigned int serial_m3110_get_mctrl(struct uart_port *port) -{ - return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; -} - -static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -static void serial_m3110_break_ctl(struct uart_port *port, int break_state) -{ -} - -static void serial_m3110_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ -} - -static void serial_m3110_enable_ms(struct uart_port *port) -{ -} - -struct uart_ops serial_m3110_ops = { - .tx_empty = serial_m3110_tx_empty, - .set_mctrl = serial_m3110_set_mctrl, - .get_mctrl = serial_m3110_get_mctrl, - .stop_tx = serial_m3110_stop_tx, - .start_tx = serial_m3110_start_tx, - .stop_rx = serial_m3110_stop_rx, - .enable_ms = serial_m3110_enable_ms, - .break_ctl = serial_m3110_break_ctl, - .startup = serial_m3110_startup, - .shutdown = serial_m3110_shutdown, - .set_termios = serial_m3110_set_termios, - .pm = serial_m3110_pm, - .type = serial_m3110_type, - .release_port = serial_m3110_release_port, - .request_port = serial_m3110_request_port, - .config_port = serial_m3110_config_port, - .verify_port = serial_m3110_verify_port, -}; - -static struct uart_driver serial_m3110_reg = { - .owner = THIS_MODULE, - .driver_name = "MRST serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = 1, - .cons = &serial_m3110_console, -}; - -#ifdef CONFIG_PM -static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) -{ - struct uart_max3110 *max = spi_get_drvdata(spi); - - disable_irq(max->irq); - uart_suspend_port(&serial_m3110_reg, &max->port); - max3110_out(max, max->cur_conf | WC_SW_SHDI); - return 0; -} - -static int serial_m3110_resume(struct spi_device *spi) -{ - struct uart_max3110 *max = spi_get_drvdata(spi); - - max3110_out(max, max->cur_conf); - uart_resume_port(&serial_m3110_reg, &max->port); - enable_irq(max->irq); - return 0; -} -#else -#define serial_m3110_suspend NULL -#define serial_m3110_resume NULL -#endif - -static int __devinit serial_m3110_probe(struct spi_device *spi) -{ - struct uart_max3110 *max; - void *buffer; - u16 res; - int ret = 0; - - max = kzalloc(sizeof(*max), GFP_KERNEL); - if (!max) - return -ENOMEM; - - /* Set spi info */ - spi->bits_per_word = 16; - max->clock = MAX3110_HIGH_CLK; - - spi_setup(spi); - - max->port.type = PORT_MAX3100; - max->port.fifosize = 2; /* Only have 16b buffer */ - max->port.ops = &serial_m3110_ops; - max->port.line = 0; - max->port.dev = &spi->dev; - max->port.uartclk = 115200; - - max->spi = spi; - strcpy(max->name, spi->modalias); - max->irq = (u16)spi->irq; - - mutex_init(&max->thread_mutex); - - max->word_7bits = 0; - max->parity = 0; - max->baud = 0; - - max->cur_conf = 0; - max->uart_flags = 0; - - /* Check if reading configuration register returns something sane */ - - res = RC_TAG; - ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); - if (ret < 0 || res == 0 || res == 0xffff) { - printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)", - res); - ret = -ENODEV; - goto err_get_page; - } - - buffer = (void *)__get_free_page(GFP_KERNEL); - if (!buffer) { - ret = -ENOMEM; - goto err_get_page; - } - max->con_xmit.buf = buffer; - max->con_xmit.head = 0; - max->con_xmit.tail = 0; - - max->main_thread = kthread_run(max3110_main_thread, - max, "max3110_main"); - if (IS_ERR(max->main_thread)) { - ret = PTR_ERR(max->main_thread); - goto err_kthread; - } - - spi_set_drvdata(spi, max); - pmax = max; - - /* Give membase a psudo value to pass serial_core's check */ - max->port.membase = (void *)0xff110000; - uart_add_one_port(&serial_m3110_reg, &max->port); - - return 0; - -err_kthread: - free_page((unsigned long)buffer); -err_get_page: - kfree(max); - return ret; -} - -static int __devexit serial_m3110_remove(struct spi_device *dev) -{ - struct uart_max3110 *max = spi_get_drvdata(dev); - - if (!max) - return 0; - - uart_remove_one_port(&serial_m3110_reg, &max->port); - - free_page((unsigned long)max->con_xmit.buf); - - if (max->main_thread) - kthread_stop(max->main_thread); - - kfree(max); - return 0; -} - -static struct spi_driver uart_max3110_driver = { - .driver = { - .name = "spi_max3111", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - .probe = serial_m3110_probe, - .remove = __devexit_p(serial_m3110_remove), - .suspend = serial_m3110_suspend, - .resume = serial_m3110_resume, -}; - -static int __init serial_m3110_init(void) -{ - int ret = 0; - - ret = uart_register_driver(&serial_m3110_reg); - if (ret) - return ret; - - ret = spi_register_driver(&uart_max3110_driver); - if (ret) - uart_unregister_driver(&serial_m3110_reg); - - return ret; -} - -static void __exit serial_m3110_exit(void) -{ - spi_unregister_driver(&uart_max3110_driver); - uart_unregister_driver(&serial_m3110_reg); -} - -module_init(serial_m3110_init); -module_exit(serial_m3110_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("max3110-uart"); diff --git a/drivers/serial/mrst_max3110.h b/drivers/serial/mrst_max3110.h deleted file mode 100644 index d1ef43af397c..000000000000 --- a/drivers/serial/mrst_max3110.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _MRST_MAX3110_H -#define _MRST_MAX3110_H - -#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */ -#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */ - -/* status bits for all 4 MAX3110 operate modes */ -#define MAX3110_READ_DATA_AVAILABLE (1 << 15) -#define MAX3110_WRITE_BUF_EMPTY (1 << 14) - -#define WC_TAG (3 << 14) -#define RC_TAG (1 << 14) -#define WD_TAG (2 << 14) -#define RD_TAG (0 << 14) - -/* bits def for write configuration */ -#define WC_FIFO_ENABLE_MASK (1 << 13) -#define WC_FIFO_ENABLE (0 << 13) - -#define WC_SW_SHDI (1 << 12) - -#define WC_IRQ_MASK (0xF << 8) -#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */ -#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */ -#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9) -#define WC_REC_ACT_IRQ_ENABLE (1 << 8) - -#define WC_IRDA_ENABLE (1 << 7) - -#define WC_STOPBITS_MASK (1 << 6) -#define WC_2_STOPBITS (1 << 6) -#define WC_1_STOPBITS (0 << 6) - -#define WC_PARITY_ENABLE_MASK (1 << 5) -#define WC_PARITY_ENABLE (1 << 5) - -#define WC_WORDLEN_MASK (1 << 4) -#define WC_7BIT_WORD (1 << 4) -#define WC_8BIT_WORD (0 << 4) - -#define WC_BAUD_DIV_MASK (0xF) -#define WC_BAUD_DR1 (0x0) -#define WC_BAUD_DR2 (0x1) -#define WC_BAUD_DR4 (0x2) -#define WC_BAUD_DR8 (0x3) -#define WC_BAUD_DR16 (0x4) -#define WC_BAUD_DR32 (0x5) -#define WC_BAUD_DR64 (0x6) -#define WC_BAUD_DR128 (0x7) -#define WC_BAUD_DR3 (0x8) -#define WC_BAUD_DR6 (0x9) -#define WC_BAUD_DR12 (0xA) -#define WC_BAUD_DR24 (0xB) -#define WC_BAUD_DR48 (0xC) -#define WC_BAUD_DR96 (0xD) -#define WC_BAUD_DR192 (0xE) -#define WC_BAUD_DR384 (0xF) - -#define M3110_RX_FIFO_DEPTH 8 -#endif diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c deleted file mode 100644 index 8e43a7b69e64..000000000000 --- a/drivers/serial/msm_serial.c +++ /dev/null @@ -1,758 +0,0 @@ -/* - * drivers/serial/msm_serial.c - driver for msm7k serial device and console - * - * Copyright (C) 2007 Google, Inc. - * Author: Robert Love - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#if defined(CONFIG_SERIAL_MSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -# define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "msm_serial.h" - -struct msm_port { - struct uart_port uart; - char name[16]; - struct clk *clk; - unsigned int imr; -}; - -static void msm_stop_tx(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - msm_port->imr &= ~UART_IMR_TXLEV; - msm_write(port, msm_port->imr, UART_IMR); -} - -static void msm_start_tx(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - msm_port->imr |= UART_IMR_TXLEV; - msm_write(port, msm_port->imr, UART_IMR); -} - -static void msm_stop_rx(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE); - msm_write(port, msm_port->imr, UART_IMR); -} - -static void msm_enable_ms(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - msm_port->imr |= UART_IMR_DELTA_CTS; - msm_write(port, msm_port->imr, UART_IMR); -} - -static void handle_rx(struct uart_port *port) -{ - struct tty_struct *tty = port->state->port.tty; - unsigned int sr; - - /* - * Handle overrun. My understanding of the hardware is that overrun - * is not tied to the RX buffer, so we handle the case out of band. - */ - if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) { - port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); - } - - /* and now the main RX loop */ - while ((sr = msm_read(port, UART_SR)) & UART_SR_RX_READY) { - unsigned int c; - char flag = TTY_NORMAL; - - c = msm_read(port, UART_RF); - - if (sr & UART_SR_RX_BREAK) { - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (sr & UART_SR_PAR_FRAME_ERR) { - port->icount.frame++; - } else { - port->icount.rx++; - } - - /* Mask conditions we're ignorning. */ - sr &= port->read_status_mask; - - if (sr & UART_SR_RX_BREAK) { - flag = TTY_BREAK; - } else if (sr & UART_SR_PAR_FRAME_ERR) { - flag = TTY_FRAME; - } - - if (!uart_handle_sysrq_char(port, c)) - tty_insert_flip_char(tty, c, flag); - } - - tty_flip_buffer_push(tty); -} - -static void handle_tx(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - struct msm_port *msm_port = UART_TO_MSM(port); - int sent_tx; - - if (port->x_char) { - msm_write(port, port->x_char, UART_TF); - port->icount.tx++; - port->x_char = 0; - } - - while (msm_read(port, UART_SR) & UART_SR_TX_READY) { - if (uart_circ_empty(xmit)) { - /* disable tx interrupts */ - msm_port->imr &= ~UART_IMR_TXLEV; - msm_write(port, msm_port->imr, UART_IMR); - break; - } - - msm_write(port, xmit->buf[xmit->tail], UART_TF); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - sent_tx = 1; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static void handle_delta_cts(struct uart_port *port) -{ - msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); - port->icount.cts++; - wake_up_interruptible(&port->state->port.delta_msr_wait); -} - -static irqreturn_t msm_irq(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct msm_port *msm_port = UART_TO_MSM(port); - unsigned int misr; - - spin_lock(&port->lock); - misr = msm_read(port, UART_MISR); - msm_write(port, 0, UART_IMR); /* disable interrupt */ - - if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) - handle_rx(port); - if (misr & UART_IMR_TXLEV) - handle_tx(port); - if (misr & UART_IMR_DELTA_CTS) - handle_delta_cts(port); - - msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ - spin_unlock(&port->lock); - - return IRQ_HANDLED; -} - -static unsigned int msm_tx_empty(struct uart_port *port) -{ - return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0; -} - -static unsigned int msm_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; -} - -static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - unsigned int mr; - - mr = msm_read(port, UART_MR1); - - if (!(mctrl & TIOCM_RTS)) { - mr &= ~UART_MR1_RX_RDY_CTL; - msm_write(port, mr, UART_MR1); - msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR); - } else { - mr |= UART_MR1_RX_RDY_CTL; - msm_write(port, mr, UART_MR1); - } -} - -static void msm_break_ctl(struct uart_port *port, int break_ctl) -{ - if (break_ctl) - msm_write(port, UART_CR_CMD_START_BREAK, UART_CR); - else - msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR); -} - -static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) -{ - unsigned int baud_code, rxstale, watermark; - - switch (baud) { - case 300: - baud_code = UART_CSR_300; - rxstale = 1; - break; - case 600: - baud_code = UART_CSR_600; - rxstale = 1; - break; - case 1200: - baud_code = UART_CSR_1200; - rxstale = 1; - break; - case 2400: - baud_code = UART_CSR_2400; - rxstale = 1; - break; - case 4800: - baud_code = UART_CSR_4800; - rxstale = 1; - break; - case 9600: - baud_code = UART_CSR_9600; - rxstale = 2; - break; - case 14400: - baud_code = UART_CSR_14400; - rxstale = 3; - break; - case 19200: - baud_code = UART_CSR_19200; - rxstale = 4; - break; - case 28800: - baud_code = UART_CSR_28800; - rxstale = 6; - break; - case 38400: - baud_code = UART_CSR_38400; - rxstale = 8; - break; - case 57600: - baud_code = UART_CSR_57600; - rxstale = 16; - break; - case 115200: - default: - baud_code = UART_CSR_115200; - baud = 115200; - rxstale = 31; - break; - } - - msm_write(port, baud_code, UART_CSR); - - /* RX stale watermark */ - watermark = UART_IPR_STALE_LSB & rxstale; - watermark |= UART_IPR_RXSTALE_LAST; - watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2); - msm_write(port, watermark, UART_IPR); - - /* set RX watermark */ - watermark = (port->fifosize * 3) / 4; - msm_write(port, watermark, UART_RFWR); - - /* set TX watermark */ - msm_write(port, 10, UART_TFWR); - - return baud; -} - -static void msm_reset(struct uart_port *port) -{ - /* reset everything */ - msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); - msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); - msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); - msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); - msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); - msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); -} - -static void msm_init_clock(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - clk_enable(msm_port->clk); - msm_serial_set_mnd_regs(port); -} - -static int msm_startup(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - unsigned int data, rfr_level; - int ret; - - snprintf(msm_port->name, sizeof(msm_port->name), - "msm_serial%d", port->line); - - ret = request_irq(port->irq, msm_irq, IRQF_TRIGGER_HIGH, - msm_port->name, port); - if (unlikely(ret)) - return ret; - - msm_init_clock(port); - - if (likely(port->fifosize > 12)) - rfr_level = port->fifosize - 12; - else - rfr_level = port->fifosize; - - /* set automatic RFR level */ - data = msm_read(port, UART_MR1); - data &= ~UART_MR1_AUTO_RFR_LEVEL1; - data &= ~UART_MR1_AUTO_RFR_LEVEL0; - data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2); - data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; - msm_write(port, data, UART_MR1); - - /* make sure that RXSTALE count is non-zero */ - data = msm_read(port, UART_IPR); - if (unlikely(!data)) { - data |= UART_IPR_RXSTALE_LAST; - data |= UART_IPR_STALE_LSB; - msm_write(port, data, UART_IPR); - } - - msm_reset(port); - - msm_write(port, 0x05, UART_CR); /* enable TX & RX */ - - /* turn on RX and CTS interrupts */ - msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE | - UART_IMR_CURRENT_CTS; - msm_write(port, msm_port->imr, UART_IMR); - - return 0; -} - -static void msm_shutdown(struct uart_port *port) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - msm_port->imr = 0; - msm_write(port, 0, UART_IMR); /* disable interrupts */ - - clk_disable(msm_port->clk); - - free_irq(port->irq, port); -} - -static void msm_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - unsigned int baud, mr; - - spin_lock_irqsave(&port->lock, flags); - - /* calculate and set baud rate */ - baud = uart_get_baud_rate(port, termios, old, 300, 115200); - baud = msm_set_baud_rate(port, baud); - if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, baud, baud); - - /* calculate parity */ - mr = msm_read(port, UART_MR2); - mr &= ~UART_MR2_PARITY_MODE; - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & PARODD) - mr |= UART_MR2_PARITY_MODE_ODD; - else if (termios->c_cflag & CMSPAR) - mr |= UART_MR2_PARITY_MODE_SPACE; - else - mr |= UART_MR2_PARITY_MODE_EVEN; - } - - /* calculate bits per char */ - mr &= ~UART_MR2_BITS_PER_CHAR; - switch (termios->c_cflag & CSIZE) { - case CS5: - mr |= UART_MR2_BITS_PER_CHAR_5; - break; - case CS6: - mr |= UART_MR2_BITS_PER_CHAR_6; - break; - case CS7: - mr |= UART_MR2_BITS_PER_CHAR_7; - break; - case CS8: - default: - mr |= UART_MR2_BITS_PER_CHAR_8; - break; - } - - /* calculate stop bits */ - mr &= ~(UART_MR2_STOP_BIT_LEN_ONE | UART_MR2_STOP_BIT_LEN_TWO); - if (termios->c_cflag & CSTOPB) - mr |= UART_MR2_STOP_BIT_LEN_TWO; - else - mr |= UART_MR2_STOP_BIT_LEN_ONE; - - /* set parity, bits per char, and stop bit */ - msm_write(port, mr, UART_MR2); - - /* calculate and set hardware flow control */ - mr = msm_read(port, UART_MR1); - mr &= ~(UART_MR1_CTS_CTL | UART_MR1_RX_RDY_CTL); - if (termios->c_cflag & CRTSCTS) { - mr |= UART_MR1_CTS_CTL; - mr |= UART_MR1_RX_RDY_CTL; - } - msm_write(port, mr, UART_MR1); - - /* Configure status bits to ignore based on termio flags. */ - port->read_status_mask = 0; - if (termios->c_iflag & INPCK) - port->read_status_mask |= UART_SR_PAR_FRAME_ERR; - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= UART_SR_RX_BREAK; - - uart_update_timeout(port, termios->c_cflag, baud); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *msm_type(struct uart_port *port) -{ - return "MSM"; -} - -static void msm_release_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *resource; - resource_size_t size; - - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!resource)) - return; - size = resource->end - resource->start + 1; - - release_mem_region(port->mapbase, size); - iounmap(port->membase); - port->membase = NULL; -} - -static int msm_request_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - struct resource *resource; - resource_size_t size; - - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!resource)) - return -ENXIO; - size = resource->end - resource->start + 1; - - if (unlikely(!request_mem_region(port->mapbase, size, "msm_serial"))) - return -EBUSY; - - port->membase = ioremap(port->mapbase, size); - if (!port->membase) { - release_mem_region(port->mapbase, size); - return -EBUSY; - } - - return 0; -} - -static void msm_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_MSM; - msm_request_port(port); - } -} - -static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM)) - return -EINVAL; - if (unlikely(port->irq != ser->irq)) - return -EINVAL; - return 0; -} - -static void msm_power(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct msm_port *msm_port = UART_TO_MSM(port); - - switch (state) { - case 0: - clk_enable(msm_port->clk); - break; - case 3: - clk_disable(msm_port->clk); - break; - default: - printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); - } -} - -static struct uart_ops msm_uart_pops = { - .tx_empty = msm_tx_empty, - .set_mctrl = msm_set_mctrl, - .get_mctrl = msm_get_mctrl, - .stop_tx = msm_stop_tx, - .start_tx = msm_start_tx, - .stop_rx = msm_stop_rx, - .enable_ms = msm_enable_ms, - .break_ctl = msm_break_ctl, - .startup = msm_startup, - .shutdown = msm_shutdown, - .set_termios = msm_set_termios, - .type = msm_type, - .release_port = msm_release_port, - .request_port = msm_request_port, - .config_port = msm_config_port, - .verify_port = msm_verify_port, - .pm = msm_power, -}; - -static struct msm_port msm_uart_ports[] = { - { - .uart = { - .iotype = UPIO_MEM, - .ops = &msm_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .fifosize = 512, - .line = 0, - }, - }, - { - .uart = { - .iotype = UPIO_MEM, - .ops = &msm_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .fifosize = 512, - .line = 1, - }, - }, - { - .uart = { - .iotype = UPIO_MEM, - .ops = &msm_uart_pops, - .flags = UPF_BOOT_AUTOCONF, - .fifosize = 64, - .line = 2, - }, - }, -}; - -#define UART_NR ARRAY_SIZE(msm_uart_ports) - -static inline struct uart_port *get_port_from_line(unsigned int line) -{ - return &msm_uart_ports[line].uart; -} - -#ifdef CONFIG_SERIAL_MSM_CONSOLE - -static void msm_console_putchar(struct uart_port *port, int c) -{ - while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) - ; - msm_write(port, c, UART_TF); -} - -static void msm_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_port *port; - struct msm_port *msm_port; - - BUG_ON(co->index < 0 || co->index >= UART_NR); - - port = get_port_from_line(co->index); - msm_port = UART_TO_MSM(port); - - spin_lock(&port->lock); - uart_console_write(port, s, count, msm_console_putchar); - spin_unlock(&port->lock); -} - -static int __init msm_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud, flow, bits, parity; - - if (unlikely(co->index >= UART_NR || co->index < 0)) - return -ENXIO; - - port = get_port_from_line(co->index); - - if (unlikely(!port->membase)) - return -ENXIO; - - port->cons = co; - - msm_init_clock(port); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - bits = 8; - parity = 'n'; - flow = 'n'; - msm_write(port, UART_MR2_BITS_PER_CHAR_8 | UART_MR2_STOP_BIT_LEN_ONE, - UART_MR2); /* 8N1 */ - - if (baud < 300 || baud > 115200) - baud = 115200; - msm_set_baud_rate(port, baud); - - msm_reset(port); - - printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver msm_uart_driver; - -static struct console msm_console = { - .name = "ttyMSM", - .write = msm_console_write, - .device = uart_console_device, - .setup = msm_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &msm_uart_driver, -}; - -#define MSM_CONSOLE (&msm_console) - -#else -#define MSM_CONSOLE NULL -#endif - -static struct uart_driver msm_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "msm_serial", - .dev_name = "ttyMSM", - .nr = UART_NR, - .cons = MSM_CONSOLE, -}; - -static int __init msm_serial_probe(struct platform_device *pdev) -{ - struct msm_port *msm_port; - struct resource *resource; - struct uart_port *port; - int irq; - - if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) - return -ENXIO; - - printk(KERN_INFO "msm_serial: detected port #%d\n", pdev->id); - - port = get_port_from_line(pdev->id); - port->dev = &pdev->dev; - msm_port = UART_TO_MSM(port); - - msm_port->clk = clk_get(&pdev->dev, "uart_clk"); - if (IS_ERR(msm_port->clk)) - return PTR_ERR(msm_port->clk); - port->uartclk = clk_get_rate(msm_port->clk); - printk(KERN_INFO "uartclk = %d\n", port->uartclk); - - - resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (unlikely(!resource)) - return -ENXIO; - port->mapbase = resource->start; - - irq = platform_get_irq(pdev, 0); - if (unlikely(irq < 0)) - return -ENXIO; - port->irq = irq; - - platform_set_drvdata(pdev, port); - - return uart_add_one_port(&msm_uart_driver, port); -} - -static int __devexit msm_serial_remove(struct platform_device *pdev) -{ - struct msm_port *msm_port = platform_get_drvdata(pdev); - - clk_put(msm_port->clk); - - return 0; -} - -static struct platform_driver msm_platform_driver = { - .remove = msm_serial_remove, - .driver = { - .name = "msm_serial", - .owner = THIS_MODULE, - }, -}; - -static int __init msm_serial_init(void) -{ - int ret; - - ret = uart_register_driver(&msm_uart_driver); - if (unlikely(ret)) - return ret; - - ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); - if (unlikely(ret)) - uart_unregister_driver(&msm_uart_driver); - - printk(KERN_INFO "msm_serial: driver initialized\n"); - - return ret; -} - -static void __exit msm_serial_exit(void) -{ -#ifdef CONFIG_SERIAL_MSM_CONSOLE - unregister_console(&msm_console); -#endif - platform_driver_unregister(&msm_platform_driver); - uart_unregister_driver(&msm_uart_driver); -} - -module_init(msm_serial_init); -module_exit(msm_serial_exit); - -MODULE_AUTHOR("Robert Love "); -MODULE_DESCRIPTION("Driver for msm7x serial device"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/msm_serial.h b/drivers/serial/msm_serial.h deleted file mode 100644 index f6ca9ca79e98..000000000000 --- a/drivers/serial/msm_serial.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * drivers/serial/msm_serial.h - * - * Copyright (C) 2007 Google, Inc. - * Author: Robert Love - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#ifndef __DRIVERS_SERIAL_MSM_SERIAL_H -#define __DRIVERS_SERIAL_MSM_SERIAL_H - -#define UART_MR1 0x0000 - -#define UART_MR1_AUTO_RFR_LEVEL0 0x3F -#define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 -#define UART_MR1_RX_RDY_CTL (1 << 7) -#define UART_MR1_CTS_CTL (1 << 6) - -#define UART_MR2 0x0004 -#define UART_MR2_ERROR_MODE (1 << 6) -#define UART_MR2_BITS_PER_CHAR 0x30 -#define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) -#define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) -#define UART_MR2_BITS_PER_CHAR_7 (0x2 << 4) -#define UART_MR2_BITS_PER_CHAR_8 (0x3 << 4) -#define UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2) -#define UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2) -#define UART_MR2_PARITY_MODE_NONE 0x0 -#define UART_MR2_PARITY_MODE_ODD 0x1 -#define UART_MR2_PARITY_MODE_EVEN 0x2 -#define UART_MR2_PARITY_MODE_SPACE 0x3 -#define UART_MR2_PARITY_MODE 0x3 - -#define UART_CSR 0x0008 -#define UART_CSR_115200 0xFF -#define UART_CSR_57600 0xEE -#define UART_CSR_38400 0xDD -#define UART_CSR_28800 0xCC -#define UART_CSR_19200 0xBB -#define UART_CSR_14400 0xAA -#define UART_CSR_9600 0x99 -#define UART_CSR_4800 0x77 -#define UART_CSR_2400 0x55 -#define UART_CSR_1200 0x44 -#define UART_CSR_600 0x33 -#define UART_CSR_300 0x22 - -#define UART_TF 0x000C - -#define UART_CR 0x0010 -#define UART_CR_CMD_NULL (0 << 4) -#define UART_CR_CMD_RESET_RX (1 << 4) -#define UART_CR_CMD_RESET_TX (2 << 4) -#define UART_CR_CMD_RESET_ERR (3 << 4) -#define UART_CR_CMD_RESET_BREAK_INT (4 << 4) -#define UART_CR_CMD_START_BREAK (5 << 4) -#define UART_CR_CMD_STOP_BREAK (6 << 4) -#define UART_CR_CMD_RESET_CTS (7 << 4) -#define UART_CR_CMD_PACKET_MODE (9 << 4) -#define UART_CR_CMD_MODE_RESET (12 << 4) -#define UART_CR_CMD_SET_RFR (13 << 4) -#define UART_CR_CMD_RESET_RFR (14 << 4) -#define UART_CR_TX_DISABLE (1 << 3) -#define UART_CR_TX_ENABLE (1 << 3) -#define UART_CR_RX_DISABLE (1 << 3) -#define UART_CR_RX_ENABLE (1 << 3) - -#define UART_IMR 0x0014 -#define UART_IMR_TXLEV (1 << 0) -#define UART_IMR_RXSTALE (1 << 3) -#define UART_IMR_RXLEV (1 << 4) -#define UART_IMR_DELTA_CTS (1 << 5) -#define UART_IMR_CURRENT_CTS (1 << 6) - -#define UART_IPR_RXSTALE_LAST 0x20 -#define UART_IPR_STALE_LSB 0x1F -#define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 - -#define UART_IPR 0x0018 -#define UART_TFWR 0x001C -#define UART_RFWR 0x0020 -#define UART_HCR 0x0024 - -#define UART_MREG 0x0028 -#define UART_NREG 0x002C -#define UART_DREG 0x0030 -#define UART_MNDREG 0x0034 -#define UART_IRDA 0x0038 -#define UART_MISR_MODE 0x0040 -#define UART_MISR_RESET 0x0044 -#define UART_MISR_EXPORT 0x0048 -#define UART_MISR_VAL 0x004C -#define UART_TEST_CTRL 0x0050 - -#define UART_SR 0x0008 -#define UART_SR_HUNT_CHAR (1 << 7) -#define UART_SR_RX_BREAK (1 << 6) -#define UART_SR_PAR_FRAME_ERR (1 << 5) -#define UART_SR_OVERRUN (1 << 4) -#define UART_SR_TX_EMPTY (1 << 3) -#define UART_SR_TX_READY (1 << 2) -#define UART_SR_RX_FULL (1 << 1) -#define UART_SR_RX_READY (1 << 0) - -#define UART_RF 0x000C -#define UART_MISR 0x0010 -#define UART_ISR 0x0014 - -#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) - -static inline -void msm_write(struct uart_port *port, unsigned int val, unsigned int off) -{ - __raw_writel(val, port->membase + off); -} - -static inline -unsigned int msm_read(struct uart_port *port, unsigned int off) -{ - return __raw_readl(port->membase + off); -} - -/* - * Setup the MND registers to use the TCXO clock. - */ -static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) -{ - msm_write(port, 0x06, UART_MREG); - msm_write(port, 0xF1, UART_NREG); - msm_write(port, 0x0F, UART_DREG); - msm_write(port, 0x1A, UART_MNDREG); -} - -/* - * Setup the MND registers to use the TCXO clock divided by 4. - */ -static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) -{ - msm_write(port, 0x18, UART_MREG); - msm_write(port, 0xF6, UART_NREG); - msm_write(port, 0x0F, UART_DREG); - msm_write(port, 0x0A, UART_MNDREG); -} - -static inline -void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) -{ - if (port->uartclk == 19200000) - msm_serial_set_mnd_regs_tcxo(port); - else - msm_serial_set_mnd_regs_tcxoby4(port); -} - -/* - * TROUT has a specific defect that makes it report it's uartclk - * as 19.2Mhz (TCXO) when it's actually 4.8Mhz (TCXO/4). This special - * cases TROUT to use the right clock. - */ -#ifdef CONFIG_MACH_TROUT -#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_tcxoby4 -#else -#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk -#endif - -#endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */ diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c deleted file mode 100644 index 9711e06a8374..000000000000 --- a/drivers/serial/mux.c +++ /dev/null @@ -1,633 +0,0 @@ -/* -** mux.c: -** serial driver for the Mux console found in some PA-RISC servers. -** -** (c) Copyright 2002 Ryan Bradetich -** (c) Copyright 2002 Hewlett-Packard Company -** -** 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 Driver currently only supports the console (port 0) on the MUX. -** Additional work will be needed on this driver to enable the full -** functionality of the MUX. -** -*/ - -#include -#include -#include -#include -#include -#include -#include /* for udelay */ -#include -#include -#include -#include - -#ifdef CONFIG_MAGIC_SYSRQ -#include -#define SUPPORT_SYSRQ -#endif - -#include - -#define MUX_OFFSET 0x800 -#define MUX_LINE_OFFSET 0x80 - -#define MUX_FIFO_SIZE 255 -#define MUX_POLL_DELAY (30 * HZ / 1000) - -#define IO_DATA_REG_OFFSET 0x3c -#define IO_DCOUNT_REG_OFFSET 0x40 - -#define MUX_EOFIFO(status) ((status & 0xF000) == 0xF000) -#define MUX_STATUS(status) ((status & 0xF000) == 0x8000) -#define MUX_BREAK(status) ((status & 0xF000) == 0x2000) - -#define MUX_NR 256 -static unsigned int port_cnt __read_mostly; -struct mux_port { - struct uart_port port; - int enabled; -}; -static struct mux_port mux_ports[MUX_NR]; - -static struct uart_driver mux_driver = { - .owner = THIS_MODULE, - .driver_name = "ttyB", - .dev_name = "ttyB", - .major = MUX_MAJOR, - .minor = 0, - .nr = MUX_NR, -}; - -static struct timer_list mux_timer; - -#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + IO_DATA_REG_OFFSET) -#define UART_GET_FIFO_CNT(p) __raw_readl((p)->membase + IO_DCOUNT_REG_OFFSET) - -/** - * get_mux_port_count - Get the number of available ports on the Mux. - * @dev: The parisc device. - * - * This function is used to determine the number of ports the Mux - * supports. The IODC data reports the number of ports the Mux - * can support, but there are cases where not all the Mux ports - * are connected. This function can override the IODC and - * return the true port count. - */ -static int __init get_mux_port_count(struct parisc_device *dev) -{ - int status; - u8 iodc_data[32]; - unsigned long bytecnt; - - /* If this is the built-in Mux for the K-Class (Eole CAP/MUX), - * we only need to allocate resources for 1 port since the - * other 7 ports are not connected. - */ - if(dev->id.hversion == 0x15) - return 1; - - status = pdc_iodc_read(&bytecnt, dev->hpa.start, 0, iodc_data, 32); - BUG_ON(status != PDC_OK); - - /* Return the number of ports specified in the iodc data. */ - return ((((iodc_data)[4] & 0xf0) >> 4) * 8) + 8; -} - -/** - * mux_tx_empty - Check if the transmitter fifo is empty. - * @port: Ptr to the uart_port. - * - * This function test if the transmitter fifo for the port - * described by 'port' is empty. If it is empty, this function - * should return TIOCSER_TEMT, otherwise return 0. - */ -static unsigned int mux_tx_empty(struct uart_port *port) -{ - return UART_GET_FIFO_CNT(port) ? 0 : TIOCSER_TEMT; -} - -/** - * mux_set_mctrl - Set the current state of the modem control inputs. - * @ports: Ptr to the uart_port. - * @mctrl: Modem control bits. - * - * The Serial MUX does not support CTS, DCD or DSR so this function - * is ignored. - */ -static void mux_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -/** - * mux_get_mctrl - Returns the current state of modem control inputs. - * @port: Ptr to the uart_port. - * - * The Serial MUX does not support CTS, DCD or DSR so these lines are - * treated as permanently active. - */ -static unsigned int mux_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; -} - -/** - * mux_stop_tx - Stop transmitting characters. - * @port: Ptr to the uart_port. - * - * The Serial MUX does not support this function. - */ -static void mux_stop_tx(struct uart_port *port) -{ -} - -/** - * mux_start_tx - Start transmitting characters. - * @port: Ptr to the uart_port. - * - * The Serial Mux does not support this function. - */ -static void mux_start_tx(struct uart_port *port) -{ -} - -/** - * mux_stop_rx - Stop receiving characters. - * @port: Ptr to the uart_port. - * - * The Serial Mux does not support this function. - */ -static void mux_stop_rx(struct uart_port *port) -{ -} - -/** - * mux_enable_ms - Enable modum status interrupts. - * @port: Ptr to the uart_port. - * - * The Serial Mux does not support this function. - */ -static void mux_enable_ms(struct uart_port *port) -{ -} - -/** - * mux_break_ctl - Control the transmitssion of a break signal. - * @port: Ptr to the uart_port. - * @break_state: Raise/Lower the break signal. - * - * The Serial Mux does not support this function. - */ -static void mux_break_ctl(struct uart_port *port, int break_state) -{ -} - -/** - * mux_write - Write chars to the mux fifo. - * @port: Ptr to the uart_port. - * - * This function writes all the data from the uart buffer to - * the mux fifo. - */ -static void mux_write(struct uart_port *port) -{ - int count; - struct circ_buf *xmit = &port->state->xmit; - - if(port->x_char) { - UART_PUT_CHAR(port, port->x_char); - port->icount.tx++; - port->x_char = 0; - return; - } - - if(uart_circ_empty(xmit) || uart_tx_stopped(port)) { - mux_stop_tx(port); - return; - } - - count = (port->fifosize) - UART_GET_FIFO_CNT(port); - do { - UART_PUT_CHAR(port, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if(uart_circ_empty(xmit)) - break; - - } while(--count > 0); - - while(UART_GET_FIFO_CNT(port)) - udelay(1); - - if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - mux_stop_tx(port); -} - -/** - * mux_read - Read chars from the mux fifo. - * @port: Ptr to the uart_port. - * - * This reads all available data from the mux's fifo and pushes - * the data to the tty layer. - */ -static void mux_read(struct uart_port *port) -{ - int data; - struct tty_struct *tty = port->state->port.tty; - __u32 start_count = port->icount.rx; - - while(1) { - data = __raw_readl(port->membase + IO_DATA_REG_OFFSET); - - if (MUX_STATUS(data)) - continue; - - if (MUX_EOFIFO(data)) - break; - - port->icount.rx++; - - if (MUX_BREAK(data)) { - port->icount.brk++; - if(uart_handle_break(port)) - continue; - } - - if (uart_handle_sysrq_char(port, data & 0xffu)) - continue; - - tty_insert_flip_char(tty, data & 0xFF, TTY_NORMAL); - } - - if (start_count != port->icount.rx) { - tty_flip_buffer_push(tty); - } -} - -/** - * mux_startup - Initialize the port. - * @port: Ptr to the uart_port. - * - * Grab any resources needed for this port and start the - * mux timer. - */ -static int mux_startup(struct uart_port *port) -{ - mux_ports[port->line].enabled = 1; - return 0; -} - -/** - * mux_shutdown - Disable the port. - * @port: Ptr to the uart_port. - * - * Release any resources needed for the port. - */ -static void mux_shutdown(struct uart_port *port) -{ - mux_ports[port->line].enabled = 0; -} - -/** - * mux_set_termios - Chane port parameters. - * @port: Ptr to the uart_port. - * @termios: new termios settings. - * @old: old termios settings. - * - * The Serial Mux does not support this function. - */ -static void -mux_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ -} - -/** - * mux_type - Describe the port. - * @port: Ptr to the uart_port. - * - * Return a pointer to a string constant describing the - * specified port. - */ -static const char *mux_type(struct uart_port *port) -{ - return "Mux"; -} - -/** - * mux_release_port - Release memory and IO regions. - * @port: Ptr to the uart_port. - * - * Release any memory and IO region resources currently in use by - * the port. - */ -static void mux_release_port(struct uart_port *port) -{ -} - -/** - * mux_request_port - Request memory and IO regions. - * @port: Ptr to the uart_port. - * - * Request any memory and IO region resources required by the port. - * If any fail, no resources should be registered when this function - * returns, and it should return -EBUSY on failure. - */ -static int mux_request_port(struct uart_port *port) -{ - return 0; -} - -/** - * mux_config_port - Perform port autoconfiguration. - * @port: Ptr to the uart_port. - * @type: Bitmask of required configurations. - * - * Perform any autoconfiguration steps for the port. This function is - * called if the UPF_BOOT_AUTOCONF flag is specified for the port. - * [Note: This is required for now because of a bug in the Serial core. - * rmk has already submitted a patch to linus, should be available for - * 2.5.47.] - */ -static void mux_config_port(struct uart_port *port, int type) -{ - port->type = PORT_MUX; -} - -/** - * mux_verify_port - Verify the port information. - * @port: Ptr to the uart_port. - * @ser: Ptr to the serial information. - * - * Verify the new serial port information contained within serinfo is - * suitable for this port type. - */ -static int mux_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - if(port->membase == NULL) - return -EINVAL; - - return 0; -} - -/** - * mux_drv_poll - Mux poll function. - * @unused: Unused variable - * - * This function periodically polls the Serial MUX to check for new data. - */ -static void mux_poll(unsigned long unused) -{ - int i; - - for(i = 0; i < port_cnt; ++i) { - if(!mux_ports[i].enabled) - continue; - - mux_read(&mux_ports[i].port); - mux_write(&mux_ports[i].port); - } - - mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); -} - - -#ifdef CONFIG_SERIAL_MUX_CONSOLE -static void mux_console_write(struct console *co, const char *s, unsigned count) -{ - /* Wait until the FIFO drains. */ - while(UART_GET_FIFO_CNT(&mux_ports[0].port)) - udelay(1); - - while(count--) { - if(*s == '\n') { - UART_PUT_CHAR(&mux_ports[0].port, '\r'); - } - UART_PUT_CHAR(&mux_ports[0].port, *s++); - } - -} - -static int mux_console_setup(struct console *co, char *options) -{ - return 0; -} - -struct tty_driver *mux_console_device(struct console *co, int *index) -{ - *index = co->index; - return mux_driver.tty_driver; -} - -static struct console mux_console = { - .name = "ttyB", - .write = mux_console_write, - .device = mux_console_device, - .setup = mux_console_setup, - .flags = CON_ENABLED | CON_PRINTBUFFER, - .index = 0, -}; - -#define MUX_CONSOLE &mux_console -#else -#define MUX_CONSOLE NULL -#endif - -static struct uart_ops mux_pops = { - .tx_empty = mux_tx_empty, - .set_mctrl = mux_set_mctrl, - .get_mctrl = mux_get_mctrl, - .stop_tx = mux_stop_tx, - .start_tx = mux_start_tx, - .stop_rx = mux_stop_rx, - .enable_ms = mux_enable_ms, - .break_ctl = mux_break_ctl, - .startup = mux_startup, - .shutdown = mux_shutdown, - .set_termios = mux_set_termios, - .type = mux_type, - .release_port = mux_release_port, - .request_port = mux_request_port, - .config_port = mux_config_port, - .verify_port = mux_verify_port, -}; - -/** - * mux_probe - Determine if the Serial Mux should claim this device. - * @dev: The parisc device. - * - * Deterimine if the Serial Mux should claim this chip (return 0) - * or not (return 1). - */ -static int __init mux_probe(struct parisc_device *dev) -{ - int i, status; - - int port_count = get_mux_port_count(dev); - printk(KERN_INFO "Serial mux driver (%d ports) Revision: 0.6\n", port_count); - - dev_set_drvdata(&dev->dev, (void *)(long)port_count); - request_mem_region(dev->hpa.start + MUX_OFFSET, - port_count * MUX_LINE_OFFSET, "Mux"); - - if(!port_cnt) { - mux_driver.cons = MUX_CONSOLE; - - status = uart_register_driver(&mux_driver); - if(status) { - printk(KERN_ERR "Serial mux: Unable to register driver.\n"); - return 1; - } - } - - for(i = 0; i < port_count; ++i, ++port_cnt) { - struct uart_port *port = &mux_ports[port_cnt].port; - port->iobase = 0; - port->mapbase = dev->hpa.start + MUX_OFFSET + - (i * MUX_LINE_OFFSET); - port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET); - port->iotype = UPIO_MEM; - port->type = PORT_MUX; - port->irq = NO_IRQ; - port->uartclk = 0; - port->fifosize = MUX_FIFO_SIZE; - port->ops = &mux_pops; - port->flags = UPF_BOOT_AUTOCONF; - port->line = port_cnt; - - /* The port->timeout needs to match what is present in - * uart_wait_until_sent in serial_core.c. Otherwise - * the time spent in msleep_interruptable will be very - * long, causing the appearance of a console hang. - */ - port->timeout = HZ / 50; - spin_lock_init(&port->lock); - - status = uart_add_one_port(&mux_driver, port); - BUG_ON(status); - } - - return 0; -} - -static int __devexit mux_remove(struct parisc_device *dev) -{ - int i, j; - int port_count = (long)dev_get_drvdata(&dev->dev); - - /* Find Port 0 for this card in the mux_ports list. */ - for(i = 0; i < port_cnt; ++i) { - if(mux_ports[i].port.mapbase == dev->hpa.start + MUX_OFFSET) - break; - } - BUG_ON(i + port_count > port_cnt); - - /* Release the resources associated with each port on the device. */ - for(j = 0; j < port_count; ++j, ++i) { - struct uart_port *port = &mux_ports[i].port; - - uart_remove_one_port(&mux_driver, port); - if(port->membase) - iounmap(port->membase); - } - - release_mem_region(dev->hpa.start + MUX_OFFSET, port_count * MUX_LINE_OFFSET); - return 0; -} - -/* Hack. This idea was taken from the 8250_gsc.c on how to properly order - * the serial port detection in the proper order. The idea is we always - * want the builtin mux to be detected before addin mux cards, so we - * specifically probe for the builtin mux cards first. - * - * This table only contains the parisc_device_id of known builtin mux - * devices. All other mux cards will be detected by the generic mux_tbl. - */ -static struct parisc_device_id builtin_mux_tbl[] = { - { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x15, 0x0000D }, /* All K-class */ - { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x44, 0x0000D }, /* E35, E45, and E55 */ - { 0, } -}; - -static struct parisc_device_id mux_tbl[] = { - { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D }, - { 0, } -}; - -MODULE_DEVICE_TABLE(parisc, builtin_mux_tbl); -MODULE_DEVICE_TABLE(parisc, mux_tbl); - -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), -}; - -static struct parisc_driver serial_mux_driver = { - .name = "serial_mux", - .id_table = mux_tbl, - .probe = mux_probe, - .remove = __devexit_p(mux_remove), -}; - -/** - * mux_init - Serial MUX initialization procedure. - * - * Register the Serial MUX driver. - */ -static int __init mux_init(void) -{ - register_parisc_driver(&builtin_serial_mux_driver); - register_parisc_driver(&serial_mux_driver); - - if(port_cnt > 0) { - /* Start the Mux timer */ - init_timer(&mux_timer); - mux_timer.function = mux_poll; - mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); - -#ifdef CONFIG_SERIAL_MUX_CONSOLE - register_console(&mux_console); -#endif - } - - return 0; -} - -/** - * mux_exit - Serial MUX cleanup procedure. - * - * Unregister the Serial MUX driver from the tty layer. - */ -static void __exit mux_exit(void) -{ - /* Delete the Mux timer. */ - if(port_cnt > 0) { - del_timer(&mux_timer); -#ifdef CONFIG_SERIAL_MUX_CONSOLE - unregister_console(&mux_console); -#endif - } - - unregister_parisc_driver(&builtin_serial_mux_driver); - unregister_parisc_driver(&serial_mux_driver); - uart_unregister_driver(&mux_driver); -} - -module_init(mux_init); -module_exit(mux_exit); - -MODULE_AUTHOR("Ryan Bradetich"); -MODULE_DESCRIPTION("Serial MUX driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(MUX_MAJOR); diff --git a/drivers/serial/netx-serial.c b/drivers/serial/netx-serial.c deleted file mode 100644 index 7735c9f35fa0..000000000000 --- a/drivers/serial/netx-serial.c +++ /dev/null @@ -1,750 +0,0 @@ -/* - * drivers/serial/netx-serial.c - * - * Copyright (c) 2005 Sascha Hauer , Pengutronix - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* We've been assigned a range on the "Low-density serial ports" major */ -#define SERIAL_NX_MAJOR 204 -#define MINOR_START 170 - -enum uart_regs { - UART_DR = 0x00, - UART_SR = 0x04, - UART_LINE_CR = 0x08, - UART_BAUDDIV_MSB = 0x0c, - UART_BAUDDIV_LSB = 0x10, - UART_CR = 0x14, - UART_FR = 0x18, - UART_IIR = 0x1c, - UART_ILPR = 0x20, - UART_RTS_CR = 0x24, - UART_RTS_LEAD = 0x28, - UART_RTS_TRAIL = 0x2c, - UART_DRV_ENABLE = 0x30, - UART_BRM_CR = 0x34, - UART_RXFIFO_IRQLEVEL = 0x38, - UART_TXFIFO_IRQLEVEL = 0x3c, -}; - -#define SR_FE (1<<0) -#define SR_PE (1<<1) -#define SR_BE (1<<2) -#define SR_OE (1<<3) - -#define LINE_CR_BRK (1<<0) -#define LINE_CR_PEN (1<<1) -#define LINE_CR_EPS (1<<2) -#define LINE_CR_STP2 (1<<3) -#define LINE_CR_FEN (1<<4) -#define LINE_CR_5BIT (0<<5) -#define LINE_CR_6BIT (1<<5) -#define LINE_CR_7BIT (2<<5) -#define LINE_CR_8BIT (3<<5) -#define LINE_CR_BITS_MASK (3<<5) - -#define CR_UART_EN (1<<0) -#define CR_SIREN (1<<1) -#define CR_SIRLP (1<<2) -#define CR_MSIE (1<<3) -#define CR_RIE (1<<4) -#define CR_TIE (1<<5) -#define CR_RTIE (1<<6) -#define CR_LBE (1<<7) - -#define FR_CTS (1<<0) -#define FR_DSR (1<<1) -#define FR_DCD (1<<2) -#define FR_BUSY (1<<3) -#define FR_RXFE (1<<4) -#define FR_TXFF (1<<5) -#define FR_RXFF (1<<6) -#define FR_TXFE (1<<7) - -#define IIR_MIS (1<<0) -#define IIR_RIS (1<<1) -#define IIR_TIS (1<<2) -#define IIR_RTIS (1<<3) -#define IIR_MASK 0xf - -#define RTS_CR_AUTO (1<<0) -#define RTS_CR_RTS (1<<1) -#define RTS_CR_COUNT (1<<2) -#define RTS_CR_MOD2 (1<<3) -#define RTS_CR_RTS_POL (1<<4) -#define RTS_CR_CTS_CTR (1<<5) -#define RTS_CR_CTS_POL (1<<6) -#define RTS_CR_STICK (1<<7) - -#define UART_PORT_SIZE 0x40 -#define DRIVER_NAME "netx-uart" - -struct netx_port { - struct uart_port port; -}; - -static void netx_stop_tx(struct uart_port *port) -{ - unsigned int val; - val = readl(port->membase + UART_CR); - writel(val & ~CR_TIE, port->membase + UART_CR); -} - -static void netx_stop_rx(struct uart_port *port) -{ - unsigned int val; - val = readl(port->membase + UART_CR); - writel(val & ~CR_RIE, port->membase + UART_CR); -} - -static void netx_enable_ms(struct uart_port *port) -{ - unsigned int val; - val = readl(port->membase + UART_CR); - writel(val | CR_MSIE, port->membase + UART_CR); -} - -static inline void netx_transmit_buffer(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - if (port->x_char) { - writel(port->x_char, port->membase + UART_DR); - port->icount.tx++; - port->x_char = 0; - return; - } - - if (uart_tx_stopped(port) || uart_circ_empty(xmit)) { - netx_stop_tx(port); - return; - } - - do { - /* send xmit->buf[xmit->tail] - * out the port here */ - writel(xmit->buf[xmit->tail], port->membase + UART_DR); - xmit->tail = (xmit->tail + 1) & - (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (!(readl(port->membase + UART_FR) & FR_TXFF)); - - if (uart_circ_empty(xmit)) - netx_stop_tx(port); -} - -static void netx_start_tx(struct uart_port *port) -{ - writel( - readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR); - - if (!(readl(port->membase + UART_FR) & FR_TXFF)) - netx_transmit_buffer(port); -} - -static unsigned int netx_tx_empty(struct uart_port *port) -{ - return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT; -} - -static void netx_txint(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - netx_stop_tx(port); - return; - } - - netx_transmit_buffer(port); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static void netx_rxint(struct uart_port *port) -{ - unsigned char rx, flg, status; - struct tty_struct *tty = port->state->port.tty; - - while (!(readl(port->membase + UART_FR) & FR_RXFE)) { - rx = readl(port->membase + UART_DR); - flg = TTY_NORMAL; - port->icount.rx++; - status = readl(port->membase + UART_SR); - if (status & SR_BE) { - writel(0, port->membase + UART_SR); - if (uart_handle_break(port)) - continue; - } - - if (unlikely(status & (SR_FE | SR_PE | SR_OE))) { - - if (status & SR_PE) - port->icount.parity++; - else if (status & SR_FE) - port->icount.frame++; - if (status & SR_OE) - port->icount.overrun++; - - status &= port->read_status_mask; - - if (status & SR_BE) - flg = TTY_BREAK; - else if (status & SR_PE) - flg = TTY_PARITY; - else if (status & SR_FE) - flg = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, rx)) - continue; - - uart_insert_char(port, status, SR_OE, rx, flg); - } - - tty_flip_buffer_push(tty); - return; -} - -static irqreturn_t netx_int(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - unsigned long flags; - unsigned char status; - - spin_lock_irqsave(&port->lock,flags); - - status = readl(port->membase + UART_IIR) & IIR_MASK; - while (status) { - if (status & IIR_RIS) - netx_rxint(port); - if (status & IIR_TIS) - netx_txint(port); - if (status & IIR_MIS) { - if (readl(port->membase + UART_FR) & FR_CTS) - uart_handle_cts_change(port, 1); - else - uart_handle_cts_change(port, 0); - } - writel(0, port->membase + UART_IIR); - status = readl(port->membase + UART_IIR) & IIR_MASK; - } - - spin_unlock_irqrestore(&port->lock,flags); - return IRQ_HANDLED; -} - -static unsigned int netx_get_mctrl(struct uart_port *port) -{ - unsigned int ret = TIOCM_DSR | TIOCM_CAR; - - if (readl(port->membase + UART_FR) & FR_CTS) - ret |= TIOCM_CTS; - - return ret; -} - -static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - unsigned int val; - - /* FIXME: Locking needed ? */ - if (mctrl & TIOCM_RTS) { - val = readl(port->membase + UART_RTS_CR); - writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR); - } -} - -static void netx_break_ctl(struct uart_port *port, int break_state) -{ - unsigned int line_cr; - spin_lock_irq(&port->lock); - - line_cr = readl(port->membase + UART_LINE_CR); - if (break_state != 0) - line_cr |= LINE_CR_BRK; - else - line_cr &= ~LINE_CR_BRK; - writel(line_cr, port->membase + UART_LINE_CR); - - spin_unlock_irq(&port->lock); -} - -static int netx_startup(struct uart_port *port) -{ - int ret; - - ret = request_irq(port->irq, netx_int, 0, - DRIVER_NAME, port); - if (ret) { - dev_err(port->dev, "unable to grab irq%d\n",port->irq); - goto exit; - } - - writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN, - port->membase + UART_LINE_CR); - - writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN, - port->membase + UART_CR); - -exit: - return ret; -} - -static void netx_shutdown(struct uart_port *port) -{ - writel(0, port->membase + UART_CR) ; - - free_irq(port->irq, port); -} - -static void -netx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud, quot; - unsigned char old_cr; - unsigned char line_cr = LINE_CR_FEN; - unsigned char rts_cr = 0; - - switch (termios->c_cflag & CSIZE) { - case CS5: - line_cr |= LINE_CR_5BIT; - break; - case CS6: - line_cr |= LINE_CR_6BIT; - break; - case CS7: - line_cr |= LINE_CR_7BIT; - break; - case CS8: - line_cr |= LINE_CR_8BIT; - break; - } - - if (termios->c_cflag & CSTOPB) - line_cr |= LINE_CR_STP2; - - if (termios->c_cflag & PARENB) { - line_cr |= LINE_CR_PEN; - if (!(termios->c_cflag & PARODD)) - line_cr |= LINE_CR_EPS; - } - - if (termios->c_cflag & CRTSCTS) - rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL; - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = baud * 4096; - quot /= 1000; - quot *= 256; - quot /= 100000; - - spin_lock_irq(&port->lock); - - uart_update_timeout(port, termios->c_cflag, baud); - - old_cr = readl(port->membase + UART_CR); - - /* disable interrupts */ - writel(old_cr & ~(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE), - port->membase + UART_CR); - - /* drain transmitter */ - while (readl(port->membase + UART_FR) & FR_BUSY); - - /* disable UART */ - writel(old_cr & ~CR_UART_EN, port->membase + UART_CR); - - /* modem status interrupts */ - old_cr &= ~CR_MSIE; - if (UART_ENABLE_MS(port, termios->c_cflag)) - old_cr |= CR_MSIE; - - writel((quot>>8) & 0xff, port->membase + UART_BAUDDIV_MSB); - writel(quot & 0xff, port->membase + UART_BAUDDIV_LSB); - writel(line_cr, port->membase + UART_LINE_CR); - - writel(rts_cr, port->membase + UART_RTS_CR); - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= SR_PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= SR_BE; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= SR_PE; - } - - port->read_status_mask = 0; - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= SR_BE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= SR_PE | SR_FE; - - writel(old_cr, port->membase + UART_CR); - - spin_unlock_irq(&port->lock); -} - -static const char *netx_type(struct uart_port *port) -{ - return port->type == PORT_NETX ? "NETX" : NULL; -} - -static void netx_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, UART_PORT_SIZE); -} - -static int netx_request_port(struct uart_port *port) -{ - return request_mem_region(port->mapbase, UART_PORT_SIZE, - DRIVER_NAME) != NULL ? 0 : -EBUSY; -} - -static void netx_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE && netx_request_port(port) == 0) - port->type = PORT_NETX; -} - -static int -netx_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_NETX) - ret = -EINVAL; - - return ret; -} - -static struct uart_ops netx_pops = { - .tx_empty = netx_tx_empty, - .set_mctrl = netx_set_mctrl, - .get_mctrl = netx_get_mctrl, - .stop_tx = netx_stop_tx, - .start_tx = netx_start_tx, - .stop_rx = netx_stop_rx, - .enable_ms = netx_enable_ms, - .break_ctl = netx_break_ctl, - .startup = netx_startup, - .shutdown = netx_shutdown, - .set_termios = netx_set_termios, - .type = netx_type, - .release_port = netx_release_port, - .request_port = netx_request_port, - .config_port = netx_config_port, - .verify_port = netx_verify_port, -}; - -static struct netx_port netx_ports[] = { - { - .port = { - .type = PORT_NETX, - .iotype = UPIO_MEM, - .membase = (char __iomem *)io_p2v(NETX_PA_UART0), - .mapbase = NETX_PA_UART0, - .irq = NETX_IRQ_UART0, - .uartclk = 100000000, - .fifosize = 16, - .flags = UPF_BOOT_AUTOCONF, - .ops = &netx_pops, - .line = 0, - }, - }, { - .port = { - .type = PORT_NETX, - .iotype = UPIO_MEM, - .membase = (char __iomem *)io_p2v(NETX_PA_UART1), - .mapbase = NETX_PA_UART1, - .irq = NETX_IRQ_UART1, - .uartclk = 100000000, - .fifosize = 16, - .flags = UPF_BOOT_AUTOCONF, - .ops = &netx_pops, - .line = 1, - }, - }, { - .port = { - .type = PORT_NETX, - .iotype = UPIO_MEM, - .membase = (char __iomem *)io_p2v(NETX_PA_UART2), - .mapbase = NETX_PA_UART2, - .irq = NETX_IRQ_UART2, - .uartclk = 100000000, - .fifosize = 16, - .flags = UPF_BOOT_AUTOCONF, - .ops = &netx_pops, - .line = 2, - }, - } -}; - -#ifdef CONFIG_SERIAL_NETX_CONSOLE - -static void netx_console_putchar(struct uart_port *port, int ch) -{ - while (readl(port->membase + UART_FR) & FR_BUSY); - writel(ch, port->membase + UART_DR); -} - -static void -netx_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_port *port = &netx_ports[co->index].port; - unsigned char cr_save; - - cr_save = readl(port->membase + UART_CR); - writel(cr_save | CR_UART_EN, port->membase + UART_CR); - - uart_console_write(port, s, count, netx_console_putchar); - - while (readl(port->membase + UART_FR) & FR_BUSY); - writel(cr_save, port->membase + UART_CR); -} - -static void __init -netx_console_get_options(struct uart_port *port, int *baud, - int *parity, int *bits, int *flow) -{ - unsigned char line_cr; - - *baud = (readl(port->membase + UART_BAUDDIV_MSB) << 8) | - readl(port->membase + UART_BAUDDIV_LSB); - *baud *= 1000; - *baud /= 4096; - *baud *= 1000; - *baud /= 256; - *baud *= 100; - - line_cr = readl(port->membase + UART_LINE_CR); - *parity = 'n'; - if (line_cr & LINE_CR_PEN) { - if (line_cr & LINE_CR_EPS) - *parity = 'e'; - else - *parity = 'o'; - } - - switch (line_cr & LINE_CR_BITS_MASK) { - case LINE_CR_8BIT: - *bits = 8; - break; - case LINE_CR_7BIT: - *bits = 7; - break; - case LINE_CR_6BIT: - *bits = 6; - break; - case LINE_CR_5BIT: - *bits = 5; - break; - } - - if (readl(port->membase + UART_RTS_CR) & RTS_CR_AUTO) - *flow = 'r'; -} - -static int __init -netx_console_setup(struct console *co, char *options) -{ - struct netx_port *sport; - int baud = 9600; - 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. - */ - if (co->index == -1 || co->index >= ARRAY_SIZE(netx_ports)) - co->index = 0; - sport = &netx_ports[co->index]; - - if (options) { - uart_parse_options(options, &baud, &parity, &bits, &flow); - } else { - /* if the UART is enabled, assume it has been correctly setup - * by the bootloader and get the options - */ - if (readl(sport->port.membase + UART_CR) & CR_UART_EN) { - netx_console_get_options(&sport->port, &baud, - &parity, &bits, &flow); - } - - } - - return uart_set_options(&sport->port, co, baud, parity, bits, flow); -} - -static struct uart_driver netx_reg; -static struct console netx_console = { - .name = "ttyNX", - .write = netx_console_write, - .device = uart_console_device, - .setup = netx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &netx_reg, -}; - -static int __init netx_console_init(void) -{ - register_console(&netx_console); - return 0; -} -console_initcall(netx_console_init); - -#define NETX_CONSOLE &netx_console -#else -#define NETX_CONSOLE NULL -#endif - -static struct uart_driver netx_reg = { - .owner = THIS_MODULE, - .driver_name = DRIVER_NAME, - .dev_name = "ttyNX", - .major = SERIAL_NX_MAJOR, - .minor = MINOR_START, - .nr = ARRAY_SIZE(netx_ports), - .cons = NETX_CONSOLE, -}; - -static int serial_netx_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct netx_port *sport = platform_get_drvdata(pdev); - - if (sport) - uart_suspend_port(&netx_reg, &sport->port); - - return 0; -} - -static int serial_netx_resume(struct platform_device *pdev) -{ - struct netx_port *sport = platform_get_drvdata(pdev); - - if (sport) - uart_resume_port(&netx_reg, &sport->port); - - return 0; -} - -static int serial_netx_probe(struct platform_device *pdev) -{ - struct uart_port *port = &netx_ports[pdev->id].port; - - dev_info(&pdev->dev, "initialising\n"); - - port->dev = &pdev->dev; - - writel(1, port->membase + UART_RXFIFO_IRQLEVEL); - uart_add_one_port(&netx_reg, &netx_ports[pdev->id].port); - platform_set_drvdata(pdev, &netx_ports[pdev->id]); - - return 0; -} - -static int serial_netx_remove(struct platform_device *pdev) -{ - struct netx_port *sport = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (sport) - uart_remove_one_port(&netx_reg, &sport->port); - - return 0; -} - -static struct platform_driver serial_netx_driver = { - .probe = serial_netx_probe, - .remove = serial_netx_remove, - - .suspend = serial_netx_suspend, - .resume = serial_netx_resume, - - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init netx_serial_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: NetX driver\n"); - - ret = uart_register_driver(&netx_reg); - if (ret) - return ret; - - ret = platform_driver_register(&serial_netx_driver); - if (ret != 0) - uart_unregister_driver(&netx_reg); - - return 0; -} - -static void __exit netx_serial_exit(void) -{ - platform_driver_unregister(&serial_netx_driver); - uart_unregister_driver(&netx_reg); -} - -module_init(netx_serial_init); -module_exit(netx_serial_exit); - -MODULE_AUTHOR("Sascha Hauer"); -MODULE_DESCRIPTION("NetX serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c deleted file mode 100644 index de173671e3d0..000000000000 --- a/drivers/serial/nwpserial.c +++ /dev/null @@ -1,477 +0,0 @@ -/* - * Serial Port driver for a NWP uart device - * - * Copyright (C) 2008 IBM Corp., Benjamin Krill - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define NWPSERIAL_NR 2 - -#define NWPSERIAL_STATUS_RXVALID 0x1 -#define NWPSERIAL_STATUS_TXFULL 0x2 - -struct nwpserial_port { - struct uart_port port; - dcr_host_t dcr_host; - unsigned int ier; - unsigned int mcr; -}; - -static DEFINE_MUTEX(nwpserial_mutex); -static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; - -static void wait_for_bits(struct nwpserial_port *up, int bits) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = dcr_read(up->dcr_host, UART_LSR); - - if (--tmout == 0) - break; - udelay(1); - } while ((status & bits) != bits); -} - -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE -static void nwpserial_console_putchar(struct uart_port *port, int c) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - /* check if tx buffer is full */ - wait_for_bits(up, UART_LSR_THRE); - dcr_write(up->dcr_host, UART_TX, c); - up->port.icount.tx++; -} - -static void -nwpserial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct nwpserial_port *up = &nwpserial_ports[co->index]; - unsigned long flags; - int locked = 1; - - if (oops_in_progress) - locked = spin_trylock_irqsave(&up->port.lock, flags); - else - spin_lock_irqsave(&up->port.lock, flags); - - /* save and disable interrupt */ - up->ier = dcr_read(up->dcr_host, UART_IER); - dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); - - uart_console_write(&up->port, s, count, nwpserial_console_putchar); - - /* wait for transmitter to become empty */ - while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) - cpu_relax(); - - /* restore interrupt state */ - dcr_write(up->dcr_host, UART_IER, up->ier); - - if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static struct uart_driver nwpserial_reg; -static struct console nwpserial_console = { - .name = "ttySQ", - .write = nwpserial_console_write, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &nwpserial_reg, -}; -#define NWPSERIAL_CONSOLE (&nwpserial_console) -#else -#define NWPSERIAL_CONSOLE NULL -#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ - -/**************************************************************************/ - -static int nwpserial_request_port(struct uart_port *port) -{ - return 0; -} - -static void nwpserial_release_port(struct uart_port *port) -{ - /* N/A */ -} - -static void nwpserial_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_NWPSERIAL; -} - -static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) -{ - struct nwpserial_port *up = dev_id; - struct tty_struct *tty = up->port.state->port.tty; - irqreturn_t ret; - unsigned int iir; - unsigned char ch; - - spin_lock(&up->port.lock); - - /* check if the uart was the interrupt source. */ - iir = dcr_read(up->dcr_host, UART_IIR); - if (!iir) { - ret = IRQ_NONE; - goto out; - } - - do { - up->port.icount.rx++; - ch = dcr_read(up->dcr_host, UART_RX); - if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) - tty_insert_flip_char(tty, ch, TTY_NORMAL); - } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR); - - tty_flip_buffer_push(tty); - ret = IRQ_HANDLED; - - /* clear interrupt */ - dcr_write(up->dcr_host, UART_IIR, 1); -out: - spin_unlock(&up->port.lock); - return ret; -} - -static int nwpserial_startup(struct uart_port *port) -{ - struct nwpserial_port *up; - int err; - - up = container_of(port, struct nwpserial_port, port); - - /* disable flow control by default */ - up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; - dcr_write(up->dcr_host, UART_MCR, up->mcr); - - /* register interrupt handler */ - err = request_irq(up->port.irq, nwpserial_interrupt, - IRQF_SHARED, "nwpserial", up); - if (err) - return err; - - /* enable interrupts */ - up->ier = UART_IER_RDI; - dcr_write(up->dcr_host, UART_IER, up->ier); - - /* enable receiving */ - up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; - - return 0; -} - -static void nwpserial_shutdown(struct uart_port *port) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - - /* disable receiving */ - up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; - - /* disable interrupts from this port */ - up->ier = 0; - dcr_write(up->dcr_host, UART_IER, up->ier); - - /* free irq */ - free_irq(up->port.irq, port); -} - -static int nwpserial_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - return -EINVAL; -} - -static const char *nwpserial_type(struct uart_port *port) -{ - return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; -} - -static void nwpserial_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - - up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID - | NWPSERIAL_STATUS_TXFULL; - - up->port.ignore_status_mask = 0; - /* ignore all characters if CREAD is not set */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; - - /* Copy back the old hardware settings */ - if (old) - tty_termios_copy_hw(termios, old); -} - -static void nwpserial_break_ctl(struct uart_port *port, int ctl) -{ - /* N/A */ -} - -static void nwpserial_enable_ms(struct uart_port *port) -{ - /* N/A */ -} - -static void nwpserial_stop_rx(struct uart_port *port) -{ - struct nwpserial_port *up; - up = container_of(port, struct nwpserial_port, port); - /* don't forward any more data (like !CREAD) */ - up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; -} - -static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) -{ - /* check if tx buffer is full */ - wait_for_bits(up, UART_LSR_THRE); - dcr_write(up->dcr_host, UART_TX, c); - up->port.icount.tx++; -} - -static void nwpserial_start_tx(struct uart_port *port) -{ - struct nwpserial_port *up; - struct circ_buf *xmit; - up = container_of(port, struct nwpserial_port, port); - xmit = &up->port.state->xmit; - - if (port->x_char) { - nwpserial_putchar(up, up->port.x_char); - port->x_char = 0; - } - - while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { - nwpserial_putchar(up, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); - } -} - -static unsigned int nwpserial_get_mctrl(struct uart_port *port) -{ - return 0; -} - -static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* N/A */ -} - -static void nwpserial_stop_tx(struct uart_port *port) -{ - /* N/A */ -} - -static unsigned int nwpserial_tx_empty(struct uart_port *port) -{ - struct nwpserial_port *up; - unsigned long flags; - int ret; - up = container_of(port, struct nwpserial_port, port); - - spin_lock_irqsave(&up->port.lock, flags); - ret = dcr_read(up->dcr_host, UART_LSR); - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; -} - -static struct uart_ops nwpserial_pops = { - .tx_empty = nwpserial_tx_empty, - .set_mctrl = nwpserial_set_mctrl, - .get_mctrl = nwpserial_get_mctrl, - .stop_tx = nwpserial_stop_tx, - .start_tx = nwpserial_start_tx, - .stop_rx = nwpserial_stop_rx, - .enable_ms = nwpserial_enable_ms, - .break_ctl = nwpserial_break_ctl, - .startup = nwpserial_startup, - .shutdown = nwpserial_shutdown, - .set_termios = nwpserial_set_termios, - .type = nwpserial_type, - .release_port = nwpserial_release_port, - .request_port = nwpserial_request_port, - .config_port = nwpserial_config_port, - .verify_port = nwpserial_verify_port, -}; - -static struct uart_driver nwpserial_reg = { - .owner = THIS_MODULE, - .driver_name = "nwpserial", - .dev_name = "ttySQ", - .major = TTY_MAJOR, - .minor = 68, - .nr = NWPSERIAL_NR, - .cons = NWPSERIAL_CONSOLE, -}; - -int nwpserial_register_port(struct uart_port *port) -{ - struct nwpserial_port *up = NULL; - int ret = -1; - int i; - static int first = 1; - int dcr_len; - int dcr_base; - struct device_node *dn; - - mutex_lock(&nwpserial_mutex); - - dn = port->dev->of_node; - if (dn == NULL) - goto out; - - /* get dcr base. */ - dcr_base = dcr_resource_start(dn, 0); - - /* find matching entry */ - for (i = 0; i < NWPSERIAL_NR; i++) - if (nwpserial_ports[i].port.iobase == dcr_base) { - up = &nwpserial_ports[i]; - break; - } - - /* we didn't find a mtching entry, search for a free port */ - if (up == NULL) - for (i = 0; i < NWPSERIAL_NR; i++) - if (nwpserial_ports[i].port.type == PORT_UNKNOWN && - nwpserial_ports[i].port.iobase == 0) { - up = &nwpserial_ports[i]; - break; - } - - if (up == NULL) { - ret = -EBUSY; - goto out; - } - - if (first) - uart_register_driver(&nwpserial_reg); - first = 0; - - up->port.membase = port->membase; - up->port.irq = port->irq; - up->port.uartclk = port->uartclk; - up->port.fifosize = port->fifosize; - up->port.regshift = port->regshift; - up->port.iotype = port->iotype; - up->port.flags = port->flags; - up->port.mapbase = port->mapbase; - up->port.private_data = port->private_data; - - if (port->dev) - up->port.dev = port->dev; - - if (up->port.iobase != dcr_base) { - up->port.ops = &nwpserial_pops; - up->port.fifosize = 16; - - spin_lock_init(&up->port.lock); - - up->port.iobase = dcr_base; - dcr_len = dcr_resource_len(dn, 0); - - up->dcr_host = dcr_map(dn, dcr_base, dcr_len); - if (!DCR_MAP_OK(up->dcr_host)) { - printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); - goto out; - } - } - - ret = uart_add_one_port(&nwpserial_reg, &up->port); - if (ret == 0) - ret = up->port.line; - -out: - mutex_unlock(&nwpserial_mutex); - - return ret; -} -EXPORT_SYMBOL(nwpserial_register_port); - -void nwpserial_unregister_port(int line) -{ - struct nwpserial_port *up = &nwpserial_ports[line]; - mutex_lock(&nwpserial_mutex); - uart_remove_one_port(&nwpserial_reg, &up->port); - - up->port.type = PORT_UNKNOWN; - - mutex_unlock(&nwpserial_mutex); -} -EXPORT_SYMBOL(nwpserial_unregister_port); - -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE -static int __init nwpserial_console_init(void) -{ - struct nwpserial_port *up = NULL; - struct device_node *dn; - const char *name; - int dcr_base; - int dcr_len; - int i; - - /* search for a free port */ - for (i = 0; i < NWPSERIAL_NR; i++) - if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { - up = &nwpserial_ports[i]; - break; - } - - if (up == NULL) - return -1; - - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name == NULL) - return -1; - - dn = of_find_node_by_path(name); - if (!dn) - return -1; - - spin_lock_init(&up->port.lock); - up->port.ops = &nwpserial_pops; - up->port.type = PORT_NWPSERIAL; - up->port.fifosize = 16; - - dcr_base = dcr_resource_start(dn, 0); - dcr_len = dcr_resource_len(dn, 0); - up->port.iobase = dcr_base; - - up->dcr_host = dcr_map(dn, dcr_base, dcr_len); - if (!DCR_MAP_OK(up->dcr_host)) { - printk("Cannot map DCR resources for SERIAL"); - return -1; - } - register_console(&nwpserial_console); - return 0; -} -console_initcall(nwpserial_console_init); -#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c deleted file mode 100644 index 5c7abe4c94dd..000000000000 --- a/drivers/serial/of_serial.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Serial Port driver for Open Firmware platform devices - * - * Copyright (C) 2006 Arnd Bergmann , IBM Corp. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct of_serial_info { - int type; - int line; -}; - -/* - * 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) -{ - struct resource resource; - struct device_node *np = ofdev->dev.of_node; - const __be32 *clk, *spd; - const __be32 *prop; - int ret, prop_size; - - memset(port, 0, sizeof *port); - spd = of_get_property(np, "current-speed", NULL); - clk = of_get_property(np, "clock-frequency", NULL); - if (!clk) { - dev_warn(&ofdev->dev, "no clock-frequency property set\n"); - return -ENODEV; - } - - ret = of_address_to_resource(np, 0, &resource); - if (ret) { - dev_warn(&ofdev->dev, "invalid address\n"); - return ret; - } - - spin_lock_init(&port->lock); - port->mapbase = resource.start; - - /* Check for shifted address mapping */ - prop = of_get_property(np, "reg-offset", &prop_size); - if (prop && (prop_size == sizeof(u32))) - port->mapbase += be32_to_cpup(prop); - - /* Check for registers offset within the devices address range */ - prop = of_get_property(np, "reg-shift", &prop_size); - if (prop && (prop_size == sizeof(u32))) - port->regshift = be32_to_cpup(prop); - - port->irq = irq_of_parse_and_map(np, 0); - port->iotype = UPIO_MEM; - port->type = type; - port->uartclk = be32_to_cpup(clk); - port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP - | UPF_FIXED_PORT | UPF_FIXED_TYPE; - port->dev = &ofdev->dev; - /* If current-speed was set, then try not to change it. */ - if (spd) - port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); - - return 0; -} - -/* - * Try to register a serial port - */ -static int __devinit of_platform_serial_probe(struct platform_device *ofdev, - const struct of_device_id *id) -{ - struct of_serial_info *info; - struct uart_port port; - int port_type; - int ret; - - if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) - return -EBUSY; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (info == NULL) - return -ENOMEM; - - port_type = (unsigned long)id->data; - ret = of_platform_serial_setup(ofdev, port_type, &port); - if (ret) - goto out; - - switch (port_type) { -#ifdef CONFIG_SERIAL_8250 - case PORT_8250 ... PORT_MAX_8250: - ret = serial8250_register_port(&port); - break; -#endif -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL - case PORT_NWPSERIAL: - ret = nwpserial_register_port(&port); - break; -#endif - default: - /* need to add code for these */ - case PORT_UNKNOWN: - dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); - ret = -ENODEV; - break; - } - if (ret < 0) - goto out; - - info->type = port_type; - info->line = ret; - dev_set_drvdata(&ofdev->dev, info); - return 0; -out: - kfree(info); - irq_dispose_mapping(port.irq); - return ret; -} - -/* - * Release a line - */ -static int of_platform_serial_remove(struct platform_device *ofdev) -{ - struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); - switch (info->type) { -#ifdef CONFIG_SERIAL_8250 - case PORT_8250 ... PORT_MAX_8250: - serial8250_unregister_port(info->line); - break; -#endif -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL - case PORT_NWPSERIAL: - nwpserial_unregister_port(info->line); - break; -#endif - default: - /* need to add code for these */ - break; - } - kfree(info); - return 0; -} - -/* - * A few common types, add more as needed. - */ -static struct of_device_id __devinitdata of_platform_serial_table[] = { - { .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, }, - { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, - { .type = "serial", .compatible = "ns16550a", .data = (void *)PORT_16550A, }, - { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, - { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, - { .type = "serial", .compatible = "ns16850", .data = (void *)PORT_16850, }, -#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL - { .type = "serial", .compatible = "ibm,qpace-nwp-serial", - .data = (void *)PORT_NWPSERIAL, }, -#endif - { .type = "serial", .data = (void *)PORT_UNKNOWN, }, - { /* end of list */ }, -}; - -static struct of_platform_driver of_platform_serial_driver = { - .driver = { - .name = "of_serial", - .owner = THIS_MODULE, - .of_match_table = of_platform_serial_table, - }, - .probe = of_platform_serial_probe, - .remove = of_platform_serial_remove, -}; - -static int __init of_platform_serial_init(void) -{ - return of_register_platform_driver(&of_platform_serial_driver); -} -module_init(of_platform_serial_init); - -static void __exit of_platform_serial_exit(void) -{ - return of_unregister_platform_driver(&of_platform_serial_driver); -}; -module_exit(of_platform_serial_exit); - -MODULE_AUTHOR("Arnd Bergmann "); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); diff --git a/drivers/serial/omap-serial.c b/drivers/serial/omap-serial.c deleted file mode 100644 index 7f2f01058789..000000000000 --- a/drivers/serial/omap-serial.c +++ /dev/null @@ -1,1359 +0,0 @@ -/* - * Driver for OMAP-UART controller. - * Based on drivers/serial/8250.c - * - * Copyright (C) 2010 Texas Instruments. - * - * Authors: - * Govindraj R - * Thara Gopinath - * - * 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. - * - * Note: This driver is made seperate from 8250 driver as we cannot - * over load 8250 driver with omap platform specific configuration for - * features like DMA, it makes easier to implement features like DMA and - * hardware flow control and software flow control configuration with - * this driver as required for the omap-platform. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; - -/* Forward declaration of functions */ -static void uart_tx_dma_callback(int lch, u16 ch_status, void *data); -static void serial_omap_rx_timeout(unsigned long uart_no); -static int serial_omap_start_rxdma(struct uart_omap_port *up); - -static inline unsigned int serial_in(struct uart_omap_port *up, int offset) -{ - offset <<= up->port.regshift; - return readw(up->port.membase + offset); -} - -static inline void serial_out(struct uart_omap_port *up, int offset, int value) -{ - offset <<= up->port.regshift; - writew(value, up->port.membase + offset); -} - -static inline void serial_omap_clear_fifos(struct uart_omap_port *up) -{ - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); -} - -/* - * serial_omap_get_divisor - calculate divisor value - * @port: uart port info - * @baud: baudrate for which divisor needs to be calculated. - * - * We have written our own function to get the divisor so as to support - * 13x mode. 3Mbps Baudrate as an different divisor. - * Reference OMAP TRM Chapter 17: - * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates - * referring to oversampling - divisor value - * baudrate 460,800 to 3,686,400 all have divisor 13 - * except 3,000,000 which has divisor value 16 - */ -static unsigned int -serial_omap_get_divisor(struct uart_port *port, unsigned int baud) -{ - unsigned int divisor; - - if (baud > OMAP_MODE13X_SPEED && baud != 3000000) - divisor = 13; - else - divisor = 16; - return port->uartclk/(baud * divisor); -} - -static void serial_omap_stop_rxdma(struct uart_omap_port *up) -{ - if (up->uart_dma.rx_dma_used) { - del_timer(&up->uart_dma.rx_timer); - omap_stop_dma(up->uart_dma.rx_dma_channel); - omap_free_dma(up->uart_dma.rx_dma_channel); - up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; - up->uart_dma.rx_dma_used = false; - } -} - -static void serial_omap_enable_ms(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - - dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id); - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void serial_omap_stop_tx(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - - if (up->use_dma && - up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { - /* - * Check if dma is still active. If yes do nothing, - * return. Else stop dma - */ - if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel)) - return; - omap_stop_dma(up->uart_dma.tx_dma_channel); - omap_free_dma(up->uart_dma.tx_dma_channel); - up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; - } - - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial_omap_stop_rx(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - - if (up->use_dma) - serial_omap_stop_rxdma(up); - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static inline void receive_chars(struct uart_omap_port *up, int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned int flag; - unsigned char ch, lsr = *status; - int max_count = 256; - - do { - if (likely(lsr & UART_LSR_DR)) - ch = serial_in(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { - /* - * For statistics only - */ - if (lsr & UART_LSR_BI) { - lsr &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (lsr & UART_LSR_PE) { - up->port.icount.parity++; - } else if (lsr & UART_LSR_FE) { - up->port.icount.frame++; - } - - if (lsr & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ignored. - */ - lsr &= up->port.read_status_mask; - -#ifdef CONFIG_SERIAL_OMAP_CONSOLE - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - lsr |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } -#endif - if (lsr & UART_LSR_BI) - flag = TTY_BREAK; - else if (lsr & UART_LSR_PE) - flag = TTY_PARITY; - else if (lsr & UART_LSR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); -ignore_char: - lsr = serial_in(up, UART_LSR); - } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); - spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&up->port.lock); -} - -static void transmit_chars(struct uart_omap_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_out(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - serial_omap_stop_tx(&up->port); - return; - } - count = up->port.fifosize / 4; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) - serial_omap_stop_tx(&up->port); -} - -static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) -{ - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial_omap_start_tx(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - struct circ_buf *xmit; - unsigned int start; - int ret = 0; - - if (!up->use_dma) { - serial_omap_enable_ier_thri(up); - return; - } - - if (up->uart_dma.tx_dma_used) - return; - - xmit = &up->port.state->xmit; - - if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) { - ret = omap_request_dma(up->uart_dma.uart_dma_tx, - "UART Tx DMA", - (void *)uart_tx_dma_callback, up, - &(up->uart_dma.tx_dma_channel)); - - if (ret < 0) { - serial_omap_enable_ier_thri(up); - return; - } - } - spin_lock(&(up->uart_dma.tx_lock)); - up->uart_dma.tx_dma_used = true; - spin_unlock(&(up->uart_dma.tx_lock)); - - start = up->uart_dma.tx_buf_dma_phys + - (xmit->tail & (UART_XMIT_SIZE - 1)); - - up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); - /* - * It is a circular buffer. See if the buffer has wounded back. - * If yes it will have to be transferred in two separate dma - * transfers - */ - if (start + up->uart_dma.tx_buf_size >= - up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - up->uart_dma.tx_buf_size = - (up->uart_dma.tx_buf_dma_phys + - UART_XMIT_SIZE) - start; - - omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - up->uart_dma.uart_base, 0, 0); - omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_POST_INC, start, 0, 0); - omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, - OMAP_DMA_DATA_TYPE_S8, - up->uart_dma.tx_buf_size, 1, - OMAP_DMA_SYNC_ELEMENT, - up->uart_dma.uart_dma_tx, 0); - /* FIXME: Cache maintenance needed here? */ - omap_start_dma(up->uart_dma.tx_dma_channel); -} - -static unsigned int check_modem_status(struct uart_omap_port *up) -{ - unsigned int status; - - status = serial_in(up, UART_MSR); - status |= up->msr_saved_flags; - up->msr_saved_flags = 0; - if ((status & UART_MSR_ANY_DELTA) == 0) - return status; - - if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && - up->port.state != NULL) { - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - if (status & UART_MSR_DDCD) - uart_handle_dcd_change - (&up->port, status & UART_MSR_DCD); - if (status & UART_MSR_DCTS) - uart_handle_cts_change - (&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.state->port.delta_msr_wait); - } - - return status; -} - -/** - * serial_omap_irq() - This handles the interrupt from one port - * @irq: uart port irq number - * @dev_id: uart port info - */ -static inline irqreturn_t serial_omap_irq(int irq, void *dev_id) -{ - struct uart_omap_port *up = dev_id; - unsigned int iir, lsr; - unsigned long flags; - - iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) - return IRQ_NONE; - - spin_lock_irqsave(&up->port.lock, flags); - lsr = serial_in(up, UART_LSR); - if (iir & UART_IIR_RLSI) { - if (!up->use_dma) { - if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); - } else { - up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); - serial_out(up, UART_IER, up->ier); - if ((serial_omap_start_rxdma(up) != 0) && - (lsr & UART_LSR_DR)) - receive_chars(up, &lsr); - } - } - - check_modem_status(up); - if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI)) - transmit_chars(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - up->port_activity = jiffies; - return IRQ_HANDLED; -} - -static unsigned int serial_omap_tx_empty(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned long flags = 0; - unsigned int ret = 0; - - dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id); - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int serial_omap_get_mctrl(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned char status; - unsigned int ret = 0; - - status = check_modem_status(up); - dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id); - - 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 serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned char mcr = 0; - - dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->pdev->id); - 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; - - mcr |= up->mcr; - serial_out(up, UART_MCR, mcr); -} - -static void serial_omap_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned long flags = 0; - - dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id); - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int serial_omap_startup(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned long flags = 0; - int retval; - - /* - * Allocate the IRQ - */ - retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags, - up->name, up); - if (retval) - return retval; - - dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id); - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - serial_omap_clear_fifos(up); - /* For Hardware flow control */ - serial_out(up, UART_MCR, UART_MCR_RTS); - - /* - * Clear the interrupt registers. - */ - (void) serial_in(up, UART_LSR); - if (serial_in(up, UART_LSR) & UART_LSR_DR) - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - /* - * Now, initialize the UART - */ - serial_out(up, UART_LCR, UART_LCR_WLEN8); - spin_lock_irqsave(&up->port.lock, flags); - /* - * Most PC uarts need OUT2 raised to enable interrupts. - */ - up->port.mctrl |= TIOCM_OUT2; - serial_omap_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - up->msr_saved_flags = 0; - if (up->use_dma) { - free_page((unsigned long)up->port.state->xmit.buf); - up->port.state->xmit.buf = dma_alloc_coherent(NULL, - UART_XMIT_SIZE, - (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys), - 0); - init_timer(&(up->uart_dma.rx_timer)); - up->uart_dma.rx_timer.function = serial_omap_rx_timeout; - up->uart_dma.rx_timer.data = up->pdev->id; - /* Currently the buffer size is 4KB. Can increase it */ - up->uart_dma.rx_buf = dma_alloc_coherent(NULL, - up->uart_dma.rx_buf_size, - (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0); - } - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_out(up, UART_IER, up->ier); - - up->port_activity = jiffies; - return 0; -} - -static void serial_omap_shutdown(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned long flags = 0; - - dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id); - /* - * Disable interrupts from this port - */ - up->ier = 0; - serial_out(up, UART_IER, 0); - - spin_lock_irqsave(&up->port.lock, flags); - up->port.mctrl &= ~TIOCM_OUT2; - serial_omap_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition and FIFOs - */ - serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); - serial_omap_clear_fifos(up); - - /* - * Read data port to reset things, and then free the irq - */ - if (serial_in(up, UART_LSR) & UART_LSR_DR) - (void) serial_in(up, UART_RX); - if (up->use_dma) { - dma_free_coherent(up->port.dev, - UART_XMIT_SIZE, up->port.state->xmit.buf, - up->uart_dma.tx_buf_dma_phys); - up->port.state->xmit.buf = NULL; - serial_omap_stop_rx(port); - dma_free_coherent(up->port.dev, - up->uart_dma.rx_buf_size, up->uart_dma.rx_buf, - up->uart_dma.rx_buf_dma_phys); - up->uart_dma.rx_buf = NULL; - } - free_irq(up->port.irq, up); -} - -static inline void -serial_omap_configure_xonxoff - (struct uart_omap_port *up, struct ktermios *termios) -{ - unsigned char efr = 0; - - up->lcr = serial_in(up, UART_LCR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - up->efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); - - serial_out(up, UART_XON1, termios->c_cc[VSTART]); - serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); - - /* clear SW control mode bits */ - efr = up->efr; - efr &= OMAP_UART_SW_CLR; - - /* - * IXON Flag: - * Enable XON/XOFF flow control on output. - * Transmit XON1, XOFF1 - */ - if (termios->c_iflag & IXON) - efr |= OMAP_UART_SW_TX; - - /* - * IXOFF Flag: - * Enable XON/XOFF flow control on input. - * Receiver compares XON1, XOFF1. - */ - if (termios->c_iflag & IXOFF) - efr |= OMAP_UART_SW_RX; - - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - - up->mcr = serial_in(up, UART_MCR); - - /* - * IXANY Flag: - * Enable any character to restart output. - * Operation resumes after receiving any - * character after recognition of the XOFF character - */ - if (termios->c_iflag & IXANY) - up->mcr |= UART_MCR_XONANY; - - 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, efr | UART_EFR_SCD); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - - serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); - serial_out(up, UART_LCR, up->lcr); -} - -static void -serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned char cval = 0; - unsigned char efr = 0; - unsigned long flags = 0; - unsigned int baud, quot; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; - - /* - * Ask the core to calculate the divisor for us. - */ - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13); - quot = serial_omap_get_divisor(port, baud); - - up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 | - UART_FCR_ENABLE_FIFO; - if (up->use_dma) - up->fcr |= UART_FCR_DMA_SELECT; - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characters to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * Modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); - serial_out(up, UART_LCR, cval); /* reset DLAB */ - - /* FIFOs and DMA Settings */ - - /* FCR can be changed only when the - * baud clock is not running - * DLL_REG and DLH_REG set to 0. - */ - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_DLL, 0); - serial_out(up, UART_DLM, 0); - serial_out(up, UART_LCR, 0); - - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - - up->efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - up->mcr = serial_in(up, UART_MCR); - serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); - /* FIFO ENABLE, DMA MODE */ - serial_out(up, UART_FCR, up->fcr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - - if (up->use_dma) { - serial_out(up, UART_TI752_TLR, 0); - serial_out(up, UART_OMAP_SCR, - (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8)); - } - - serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr); - - /* Protocol, Baud Rate, and Interrupt Settings */ - - serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - - up->efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - - serial_out(up, UART_LCR, 0); - serial_out(up, UART_IER, 0); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - - serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ - - serial_out(up, UART_LCR, 0); - serial_out(up, UART_IER, up->ier); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - - serial_out(up, UART_EFR, up->efr); - serial_out(up, UART_LCR, cval); - - if (baud > 230400 && baud != 3000000) - serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_13X_MODE); - else - serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_16X_MODE); - - /* Hardware Flow Control Configuration */ - - if (termios->c_cflag & CRTSCTS) { - efr |= (UART_EFR_CTS | UART_EFR_RTS); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - - up->mcr = serial_in(up, UART_MCR); - serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); - - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - up->efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); - - serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); - serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */ - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); - serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); - serial_out(up, UART_LCR, cval); - } - - serial_omap_set_mctrl(&up->port, up->port.mctrl); - /* Software Flow Control Configuration */ - if (termios->c_iflag & (IXON | IXOFF)) - serial_omap_configure_xonxoff(up, termios); - - spin_unlock_irqrestore(&up->port.lock, flags); - dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id); -} - -static void -serial_omap_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned char efr; - - dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - efr = serial_in(up, UART_EFR); - serial_out(up, UART_EFR, efr | UART_EFR_ECB); - serial_out(up, UART_LCR, 0); - - serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - serial_out(up, UART_EFR, efr); - serial_out(up, UART_LCR, 0); - /* Enable module level wake up */ - serial_out(up, UART_OMAP_WER, - (state != 0) ? OMAP_UART_WER_MOD_WKUP : 0); -} - -static void serial_omap_release_port(struct uart_port *port) -{ - dev_dbg(port->dev, "serial_omap_release_port+\n"); -} - -static int serial_omap_request_port(struct uart_port *port) -{ - dev_dbg(port->dev, "serial_omap_request_port+\n"); - return 0; -} - -static void serial_omap_config_port(struct uart_port *port, int flags) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - - dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", - up->pdev->id); - up->port.type = PORT_OMAP; -} - -static int -serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* we don't want the core code to modify any port params */ - dev_dbg(port->dev, "serial_omap_verify_port+\n"); - return -EINVAL; -} - -static const char * -serial_omap_type(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - - dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->pdev->id); - return up->name; -} - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static inline void wait_for_xmitr(struct uart_omap_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while ((status & BOTH_EMPTY) != BOTH_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - for (tmout = 1000000; tmout; tmout--) { - unsigned int msr = serial_in(up, UART_MSR); - - up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; - if (msr & UART_MSR_CTS) - break; - - udelay(1); - } - } -} - -#ifdef CONFIG_CONSOLE_POLL - -static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -static int serial_omap_poll_get_char(struct uart_port *port) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - unsigned int status = serial_in(up, UART_LSR); - - if (!(status & UART_LSR_DR)) - return NO_POLL_CHAR; - - return serial_in(up, UART_RX); -} - -#endif /* CONFIG_CONSOLE_POLL */ - -#ifdef CONFIG_SERIAL_OMAP_CONSOLE - -static struct uart_omap_port *serial_omap_console_ports[4]; - -static struct uart_driver serial_omap_reg; - -static void serial_omap_console_putchar(struct uart_port *port, int ch) -{ - struct uart_omap_port *up = (struct uart_omap_port *)port; - - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -static void -serial_omap_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_omap_port *up = serial_omap_console_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = spin_trylock(&up->port.lock); - else - spin_lock(&up->port.lock); - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, serial_omap_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - serial_out(up, UART_IER, ier); - /* - * The receive handling will happen properly because the - * receive ready bit will still be set; it is not cleared - * on read. However, modem control will not, we must - * call it if we have saved something in the saved flags - * while processing with interrupts off. - */ - if (up->msr_saved_flags) - check_modem_status(up); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static int __init -serial_omap_console_setup(struct console *co, char *options) -{ - struct uart_omap_port *up; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (serial_omap_console_ports[co->index] == NULL) - return -ENODEV; - up = serial_omap_console_ports[co->index]; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&up->port, co, baud, parity, bits, flow); -} - -static struct console serial_omap_console = { - .name = OMAP_SERIAL_NAME, - .write = serial_omap_console_write, - .device = uart_console_device, - .setup = serial_omap_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial_omap_reg, -}; - -static void serial_omap_add_console_port(struct uart_omap_port *up) -{ - serial_omap_console_ports[up->pdev->id] = up; -} - -#define OMAP_CONSOLE (&serial_omap_console) - -#else - -#define OMAP_CONSOLE NULL - -static inline void serial_omap_add_console_port(struct uart_omap_port *up) -{} - -#endif - -static struct uart_ops serial_omap_pops = { - .tx_empty = serial_omap_tx_empty, - .set_mctrl = serial_omap_set_mctrl, - .get_mctrl = serial_omap_get_mctrl, - .stop_tx = serial_omap_stop_tx, - .start_tx = serial_omap_start_tx, - .stop_rx = serial_omap_stop_rx, - .enable_ms = serial_omap_enable_ms, - .break_ctl = serial_omap_break_ctl, - .startup = serial_omap_startup, - .shutdown = serial_omap_shutdown, - .set_termios = serial_omap_set_termios, - .pm = serial_omap_pm, - .type = serial_omap_type, - .release_port = serial_omap_release_port, - .request_port = serial_omap_request_port, - .config_port = serial_omap_config_port, - .verify_port = serial_omap_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_put_char = serial_omap_poll_put_char, - .poll_get_char = serial_omap_poll_get_char, -#endif -}; - -static struct uart_driver serial_omap_reg = { - .owner = THIS_MODULE, - .driver_name = "OMAP-SERIAL", - .dev_name = OMAP_SERIAL_NAME, - .nr = OMAP_MAX_HSUART_PORTS, - .cons = OMAP_CONSOLE, -}; - -static int -serial_omap_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct uart_omap_port *up = platform_get_drvdata(pdev); - - if (up) - uart_suspend_port(&serial_omap_reg, &up->port); - return 0; -} - -static int serial_omap_resume(struct platform_device *dev) -{ - struct uart_omap_port *up = platform_get_drvdata(dev); - - if (up) - uart_resume_port(&serial_omap_reg, &up->port); - return 0; -} - -static void serial_omap_rx_timeout(unsigned long uart_no) -{ - struct uart_omap_port *up = ui[uart_no]; - unsigned int curr_dma_pos, curr_transmitted_size; - int ret = 0; - - curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel); - if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) || - (curr_dma_pos == 0)) { - if (jiffies_to_msecs(jiffies - up->port_activity) < - RX_TIMEOUT) { - mod_timer(&up->uart_dma.rx_timer, jiffies + - usecs_to_jiffies(up->uart_dma.rx_timeout)); - } else { - serial_omap_stop_rxdma(up); - up->ier |= (UART_IER_RDI | UART_IER_RLSI); - serial_out(up, UART_IER, up->ier); - } - return; - } - - curr_transmitted_size = curr_dma_pos - - up->uart_dma.prev_rx_dma_pos; - up->port.icount.rx += curr_transmitted_size; - tty_insert_flip_string(up->port.state->port.tty, - up->uart_dma.rx_buf + - (up->uart_dma.prev_rx_dma_pos - - up->uart_dma.rx_buf_dma_phys), - curr_transmitted_size); - tty_flip_buffer_push(up->port.state->port.tty); - up->uart_dma.prev_rx_dma_pos = curr_dma_pos; - if (up->uart_dma.rx_buf_size + - up->uart_dma.rx_buf_dma_phys == curr_dma_pos) { - ret = serial_omap_start_rxdma(up); - if (ret < 0) { - serial_omap_stop_rxdma(up); - up->ier |= (UART_IER_RDI | UART_IER_RLSI); - serial_out(up, UART_IER, up->ier); - } - } else { - mod_timer(&up->uart_dma.rx_timer, jiffies + - usecs_to_jiffies(up->uart_dma.rx_timeout)); - } - up->port_activity = jiffies; -} - -static void uart_rx_dma_callback(int lch, u16 ch_status, void *data) -{ - return; -} - -static int serial_omap_start_rxdma(struct uart_omap_port *up) -{ - int ret = 0; - - if (up->uart_dma.rx_dma_channel == -1) { - ret = omap_request_dma(up->uart_dma.uart_dma_rx, - "UART Rx DMA", - (void *)uart_rx_dma_callback, up, - &(up->uart_dma.rx_dma_channel)); - if (ret < 0) - return ret; - - omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - up->uart_dma.uart_base, 0, 0); - omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0, - OMAP_DMA_AMODE_POST_INC, - up->uart_dma.rx_buf_dma_phys, 0, 0); - omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel, - OMAP_DMA_DATA_TYPE_S8, - up->uart_dma.rx_buf_size, 1, - OMAP_DMA_SYNC_ELEMENT, - up->uart_dma.uart_dma_rx, 0); - } - up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys; - /* FIXME: Cache maintenance needed here? */ - omap_start_dma(up->uart_dma.rx_dma_channel); - mod_timer(&up->uart_dma.rx_timer, jiffies + - usecs_to_jiffies(up->uart_dma.rx_timeout)); - up->uart_dma.rx_dma_used = true; - return ret; -} - -static void serial_omap_continue_tx(struct uart_omap_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - unsigned int start = up->uart_dma.tx_buf_dma_phys - + (xmit->tail & (UART_XMIT_SIZE - 1)); - - if (uart_circ_empty(xmit)) - return; - - up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); - /* - * It is a circular buffer. See if the buffer has wounded back. - * If yes it will have to be transferred in two separate dma - * transfers - */ - if (start + up->uart_dma.tx_buf_size >= - up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - up->uart_dma.tx_buf_size = - (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start; - omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_CONSTANT, - up->uart_dma.uart_base, 0, 0); - omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, - OMAP_DMA_AMODE_POST_INC, start, 0, 0); - omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, - OMAP_DMA_DATA_TYPE_S8, - up->uart_dma.tx_buf_size, 1, - OMAP_DMA_SYNC_ELEMENT, - up->uart_dma.uart_dma_tx, 0); - /* FIXME: Cache maintenance needed here? */ - omap_start_dma(up->uart_dma.tx_dma_channel); -} - -static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) -{ - struct uart_omap_port *up = (struct uart_omap_port *)data; - struct circ_buf *xmit = &up->port.state->xmit; - - xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \ - (UART_XMIT_SIZE - 1); - up->port.icount.tx += up->uart_dma.tx_buf_size; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) { - spin_lock(&(up->uart_dma.tx_lock)); - serial_omap_stop_tx(&up->port); - up->uart_dma.tx_dma_used = false; - spin_unlock(&(up->uart_dma.tx_lock)); - } else { - omap_stop_dma(up->uart_dma.tx_dma_channel); - serial_omap_continue_tx(up); - } - up->port_activity = jiffies; - return; -} - -static int serial_omap_probe(struct platform_device *pdev) -{ - struct uart_omap_port *up; - struct resource *mem, *irq, *dma_tx, *dma_rx; - struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; - int ret = -ENOSPC; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - - irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq) { - dev_err(&pdev->dev, "no irq resource?\n"); - return -ENODEV; - } - - if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, - pdev->dev.driver->name)) { - dev_err(&pdev->dev, "memory region already claimed\n"); - return -EBUSY; - } - - dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (!dma_rx) { - ret = -EINVAL; - goto err; - } - - dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (!dma_tx) { - ret = -EINVAL; - goto err; - } - - up = kzalloc(sizeof(*up), GFP_KERNEL); - if (up == NULL) { - ret = -ENOMEM; - goto do_release_region; - } - sprintf(up->name, "OMAP UART%d", pdev->id); - up->pdev = pdev; - up->port.dev = &pdev->dev; - up->port.type = PORT_OMAP; - up->port.iotype = UPIO_MEM; - up->port.irq = irq->start; - - up->port.regshift = 2; - up->port.fifosize = 64; - up->port.ops = &serial_omap_pops; - up->port.line = pdev->id; - - up->port.membase = omap_up_info->membase; - up->port.mapbase = omap_up_info->mapbase; - up->port.flags = omap_up_info->flags; - up->port.irqflags = omap_up_info->irqflags; - up->port.uartclk = omap_up_info->uartclk; - up->uart_dma.uart_base = mem->start; - - if (omap_up_info->dma_enabled) { - up->uart_dma.uart_dma_tx = dma_tx->start; - up->uart_dma.uart_dma_rx = dma_rx->start; - up->use_dma = 1; - up->uart_dma.rx_buf_size = 4096; - up->uart_dma.rx_timeout = 2; - spin_lock_init(&(up->uart_dma.tx_lock)); - spin_lock_init(&(up->uart_dma.rx_lock)); - up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; - up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; - } - - ui[pdev->id] = up; - serial_omap_add_console_port(up); - - ret = uart_add_one_port(&serial_omap_reg, &up->port); - if (ret != 0) - goto do_release_region; - - platform_set_drvdata(pdev, up); - return 0; -err: - dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", - pdev->id, __func__, ret); -do_release_region: - release_mem_region(mem->start, (mem->end - mem->start) + 1); - return ret; -} - -static int serial_omap_remove(struct platform_device *dev) -{ - struct uart_omap_port *up = platform_get_drvdata(dev); - - platform_set_drvdata(dev, NULL); - if (up) { - uart_remove_one_port(&serial_omap_reg, &up->port); - kfree(up); - } - return 0; -} - -static struct platform_driver serial_omap_driver = { - .probe = serial_omap_probe, - .remove = serial_omap_remove, - - .suspend = serial_omap_suspend, - .resume = serial_omap_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -static int __init serial_omap_init(void) -{ - int ret; - - ret = uart_register_driver(&serial_omap_reg); - if (ret != 0) - return ret; - ret = platform_driver_register(&serial_omap_driver); - if (ret != 0) - uart_unregister_driver(&serial_omap_reg); - return ret; -} - -static void __exit serial_omap_exit(void) -{ - platform_driver_unregister(&serial_omap_driver); - uart_unregister_driver(&serial_omap_reg); -} - -module_init(serial_omap_init); -module_exit(serial_omap_exit); - -MODULE_DESCRIPTION("OMAP High Speed UART driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/serial/pch_uart.c b/drivers/serial/pch_uart.c deleted file mode 100644 index 70a61458ec42..000000000000 --- a/drivers/serial/pch_uart.c +++ /dev/null @@ -1,1451 +0,0 @@ -/* - *Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. - * - *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; version 2 of the License. - * - *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 - -enum { - PCH_UART_HANDLED_RX_INT_SHIFT, - PCH_UART_HANDLED_TX_INT_SHIFT, - PCH_UART_HANDLED_RX_ERR_INT_SHIFT, - PCH_UART_HANDLED_RX_TRG_INT_SHIFT, - PCH_UART_HANDLED_MS_INT_SHIFT, -}; - -enum { - PCH_UART_8LINE, - PCH_UART_2LINE, -}; - -#define PCH_UART_DRIVER_DEVICE "ttyPCH" - -#define PCH_UART_NR_GE_256FIFO 1 -#define PCH_UART_NR_GE_64FIFO 3 -#define PCH_UART_NR_GE (PCH_UART_NR_GE_256FIFO+PCH_UART_NR_GE_64FIFO) -#define PCH_UART_NR PCH_UART_NR_GE - -#define PCH_UART_HANDLED_RX_INT (1<<((PCH_UART_HANDLED_RX_INT_SHIFT)<<1)) -#define PCH_UART_HANDLED_TX_INT (1<<((PCH_UART_HANDLED_TX_INT_SHIFT)<<1)) -#define PCH_UART_HANDLED_RX_ERR_INT (1<<((\ - PCH_UART_HANDLED_RX_ERR_INT_SHIFT)<<1)) -#define PCH_UART_HANDLED_RX_TRG_INT (1<<((\ - PCH_UART_HANDLED_RX_TRG_INT_SHIFT)<<1)) -#define PCH_UART_HANDLED_MS_INT (1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1)) - -#define PCH_UART_RBR 0x00 -#define PCH_UART_THR 0x00 - -#define PCH_UART_IER_MASK (PCH_UART_IER_ERBFI|PCH_UART_IER_ETBEI|\ - PCH_UART_IER_ELSI|PCH_UART_IER_EDSSI) -#define PCH_UART_IER_ERBFI 0x00000001 -#define PCH_UART_IER_ETBEI 0x00000002 -#define PCH_UART_IER_ELSI 0x00000004 -#define PCH_UART_IER_EDSSI 0x00000008 - -#define PCH_UART_IIR_IP 0x00000001 -#define PCH_UART_IIR_IID 0x00000006 -#define PCH_UART_IIR_MSI 0x00000000 -#define PCH_UART_IIR_TRI 0x00000002 -#define PCH_UART_IIR_RRI 0x00000004 -#define PCH_UART_IIR_REI 0x00000006 -#define PCH_UART_IIR_TOI 0x00000008 -#define PCH_UART_IIR_FIFO256 0x00000020 -#define PCH_UART_IIR_FIFO64 PCH_UART_IIR_FIFO256 -#define PCH_UART_IIR_FE 0x000000C0 - -#define PCH_UART_FCR_FIFOE 0x00000001 -#define PCH_UART_FCR_RFR 0x00000002 -#define PCH_UART_FCR_TFR 0x00000004 -#define PCH_UART_FCR_DMS 0x00000008 -#define PCH_UART_FCR_FIFO256 0x00000020 -#define PCH_UART_FCR_RFTL 0x000000C0 - -#define PCH_UART_FCR_RFTL1 0x00000000 -#define PCH_UART_FCR_RFTL64 0x00000040 -#define PCH_UART_FCR_RFTL128 0x00000080 -#define PCH_UART_FCR_RFTL224 0x000000C0 -#define PCH_UART_FCR_RFTL16 PCH_UART_FCR_RFTL64 -#define PCH_UART_FCR_RFTL32 PCH_UART_FCR_RFTL128 -#define PCH_UART_FCR_RFTL56 PCH_UART_FCR_RFTL224 -#define PCH_UART_FCR_RFTL4 PCH_UART_FCR_RFTL64 -#define PCH_UART_FCR_RFTL8 PCH_UART_FCR_RFTL128 -#define PCH_UART_FCR_RFTL14 PCH_UART_FCR_RFTL224 -#define PCH_UART_FCR_RFTL_SHIFT 6 - -#define PCH_UART_LCR_WLS 0x00000003 -#define PCH_UART_LCR_STB 0x00000004 -#define PCH_UART_LCR_PEN 0x00000008 -#define PCH_UART_LCR_EPS 0x00000010 -#define PCH_UART_LCR_SP 0x00000020 -#define PCH_UART_LCR_SB 0x00000040 -#define PCH_UART_LCR_DLAB 0x00000080 -#define PCH_UART_LCR_NP 0x00000000 -#define PCH_UART_LCR_OP PCH_UART_LCR_PEN -#define PCH_UART_LCR_EP (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS) -#define PCH_UART_LCR_1P (PCH_UART_LCR_PEN | PCH_UART_LCR_SP) -#define PCH_UART_LCR_0P (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS |\ - PCH_UART_LCR_SP) - -#define PCH_UART_LCR_5BIT 0x00000000 -#define PCH_UART_LCR_6BIT 0x00000001 -#define PCH_UART_LCR_7BIT 0x00000002 -#define PCH_UART_LCR_8BIT 0x00000003 - -#define PCH_UART_MCR_DTR 0x00000001 -#define PCH_UART_MCR_RTS 0x00000002 -#define PCH_UART_MCR_OUT 0x0000000C -#define PCH_UART_MCR_LOOP 0x00000010 -#define PCH_UART_MCR_AFE 0x00000020 - -#define PCH_UART_LSR_DR 0x00000001 -#define PCH_UART_LSR_ERR (1<<7) - -#define PCH_UART_MSR_DCTS 0x00000001 -#define PCH_UART_MSR_DDSR 0x00000002 -#define PCH_UART_MSR_TERI 0x00000004 -#define PCH_UART_MSR_DDCD 0x00000008 -#define PCH_UART_MSR_CTS 0x00000010 -#define PCH_UART_MSR_DSR 0x00000020 -#define PCH_UART_MSR_RI 0x00000040 -#define PCH_UART_MSR_DCD 0x00000080 -#define PCH_UART_MSR_DELTA (PCH_UART_MSR_DCTS | PCH_UART_MSR_DDSR |\ - PCH_UART_MSR_TERI | PCH_UART_MSR_DDCD) - -#define PCH_UART_DLL 0x00 -#define PCH_UART_DLM 0x01 - -#define DIV_ROUND(a, b) (((a) + ((b)/2)) / (b)) - -#define PCH_UART_IID_RLS (PCH_UART_IIR_REI) -#define PCH_UART_IID_RDR (PCH_UART_IIR_RRI) -#define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI) -#define PCH_UART_IID_THRE (PCH_UART_IIR_TRI) -#define PCH_UART_IID_MS (PCH_UART_IIR_MSI) - -#define PCH_UART_HAL_PARITY_NONE (PCH_UART_LCR_NP) -#define PCH_UART_HAL_PARITY_ODD (PCH_UART_LCR_OP) -#define PCH_UART_HAL_PARITY_EVEN (PCH_UART_LCR_EP) -#define PCH_UART_HAL_PARITY_FIX1 (PCH_UART_LCR_1P) -#define PCH_UART_HAL_PARITY_FIX0 (PCH_UART_LCR_0P) -#define PCH_UART_HAL_5BIT (PCH_UART_LCR_5BIT) -#define PCH_UART_HAL_6BIT (PCH_UART_LCR_6BIT) -#define PCH_UART_HAL_7BIT (PCH_UART_LCR_7BIT) -#define PCH_UART_HAL_8BIT (PCH_UART_LCR_8BIT) -#define PCH_UART_HAL_STB1 0 -#define PCH_UART_HAL_STB2 (PCH_UART_LCR_STB) - -#define PCH_UART_HAL_CLR_TX_FIFO (PCH_UART_FCR_TFR) -#define PCH_UART_HAL_CLR_RX_FIFO (PCH_UART_FCR_RFR) -#define PCH_UART_HAL_CLR_ALL_FIFO (PCH_UART_HAL_CLR_TX_FIFO | \ - PCH_UART_HAL_CLR_RX_FIFO) - -#define PCH_UART_HAL_DMA_MODE0 0 -#define PCH_UART_HAL_FIFO_DIS 0 -#define PCH_UART_HAL_FIFO16 (PCH_UART_FCR_FIFOE) -#define PCH_UART_HAL_FIFO256 (PCH_UART_FCR_FIFOE | \ - PCH_UART_FCR_FIFO256) -#define PCH_UART_HAL_FIFO64 (PCH_UART_HAL_FIFO256) -#define PCH_UART_HAL_TRIGGER1 (PCH_UART_FCR_RFTL1) -#define PCH_UART_HAL_TRIGGER64 (PCH_UART_FCR_RFTL64) -#define PCH_UART_HAL_TRIGGER128 (PCH_UART_FCR_RFTL128) -#define PCH_UART_HAL_TRIGGER224 (PCH_UART_FCR_RFTL224) -#define PCH_UART_HAL_TRIGGER16 (PCH_UART_FCR_RFTL16) -#define PCH_UART_HAL_TRIGGER32 (PCH_UART_FCR_RFTL32) -#define PCH_UART_HAL_TRIGGER56 (PCH_UART_FCR_RFTL56) -#define PCH_UART_HAL_TRIGGER4 (PCH_UART_FCR_RFTL4) -#define PCH_UART_HAL_TRIGGER8 (PCH_UART_FCR_RFTL8) -#define PCH_UART_HAL_TRIGGER14 (PCH_UART_FCR_RFTL14) -#define PCH_UART_HAL_TRIGGER_L (PCH_UART_FCR_RFTL64) -#define PCH_UART_HAL_TRIGGER_M (PCH_UART_FCR_RFTL128) -#define PCH_UART_HAL_TRIGGER_H (PCH_UART_FCR_RFTL224) - -#define PCH_UART_HAL_RX_INT (PCH_UART_IER_ERBFI) -#define PCH_UART_HAL_TX_INT (PCH_UART_IER_ETBEI) -#define PCH_UART_HAL_RX_ERR_INT (PCH_UART_IER_ELSI) -#define PCH_UART_HAL_MS_INT (PCH_UART_IER_EDSSI) -#define PCH_UART_HAL_ALL_INT (PCH_UART_IER_MASK) - -#define PCH_UART_HAL_DTR (PCH_UART_MCR_DTR) -#define PCH_UART_HAL_RTS (PCH_UART_MCR_RTS) -#define PCH_UART_HAL_OUT (PCH_UART_MCR_OUT) -#define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP) -#define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE) - -struct pch_uart_buffer { - unsigned char *buf; - int size; -}; - -struct eg20t_port { - struct uart_port port; - int port_type; - void __iomem *membase; - resource_size_t mapbase; - unsigned int iobase; - struct pci_dev *pdev; - int fifo_size; - int base_baud; - int start_tx; - int start_rx; - int tx_empty; - int int_dis_flag; - int trigger; - int trigger_level; - struct pch_uart_buffer rxbuf; - unsigned int dmsr; - unsigned int fcr; - unsigned int use_dma; - unsigned int use_dma_flag; - struct dma_async_tx_descriptor *desc_tx; - struct dma_async_tx_descriptor *desc_rx; - struct pch_dma_slave param_tx; - struct pch_dma_slave param_rx; - struct dma_chan *chan_tx; - struct dma_chan *chan_rx; - struct scatterlist sg_tx; - struct scatterlist sg_rx; - int tx_dma_use; - void *rx_buf_virt; - dma_addr_t rx_buf_dma; -}; - -static unsigned int default_baud = 9600; -static const int trigger_level_256[4] = { 1, 64, 128, 224 }; -static const int trigger_level_64[4] = { 1, 16, 32, 56 }; -static const int trigger_level_16[4] = { 1, 4, 8, 14 }; -static const int trigger_level_1[4] = { 1, 1, 1, 1 }; - -static void pch_uart_hal_request(struct pci_dev *pdev, int fifosize, - int base_baud) -{ - struct eg20t_port *priv = pci_get_drvdata(pdev); - - priv->trigger_level = 1; - priv->fcr = 0; -} - -static unsigned int get_msr(struct eg20t_port *priv, void __iomem *base) -{ - unsigned int msr = ioread8(base + UART_MSR); - priv->dmsr |= msr & PCH_UART_MSR_DELTA; - - return msr; -} - -static void pch_uart_hal_enable_interrupt(struct eg20t_port *priv, - unsigned int flag) -{ - u8 ier = ioread8(priv->membase + UART_IER); - ier |= flag & PCH_UART_IER_MASK; - iowrite8(ier, priv->membase + UART_IER); -} - -static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv, - unsigned int flag) -{ - u8 ier = ioread8(priv->membase + UART_IER); - ier &= ~(flag & PCH_UART_IER_MASK); - iowrite8(ier, priv->membase + UART_IER); -} - -static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, - unsigned int parity, unsigned int bits, - unsigned int stb) -{ - unsigned int dll, dlm, lcr; - int div; - - div = DIV_ROUND(priv->base_baud / 16, baud); - if (div < 0 || USHRT_MAX <= div) { - pr_err("Invalid Baud(div=0x%x)\n", div); - return -EINVAL; - } - - dll = (unsigned int)div & 0x00FFU; - dlm = ((unsigned int)div >> 8) & 0x00FFU; - - if (parity & ~(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS | PCH_UART_LCR_SP)) { - pr_err("Invalid parity(0x%x)\n", parity); - return -EINVAL; - } - - if (bits & ~PCH_UART_LCR_WLS) { - pr_err("Invalid bits(0x%x)\n", bits); - return -EINVAL; - } - - if (stb & ~PCH_UART_LCR_STB) { - pr_err("Invalid STB(0x%x)\n", stb); - return -EINVAL; - } - - lcr = parity; - lcr |= bits; - lcr |= stb; - - pr_debug("%s:baud = %d, div = %04x, lcr = %02x (%lu)\n", - __func__, baud, div, lcr, jiffies); - iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR); - iowrite8(dll, priv->membase + PCH_UART_DLL); - iowrite8(dlm, priv->membase + PCH_UART_DLM); - iowrite8(lcr, priv->membase + UART_LCR); - - return 0; -} - -static int pch_uart_hal_fifo_reset(struct eg20t_port *priv, - unsigned int flag) -{ - if (flag & ~(PCH_UART_FCR_TFR | PCH_UART_FCR_RFR)) { - pr_err("%s:Invalid flag(0x%x)\n", __func__, flag); - return -EINVAL; - } - - iowrite8(PCH_UART_FCR_FIFOE | priv->fcr, priv->membase + UART_FCR); - iowrite8(PCH_UART_FCR_FIFOE | priv->fcr | flag, - priv->membase + UART_FCR); - iowrite8(priv->fcr, priv->membase + UART_FCR); - - return 0; -} - -static int pch_uart_hal_set_fifo(struct eg20t_port *priv, - unsigned int dmamode, - unsigned int fifo_size, unsigned int trigger) -{ - u8 fcr; - - if (dmamode & ~PCH_UART_FCR_DMS) { - pr_err("%s:Invalid DMA Mode(0x%x)\n", __func__, dmamode); - return -EINVAL; - } - - if (fifo_size & ~(PCH_UART_FCR_FIFOE | PCH_UART_FCR_FIFO256)) { - pr_err("%s:Invalid FIFO SIZE(0x%x)\n", __func__, fifo_size); - return -EINVAL; - } - - if (trigger & ~PCH_UART_FCR_RFTL) { - pr_err("%s:Invalid TRIGGER(0x%x)\n", __func__, trigger); - return -EINVAL; - } - - switch (priv->fifo_size) { - case 256: - priv->trigger_level = - trigger_level_256[trigger >> PCH_UART_FCR_RFTL_SHIFT]; - break; - case 64: - priv->trigger_level = - trigger_level_64[trigger >> PCH_UART_FCR_RFTL_SHIFT]; - break; - case 16: - priv->trigger_level = - trigger_level_16[trigger >> PCH_UART_FCR_RFTL_SHIFT]; - break; - default: - priv->trigger_level = - trigger_level_1[trigger >> PCH_UART_FCR_RFTL_SHIFT]; - break; - } - fcr = - dmamode | fifo_size | trigger | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR; - iowrite8(PCH_UART_FCR_FIFOE, priv->membase + UART_FCR); - iowrite8(PCH_UART_FCR_FIFOE | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR, - priv->membase + UART_FCR); - iowrite8(fcr, priv->membase + UART_FCR); - priv->fcr = fcr; - - return 0; -} - -static u8 pch_uart_hal_get_modem(struct eg20t_port *priv) -{ - priv->dmsr = 0; - return get_msr(priv, priv->membase); -} - -static int pch_uart_hal_write(struct eg20t_port *priv, - const unsigned char *buf, int tx_size) -{ - int i; - unsigned int thr; - - for (i = 0; i < tx_size;) { - thr = buf[i++]; - iowrite8(thr, priv->membase + PCH_UART_THR); - } - return i; -} - -static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf, - int rx_size) -{ - int i; - u8 rbr, lsr; - - lsr = ioread8(priv->membase + UART_LSR); - for (i = 0, lsr = ioread8(priv->membase + UART_LSR); - i < rx_size && lsr & UART_LSR_DR; - lsr = ioread8(priv->membase + UART_LSR)) { - rbr = ioread8(priv->membase + PCH_UART_RBR); - buf[i++] = rbr; - } - return i; -} - -static unsigned int pch_uart_hal_get_iid(struct eg20t_port *priv) -{ - unsigned int iir; - int ret; - - iir = ioread8(priv->membase + UART_IIR); - ret = (iir & (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP)); - return ret; -} - -static u8 pch_uart_hal_get_line_status(struct eg20t_port *priv) -{ - return ioread8(priv->membase + UART_LSR); -} - -static void pch_uart_hal_set_break(struct eg20t_port *priv, int on) -{ - unsigned int lcr; - - lcr = ioread8(priv->membase + UART_LCR); - if (on) - lcr |= PCH_UART_LCR_SB; - else - lcr &= ~PCH_UART_LCR_SB; - - iowrite8(lcr, priv->membase + UART_LCR); -} - -static int push_rx(struct eg20t_port *priv, const unsigned char *buf, - int size) -{ - struct uart_port *port; - struct tty_struct *tty; - - port = &priv->port; - tty = tty_port_tty_get(&port->state->port); - if (!tty) { - pr_debug("%s:tty is busy now", __func__); - return -EBUSY; - } - - tty_insert_flip_string(tty, buf, size); - tty_flip_buffer_push(tty); - tty_kref_put(tty); - - return 0; -} - -static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf) -{ - int ret; - struct uart_port *port = &priv->port; - - if (port->x_char) { - pr_debug("%s:X character send %02x (%lu)\n", __func__, - port->x_char, jiffies); - buf[0] = port->x_char; - port->x_char = 0; - ret = 1; - } else { - ret = 0; - } - - return ret; -} - -static int dma_push_rx(struct eg20t_port *priv, int size) -{ - struct tty_struct *tty; - int room; - struct uart_port *port = &priv->port; - - port = &priv->port; - tty = tty_port_tty_get(&port->state->port); - if (!tty) { - pr_debug("%s:tty is busy now", __func__); - return 0; - } - - room = tty_buffer_request_room(tty, size); - - if (room < size) - dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", - size - room); - if (!room) - return room; - - tty_insert_flip_string(tty, sg_virt(&priv->sg_rx), size); - - port->icount.rx += room; - tty_kref_put(tty); - - return room; -} - -static void pch_free_dma(struct uart_port *port) -{ - struct eg20t_port *priv; - priv = container_of(port, struct eg20t_port, port); - - if (priv->chan_tx) { - dma_release_channel(priv->chan_tx); - priv->chan_tx = NULL; - } - if (priv->chan_rx) { - dma_release_channel(priv->chan_rx); - priv->chan_rx = NULL; - } - if (sg_dma_address(&priv->sg_rx)) - dma_free_coherent(port->dev, port->fifosize, - sg_virt(&priv->sg_rx), - sg_dma_address(&priv->sg_rx)); - - return; -} - -static bool filter(struct dma_chan *chan, void *slave) -{ - struct pch_dma_slave *param = slave; - - if ((chan->chan_id == param->chan_id) && (param->dma_dev == - chan->device->dev)) { - chan->private = param; - return true; - } else { - return false; - } -} - -static void pch_request_dma(struct uart_port *port) -{ - dma_cap_mask_t mask; - struct dma_chan *chan; - struct pci_dev *dma_dev; - struct pch_dma_slave *param; - struct eg20t_port *priv = - container_of(port, struct eg20t_port, port); - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0xa, 0)); /* Get DMA's dev - information */ - /* Set Tx DMA */ - param = &priv->param_tx; - param->dma_dev = &dma_dev->dev; - param->chan_id = priv->port.line; - param->tx_reg = port->mapbase + UART_TX; - chan = dma_request_channel(mask, filter, param); - if (!chan) { - pr_err("%s:dma_request_channel FAILS(Tx)\n", __func__); - return; - } - priv->chan_tx = chan; - - /* Set Rx DMA */ - param = &priv->param_rx; - param->dma_dev = &dma_dev->dev; - param->chan_id = priv->port.line + 1; /* Rx = Tx + 1 */ - param->rx_reg = port->mapbase + UART_RX; - chan = dma_request_channel(mask, filter, param); - if (!chan) { - pr_err("%s:dma_request_channel FAILS(Rx)\n", __func__); - dma_release_channel(priv->chan_tx); - return; - } - - /* Get Consistent memory for DMA */ - priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, - &priv->rx_buf_dma, GFP_KERNEL); - priv->chan_rx = chan; -} - -static void pch_dma_rx_complete(void *arg) -{ - struct eg20t_port *priv = arg; - struct uart_port *port = &priv->port; - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - - if (!tty) { - pr_debug("%s:tty is busy now", __func__); - return; - } - - if (dma_push_rx(priv, priv->trigger_level)) - tty_flip_buffer_push(tty); - - tty_kref_put(tty); -} - -static void pch_dma_tx_complete(void *arg) -{ - struct eg20t_port *priv = arg; - struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; - - xmit->tail += sg_dma_len(&priv->sg_tx); - xmit->tail &= UART_XMIT_SIZE - 1; - port->icount.tx += sg_dma_len(&priv->sg_tx); - - async_tx_ack(priv->desc_tx); - priv->tx_dma_use = 0; -} - -static int pop_tx(struct eg20t_port *priv, unsigned char *buf, int size) -{ - int count = 0; - struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; - - if (uart_tx_stopped(port) || uart_circ_empty(xmit) || count >= size) - goto pop_tx_end; - - do { - int cnt_to_end = - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - int sz = min(size - count, cnt_to_end); - memcpy(&buf[count], &xmit->buf[xmit->tail], sz); - xmit->tail = (xmit->tail + sz) & (UART_XMIT_SIZE - 1); - count += sz; - } while (!uart_circ_empty(xmit) && count < size); - -pop_tx_end: - pr_debug("%d characters. Remained %d characters. (%lu)\n", - count, size - count, jiffies); - - return count; -} - -static int handle_rx_to(struct eg20t_port *priv) -{ - struct pch_uart_buffer *buf; - int rx_size; - int ret; - if (!priv->start_rx) { - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); - return 0; - } - buf = &priv->rxbuf; - do { - rx_size = pch_uart_hal_read(priv, buf->buf, buf->size); - ret = push_rx(priv, buf->buf, rx_size); - if (ret) - return 0; - } while (rx_size == buf->size); - - return PCH_UART_HANDLED_RX_INT; -} - -static int handle_rx(struct eg20t_port *priv) -{ - return handle_rx_to(priv); -} - -static int dma_handle_rx(struct eg20t_port *priv) -{ - struct uart_port *port = &priv->port; - struct dma_async_tx_descriptor *desc; - struct scatterlist *sg; - - priv = container_of(port, struct eg20t_port, port); - sg = &priv->sg_rx; - - sg_init_table(&priv->sg_rx, 1); /* Initialize SG table */ - - sg_dma_len(sg) = priv->fifo_size; - - sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), - sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & - ~PAGE_MASK); - - sg_dma_address(sg) = priv->rx_buf_dma; - - desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, - sg, 1, DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT); - if (!desc) - return 0; - - priv->desc_rx = desc; - desc->callback = pch_dma_rx_complete; - desc->callback_param = priv; - desc->tx_submit(desc); - dma_async_issue_pending(priv->chan_rx); - - return PCH_UART_HANDLED_RX_INT; -} - -static unsigned int handle_tx(struct eg20t_port *priv) -{ - struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; - int ret; - int fifo_size; - int tx_size; - int size; - int tx_empty; - - if (!priv->start_tx) { - pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); - priv->tx_empty = 1; - return 0; - } - - fifo_size = max(priv->fifo_size, 1); - tx_empty = 1; - if (pop_tx_x(priv, xmit->buf)) { - pch_uart_hal_write(priv, xmit->buf, 1); - port->icount.tx++; - tx_empty = 0; - fifo_size--; - } - size = min(xmit->head - xmit->tail, fifo_size); - tx_size = pop_tx(priv, xmit->buf, size); - if (tx_size > 0) { - ret = pch_uart_hal_write(priv, xmit->buf, tx_size); - port->icount.tx += ret; - tx_empty = 0; - } - - priv->tx_empty = tx_empty; - - if (tx_empty) - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); - - return PCH_UART_HANDLED_TX_INT; -} - -static unsigned int dma_handle_tx(struct eg20t_port *priv) -{ - struct uart_port *port = &priv->port; - struct circ_buf *xmit = &port->state->xmit; - struct scatterlist *sg = &priv->sg_tx; - int nent; - int fifo_size; - int tx_empty; - struct dma_async_tx_descriptor *desc; - - if (!priv->start_tx) { - pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); - priv->tx_empty = 1; - return 0; - } - - fifo_size = max(priv->fifo_size, 1); - tx_empty = 1; - if (pop_tx_x(priv, xmit->buf)) { - pch_uart_hal_write(priv, xmit->buf, 1); - port->icount.tx++; - tx_empty = 0; - fifo_size--; - } - - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); - - priv->tx_dma_use = 1; - - sg_init_table(&priv->sg_tx, 1); /* Initialize SG table */ - - sg_set_page(&priv->sg_tx, virt_to_page(xmit->buf), - UART_XMIT_SIZE, (int)xmit->buf & ~PAGE_MASK); - - nent = dma_map_sg(port->dev, &priv->sg_tx, 1, DMA_TO_DEVICE); - if (!nent) { - pr_err("%s:dma_map_sg Failed\n", __func__); - return 0; - } - - sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + - sg->offset; - sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, - UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, - xmit->tail, UART_XMIT_SIZE)); - - desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, - sg, nent, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - pr_err("%s:device_prep_slave_sg Failed\n", __func__); - return 0; - } - - dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); - - priv->desc_tx = desc; - desc->callback = pch_dma_tx_complete; - desc->callback_param = priv; - - desc->tx_submit(desc); - - dma_async_issue_pending(priv->chan_tx); - - return PCH_UART_HANDLED_TX_INT; -} - -static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr) -{ - u8 fcr = ioread8(priv->membase + UART_FCR); - - /* Reset FIFO */ - fcr |= UART_FCR_CLEAR_RCVR; - iowrite8(fcr, priv->membase + UART_FCR); - - if (lsr & PCH_UART_LSR_ERR) - dev_err(&priv->pdev->dev, "Error data in FIFO\n"); - - if (lsr & UART_LSR_FE) - dev_err(&priv->pdev->dev, "Framing Error\n"); - - if (lsr & UART_LSR_PE) - dev_err(&priv->pdev->dev, "Parity Error\n"); - - if (lsr & UART_LSR_OE) - dev_err(&priv->pdev->dev, "Overrun Error\n"); -} - -static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) -{ - struct eg20t_port *priv = dev_id; - unsigned int handled; - u8 lsr; - int ret = 0; - unsigned int iid; - unsigned long flags; - - spin_lock_irqsave(&priv->port.lock, flags); - handled = 0; - while ((iid = pch_uart_hal_get_iid(priv)) > 1) { - switch (iid) { - case PCH_UART_IID_RLS: /* Receiver Line Status */ - lsr = pch_uart_hal_get_line_status(priv); - if (lsr & (PCH_UART_LSR_ERR | UART_LSR_FE | - UART_LSR_PE | UART_LSR_OE)) { - pch_uart_err_ir(priv, lsr); - ret = PCH_UART_HANDLED_RX_ERR_INT; - } - break; - case PCH_UART_IID_RDR: /* Received Data Ready */ - if (priv->use_dma) - ret = dma_handle_rx(priv); - else - ret = handle_rx(priv); - break; - case PCH_UART_IID_RDR_TO: /* Received Data Ready - (FIFO Timeout) */ - ret = handle_rx_to(priv); - break; - case PCH_UART_IID_THRE: /* Transmitter Holding Register - Empty */ - if (priv->use_dma) - ret = dma_handle_tx(priv); - else - ret = handle_tx(priv); - break; - case PCH_UART_IID_MS: /* Modem Status */ - ret = PCH_UART_HANDLED_MS_INT; - break; - default: /* Never junp to this label */ - pr_err("%s:iid=%d (%lu)\n", __func__, iid, jiffies); - ret = -1; - break; - } - handled |= (unsigned int)ret; - } - if (handled == 0 && iid <= 1) { - if (priv->int_dis_flag) - priv->int_dis_flag = 0; - } - - spin_unlock_irqrestore(&priv->port.lock, flags); - return IRQ_RETVAL(handled); -} - -/* This function tests whether the transmitter fifo and shifter for the port - described by 'port' is empty. */ -static unsigned int pch_uart_tx_empty(struct uart_port *port) -{ - struct eg20t_port *priv; - int ret; - priv = container_of(port, struct eg20t_port, port); - if (priv->tx_empty) - ret = TIOCSER_TEMT; - else - ret = 0; - - return ret; -} - -/* Returns the current state of modem control inputs. */ -static unsigned int pch_uart_get_mctrl(struct uart_port *port) -{ - struct eg20t_port *priv; - u8 modem; - unsigned int ret = 0; - - priv = container_of(port, struct eg20t_port, port); - modem = pch_uart_hal_get_modem(priv); - - if (modem & UART_MSR_DCD) - ret |= TIOCM_CAR; - - if (modem & UART_MSR_RI) - ret |= TIOCM_RNG; - - if (modem & UART_MSR_DSR) - ret |= TIOCM_DSR; - - if (modem & UART_MSR_CTS) - ret |= TIOCM_CTS; - - return ret; -} - -static void pch_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - u32 mcr = 0; - unsigned int dat; - struct eg20t_port *priv = container_of(port, struct eg20t_port, port); - - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - if (mctrl) { - dat = pch_uart_get_mctrl(port); - dat |= mcr; - iowrite8(dat, priv->membase + UART_MCR); - } -} - -static void pch_uart_stop_tx(struct uart_port *port) -{ - struct eg20t_port *priv; - priv = container_of(port, struct eg20t_port, port); - priv->start_tx = 0; - priv->tx_dma_use = 0; -} - -static void pch_uart_start_tx(struct uart_port *port) -{ - struct eg20t_port *priv; - - priv = container_of(port, struct eg20t_port, port); - - if (priv->use_dma) - if (priv->tx_dma_use) - return; - - priv->start_tx = 1; - pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT); -} - -static void pch_uart_stop_rx(struct uart_port *port) -{ - struct eg20t_port *priv; - priv = container_of(port, struct eg20t_port, port); - priv->start_rx = 0; - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); - priv->int_dis_flag = 1; -} - -/* Enable the modem status interrupts. */ -static void pch_uart_enable_ms(struct uart_port *port) -{ - struct eg20t_port *priv; - priv = container_of(port, struct eg20t_port, port); - pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_MS_INT); -} - -/* Control the transmission of a break signal. */ -static void pch_uart_break_ctl(struct uart_port *port, int ctl) -{ - struct eg20t_port *priv; - unsigned long flags; - - priv = container_of(port, struct eg20t_port, port); - spin_lock_irqsave(&port->lock, flags); - pch_uart_hal_set_break(priv, ctl); - spin_unlock_irqrestore(&port->lock, flags); -} - -/* Grab any interrupt resources and initialise any low level driver state. */ -static int pch_uart_startup(struct uart_port *port) -{ - struct eg20t_port *priv; - int ret; - int fifo_size; - int trigger_level; - - priv = container_of(port, struct eg20t_port, port); - priv->tx_empty = 1; - port->uartclk = priv->base_baud; - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); - ret = pch_uart_hal_set_line(priv, default_baud, - PCH_UART_HAL_PARITY_NONE, PCH_UART_HAL_8BIT, - PCH_UART_HAL_STB1); - if (ret) - return ret; - - switch (priv->fifo_size) { - case 256: - fifo_size = PCH_UART_HAL_FIFO256; - break; - case 64: - fifo_size = PCH_UART_HAL_FIFO64; - break; - case 16: - fifo_size = PCH_UART_HAL_FIFO16; - case 1: - default: - fifo_size = PCH_UART_HAL_FIFO_DIS; - break; - } - - switch (priv->trigger) { - case PCH_UART_HAL_TRIGGER1: - trigger_level = 1; - break; - case PCH_UART_HAL_TRIGGER_L: - trigger_level = priv->fifo_size / 4; - break; - case PCH_UART_HAL_TRIGGER_M: - trigger_level = priv->fifo_size / 2; - break; - case PCH_UART_HAL_TRIGGER_H: - default: - trigger_level = priv->fifo_size - (priv->fifo_size / 8); - break; - } - - priv->trigger_level = trigger_level; - ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0, - fifo_size, priv->trigger); - if (ret < 0) - return ret; - - ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED, - KBUILD_MODNAME, priv); - if (ret < 0) - return ret; - - if (priv->use_dma) - pch_request_dma(port); - - priv->start_rx = 1; - pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT); - uart_update_timeout(port, CS8, default_baud); - - return 0; -} - -static void pch_uart_shutdown(struct uart_port *port) -{ - struct eg20t_port *priv; - int ret; - - priv = container_of(port, struct eg20t_port, port); - pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); - pch_uart_hal_fifo_reset(priv, PCH_UART_HAL_CLR_ALL_FIFO); - ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0, - PCH_UART_HAL_FIFO_DIS, PCH_UART_HAL_TRIGGER1); - if (ret) - pr_err("pch_uart_hal_set_fifo Failed(ret=%d)\n", ret); - - if (priv->use_dma_flag) - pch_free_dma(port); - - free_irq(priv->port.irq, priv); -} - -/* Change the port parameters, including word length, parity, stop - *bits. Update read_status_mask and ignore_status_mask to indicate - *the types of events we are interested in receiving. */ -static void pch_uart_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - int baud; - int rtn; - unsigned int parity, bits, stb; - struct eg20t_port *priv; - unsigned long flags; - - priv = container_of(port, struct eg20t_port, port); - switch (termios->c_cflag & CSIZE) { - case CS5: - bits = PCH_UART_HAL_5BIT; - break; - case CS6: - bits = PCH_UART_HAL_6BIT; - break; - case CS7: - bits = PCH_UART_HAL_7BIT; - break; - default: /* CS8 */ - bits = PCH_UART_HAL_8BIT; - break; - } - if (termios->c_cflag & CSTOPB) - stb = PCH_UART_HAL_STB2; - else - stb = PCH_UART_HAL_STB1; - - if (termios->c_cflag & PARENB) { - if (!(termios->c_cflag & PARODD)) - parity = PCH_UART_HAL_PARITY_ODD; - else - parity = PCH_UART_HAL_PARITY_EVEN; - - } else { - parity = PCH_UART_HAL_PARITY_NONE; - } - termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - - spin_lock_irqsave(&port->lock, flags); - - uart_update_timeout(port, termios->c_cflag, baud); - rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); - if (rtn) - goto out; - - /* Don't rewrite B0 */ - if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, baud, baud); - -out: - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *pch_uart_type(struct uart_port *port) -{ - return KBUILD_MODNAME; -} - -static void pch_uart_release_port(struct uart_port *port) -{ - struct eg20t_port *priv; - - priv = container_of(port, struct eg20t_port, port); - pci_iounmap(priv->pdev, priv->membase); - pci_release_regions(priv->pdev); -} - -static int pch_uart_request_port(struct uart_port *port) -{ - struct eg20t_port *priv; - int ret; - void __iomem *membase; - - priv = container_of(port, struct eg20t_port, port); - ret = pci_request_regions(priv->pdev, KBUILD_MODNAME); - if (ret < 0) - return -EBUSY; - - membase = pci_iomap(priv->pdev, 1, 0); - if (!membase) { - pci_release_regions(priv->pdev); - return -EBUSY; - } - priv->membase = port->membase = membase; - - return 0; -} - -static void pch_uart_config_port(struct uart_port *port, int type) -{ - struct eg20t_port *priv; - - priv = container_of(port, struct eg20t_port, port); - if (type & UART_CONFIG_TYPE) { - port->type = priv->port_type; - pch_uart_request_port(port); - } -} - -static int pch_uart_verify_port(struct uart_port *port, - struct serial_struct *serinfo) -{ - struct eg20t_port *priv; - - priv = container_of(port, struct eg20t_port, port); - if (serinfo->flags & UPF_LOW_LATENCY) { - pr_info("PCH UART : Use PIO Mode (without DMA)\n"); - priv->use_dma = 0; - serinfo->flags &= ~UPF_LOW_LATENCY; - } else { -#ifndef CONFIG_PCH_DMA - pr_err("%s : PCH DMA is not Loaded.\n", __func__); - return -EOPNOTSUPP; -#endif - priv->use_dma = 1; - priv->use_dma_flag = 1; - pr_info("PCH UART : Use DMA Mode\n"); - } - - return 0; -} - -static struct uart_ops pch_uart_ops = { - .tx_empty = pch_uart_tx_empty, - .set_mctrl = pch_uart_set_mctrl, - .get_mctrl = pch_uart_get_mctrl, - .stop_tx = pch_uart_stop_tx, - .start_tx = pch_uart_start_tx, - .stop_rx = pch_uart_stop_rx, - .enable_ms = pch_uart_enable_ms, - .break_ctl = pch_uart_break_ctl, - .startup = pch_uart_startup, - .shutdown = pch_uart_shutdown, - .set_termios = pch_uart_set_termios, -/* .pm = pch_uart_pm, Not supported yet */ -/* .set_wake = pch_uart_set_wake, Not supported yet */ - .type = pch_uart_type, - .release_port = pch_uart_release_port, - .request_port = pch_uart_request_port, - .config_port = pch_uart_config_port, - .verify_port = pch_uart_verify_port -}; - -static struct uart_driver pch_uart_driver = { - .owner = THIS_MODULE, - .driver_name = KBUILD_MODNAME, - .dev_name = PCH_UART_DRIVER_DEVICE, - .major = 0, - .minor = 0, - .nr = PCH_UART_NR, -}; - -static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, - int port_type) -{ - struct eg20t_port *priv; - int ret; - unsigned int iobase; - unsigned int mapbase; - unsigned char *rxbuf; - int fifosize, base_baud; - static int num; - - priv = kzalloc(sizeof(struct eg20t_port), GFP_KERNEL); - if (priv == NULL) - goto init_port_alloc_err; - - rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); - if (!rxbuf) - goto init_port_free_txbuf; - - switch (port_type) { - case PORT_UNKNOWN: - fifosize = 256; /* UART0 */ - base_baud = 1843200; /* 1.8432MHz */ - break; - case PORT_8250: - fifosize = 64; /* UART1~3 */ - base_baud = 1843200; /* 1.8432MHz */ - break; - default: - dev_err(&pdev->dev, "Invalid Port Type(=%d)\n", port_type); - goto init_port_hal_free; - } - - iobase = pci_resource_start(pdev, 0); - mapbase = pci_resource_start(pdev, 1); - priv->mapbase = mapbase; - priv->iobase = iobase; - priv->pdev = pdev; - priv->tx_empty = 1; - priv->rxbuf.buf = rxbuf; - priv->rxbuf.size = PAGE_SIZE; - - priv->fifo_size = fifosize; - priv->base_baud = base_baud; - priv->port_type = PORT_MAX_8250 + port_type + 1; - priv->port.dev = &pdev->dev; - priv->port.iobase = iobase; - priv->port.membase = NULL; - priv->port.mapbase = mapbase; - priv->port.irq = pdev->irq; - priv->port.iotype = UPIO_PORT; - priv->port.ops = &pch_uart_ops; - priv->port.flags = UPF_BOOT_AUTOCONF; - priv->port.fifosize = fifosize; - priv->port.line = num++; - priv->trigger = PCH_UART_HAL_TRIGGER_M; - - pci_set_drvdata(pdev, priv); - pch_uart_hal_request(pdev, fifosize, base_baud); - ret = uart_add_one_port(&pch_uart_driver, &priv->port); - if (ret < 0) - goto init_port_hal_free; - - return priv; - -init_port_hal_free: - free_page((unsigned long)rxbuf); -init_port_free_txbuf: - kfree(priv); -init_port_alloc_err: - - return NULL; -} - -static void pch_uart_exit_port(struct eg20t_port *priv) -{ - uart_remove_one_port(&pch_uart_driver, &priv->port); - pci_set_drvdata(priv->pdev, NULL); - free_page((unsigned long)priv->rxbuf.buf); -} - -static void pch_uart_pci_remove(struct pci_dev *pdev) -{ - struct eg20t_port *priv; - - priv = (struct eg20t_port *)pci_get_drvdata(pdev); - pch_uart_exit_port(priv); - pci_disable_device(pdev); - kfree(priv); - return; -} -#ifdef CONFIG_PM -static int pch_uart_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct eg20t_port *priv = pci_get_drvdata(pdev); - - uart_suspend_port(&pch_uart_driver, &priv->port); - - pci_save_state(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - return 0; -} - -static int pch_uart_pci_resume(struct pci_dev *pdev) -{ - struct eg20t_port *priv = pci_get_drvdata(pdev); - int ret; - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - - ret = pci_enable_device(pdev); - if (ret) { - dev_err(&pdev->dev, - "%s-pci_enable_device failed(ret=%d) ", __func__, ret); - return ret; - } - - uart_resume_port(&pch_uart_driver, &priv->port); - - return 0; -} -#else -#define pch_uart_pci_suspend NULL -#define pch_uart_pci_resume NULL -#endif - -static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811), - .driver_data = PCH_UART_8LINE}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812), - .driver_data = PCH_UART_2LINE}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8813), - .driver_data = PCH_UART_2LINE}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8814), - .driver_data = PCH_UART_2LINE}, - {0,}, -}; - -static int __devinit pch_uart_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - int ret; - struct eg20t_port *priv; - - ret = pci_enable_device(pdev); - if (ret < 0) - goto probe_error; - - priv = pch_uart_init_port(pdev, id->driver_data); - if (!priv) { - ret = -EBUSY; - goto probe_disable_device; - } - pci_set_drvdata(pdev, priv); - - return ret; - -probe_disable_device: - pci_disable_device(pdev); -probe_error: - return ret; -} - -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), - .suspend = pch_uart_pci_suspend, - .resume = pch_uart_pci_resume, -}; - -static int __init pch_uart_module_init(void) -{ - int ret; - - /* register as UART driver */ - ret = uart_register_driver(&pch_uart_driver); - if (ret < 0) - return ret; - - /* register as PCI driver */ - ret = pci_register_driver(&pch_uart_pci_driver); - if (ret < 0) - uart_unregister_driver(&pch_uart_driver); - - return ret; -} -module_init(pch_uart_module_init); - -static void __exit pch_uart_module_exit(void) -{ - pci_unregister_driver(&pch_uart_pci_driver); - uart_unregister_driver(&pch_uart_driver); -} -module_exit(pch_uart_module_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver"); -module_param(default_baud, uint, S_IRUGO); diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c deleted file mode 100644 index 5b9cde79e4ea..000000000000 --- a/drivers/serial/pmac_zilog.c +++ /dev/null @@ -1,2208 +0,0 @@ -/* - * linux/drivers/serial/pmac_zilog.c - * - * Driver for PowerMac Z85c30 based ESCC cell found in the - * "macio" ASICs of various PowerMac models - * - * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) - * - * Derived from drivers/macintosh/macserial.c by Paul Mackerras - * and drivers/serial/sunzilog.c by David S. Miller - * - * Hrm... actually, I ripped most of sunzilog (Thanks David !) and - * adapted special tweaks needed for us. I don't think it's worth - * merging back those though. The DMA code still has to get in - * and once done, I expect that driver to remain fairly stable in - * the long term, unless we change the driver model again... - * - * 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 - * - * 2004-08-06 Harald Welte - * - Enable BREAK interrupt - * - Add support for sysreq - * - * TODO: - Add DMA support - * - Defer port shutdown to a few seconds after close - * - maybe put something right into uap->clk_divisor - */ - -#undef DEBUG -#undef DEBUG_HARD -#undef USE_CTRL_O_SYSRQ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef CONFIG_PPC_PMAC -#include -#include -#include -#include -#include -#else -#include -#define of_machine_is_compatible(x) (0) -#endif - -#if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include - -#include "pmac_zilog.h" - -/* Not yet implemented */ -#undef HAS_DBDMA - -static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt )"; -MODULE_AUTHOR("Benjamin Herrenschmidt "); -MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports."); -MODULE_LICENSE("GPL"); - -#ifdef CONFIG_SERIAL_PMACZILOG_TTYS -#define PMACZILOG_MAJOR TTY_MAJOR -#define PMACZILOG_MINOR 64 -#define PMACZILOG_NAME "ttyS" -#else -#define PMACZILOG_MAJOR 204 -#define PMACZILOG_MINOR 192 -#define PMACZILOG_NAME "ttyPZ" -#endif - - -/* - * For the sake of early serial console, we can do a pre-probe - * (optional) of the ports at rather early boot time. - */ -static struct uart_pmac_port pmz_ports[MAX_ZS_PORTS]; -static int pmz_ports_count; -static DEFINE_MUTEX(pmz_irq_mutex); - -static struct uart_driver pmz_uart_reg = { - .owner = THIS_MODULE, - .driver_name = PMACZILOG_NAME, - .dev_name = PMACZILOG_NAME, - .major = PMACZILOG_MAJOR, - .minor = PMACZILOG_MINOR, -}; - - -/* - * Load all registers to reprogram the port - * This function must only be called when the TX is not busy. The UART - * port lock must be held and local interrupts disabled. - */ -static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) -{ - int i; - - if (ZS_IS_ASLEEP(uap)) - return; - - /* Let pending transmits finish. */ - for (i = 0; i < 1000; i++) { - unsigned char stat = read_zsreg(uap, R1); - if (stat & ALL_SNT) - break; - udelay(100); - } - - ZS_CLEARERR(uap); - zssync(uap); - ZS_CLEARFIFO(uap); - zssync(uap); - ZS_CLEARERR(uap); - - /* Disable all interrupts. */ - write_zsreg(uap, R1, - regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); - - /* Set parity, sync config, stop bits, and clock divisor. */ - write_zsreg(uap, R4, regs[R4]); - - /* Set misc. TX/RX control bits. */ - write_zsreg(uap, R10, regs[R10]); - - /* Set TX/RX controls sans the enable bits. */ - write_zsreg(uap, R3, regs[R3] & ~RxENABLE); - write_zsreg(uap, R5, regs[R5] & ~TxENABLE); - - /* now set R7 "prime" on ESCC */ - write_zsreg(uap, R15, regs[R15] | EN85C30); - write_zsreg(uap, R7, regs[R7P]); - - /* make sure we use R7 "non-prime" on ESCC */ - write_zsreg(uap, R15, regs[R15] & ~EN85C30); - - /* Synchronous mode config. */ - write_zsreg(uap, R6, regs[R6]); - write_zsreg(uap, R7, regs[R7]); - - /* Disable baud generator. */ - write_zsreg(uap, R14, regs[R14] & ~BRENAB); - - /* Clock mode control. */ - write_zsreg(uap, R11, regs[R11]); - - /* Lower and upper byte of baud rate generator divisor. */ - write_zsreg(uap, R12, regs[R12]); - write_zsreg(uap, R13, regs[R13]); - - /* Now rewrite R14, with BRENAB (if set). */ - write_zsreg(uap, R14, regs[R14]); - - /* Reset external status interrupts. */ - write_zsreg(uap, R0, RES_EXT_INT); - write_zsreg(uap, R0, RES_EXT_INT); - - /* Rewrite R3/R5, this time without enables masked. */ - write_zsreg(uap, R3, regs[R3]); - write_zsreg(uap, R5, regs[R5]); - - /* Rewrite R1, this time without IRQ enabled masked. */ - write_zsreg(uap, R1, regs[R1]); - - /* Enable interrupts */ - write_zsreg(uap, R9, regs[R9]); -} - -/* - * We do like sunzilog to avoid disrupting pending Tx - * Reprogram the Zilog channel HW registers with the copies found in the - * software state struct. If the transmitter is busy, we defer this update - * until the next TX complete interrupt. Else, we do it right now. - * - * The UART port lock must be held and local interrupts disabled. - */ -static void pmz_maybe_update_regs(struct uart_pmac_port *uap) -{ - if (!ZS_REGS_HELD(uap)) { - if (ZS_TX_ACTIVE(uap)) { - uap->flags |= PMACZILOG_FLAG_REGS_HELD; - } else { - pmz_debug("pmz: maybe_update_regs: updating\n"); - pmz_load_zsregs(uap, uap->curregs); - } - } -} - -static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) -{ - struct tty_struct *tty = NULL; - unsigned char ch, r1, drop, error, flag; - int loops = 0; - - /* The interrupt can be enabled when the port isn't open, typically - * that happens when using one port is open and the other closed (stale - * interrupt) or when one port is used as a console. - */ - if (!ZS_IS_OPEN(uap)) { - pmz_debug("pmz: draining input\n"); - /* Port is closed, drain input data */ - for (;;) { - if ((++loops) > 1000) - goto flood; - (void)read_zsreg(uap, R1); - write_zsreg(uap, R0, ERR_RES); - (void)read_zsdata(uap); - ch = read_zsreg(uap, R0); - if (!(ch & Rx_CH_AV)) - break; - } - return NULL; - } - - /* Sanity check, make sure the old bug is no longer happening */ - if (uap->port.state == NULL || uap->port.state->port.tty == NULL) { - WARN_ON(1); - (void)read_zsdata(uap); - return NULL; - } - tty = uap->port.state->port.tty; - - while (1) { - error = 0; - drop = 0; - - r1 = read_zsreg(uap, R1); - ch = read_zsdata(uap); - - if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { - write_zsreg(uap, R0, ERR_RES); - zssync(uap); - } - - ch &= uap->parity_mask; - if (ch == 0 && uap->flags & PMACZILOG_FLAG_BREAK) { - uap->flags &= ~PMACZILOG_FLAG_BREAK; - } - -#if defined(CONFIG_MAGIC_SYSRQ) && defined(CONFIG_SERIAL_CORE_CONSOLE) -#ifdef USE_CTRL_O_SYSRQ - /* Handle the SysRq ^O Hack */ - if (ch == '\x0f') { - uap->port.sysrq = jiffies + HZ*5; - goto next_char; - } -#endif /* USE_CTRL_O_SYSRQ */ - if (uap->port.sysrq) { - int swallow; - spin_unlock(&uap->port.lock); - swallow = uart_handle_sysrq_char(&uap->port, ch); - spin_lock(&uap->port.lock); - if (swallow) - goto next_char; - } -#endif /* CONFIG_MAGIC_SYSRQ && CONFIG_SERIAL_CORE_CONSOLE */ - - /* A real serial line, record the character and status. */ - if (drop) - goto next_char; - - flag = TTY_NORMAL; - uap->port.icount.rx++; - - if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) { - error = 1; - if (r1 & BRK_ABRT) { - pmz_debug("pmz: got break !\n"); - r1 &= ~(PAR_ERR | CRC_ERR); - uap->port.icount.brk++; - if (uart_handle_break(&uap->port)) - goto next_char; - } - else if (r1 & PAR_ERR) - uap->port.icount.parity++; - else if (r1 & CRC_ERR) - uap->port.icount.frame++; - if (r1 & Rx_OVR) - uap->port.icount.overrun++; - r1 &= uap->port.read_status_mask; - if (r1 & BRK_ABRT) - flag = TTY_BREAK; - else if (r1 & PAR_ERR) - flag = TTY_PARITY; - else if (r1 & CRC_ERR) - flag = TTY_FRAME; - } - - if (uap->port.ignore_status_mask == 0xff || - (r1 & uap->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if (r1 & Rx_OVR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - next_char: - /* We can get stuck in an infinite loop getting char 0 when the - * line is in a wrong HW state, we break that here. - * When that happens, I disable the receive side of the driver. - * Note that what I've been experiencing is a real irq loop where - * I'm getting flooded regardless of the actual port speed. - * Something stange is going on with the HW - */ - if ((++loops) > 1000) - goto flood; - ch = read_zsreg(uap, R0); - if (!(ch & Rx_CH_AV)) - break; - } - - return tty; - flood: - uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); - write_zsreg(uap, R1, uap->curregs[R1]); - zssync(uap); - pmz_error("pmz: rx irq flood !\n"); - return tty; -} - -static void pmz_status_handle(struct uart_pmac_port *uap) -{ - unsigned char status; - - status = read_zsreg(uap, R0); - write_zsreg(uap, R0, RES_EXT_INT); - zssync(uap); - - if (ZS_IS_OPEN(uap) && ZS_WANTS_MODEM_STATUS(uap)) { - if (status & SYNC_HUNT) - uap->port.icount.dsr++; - - /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. - * But it does not tell us which bit has changed, we have to keep - * track of this ourselves. - * The CTS input is inverted for some reason. -- paulus - */ - if ((status ^ uap->prev_status) & DCD) - uart_handle_dcd_change(&uap->port, - (status & DCD)); - if ((status ^ uap->prev_status) & CTS) - uart_handle_cts_change(&uap->port, - !(status & CTS)); - - wake_up_interruptible(&uap->port.state->port.delta_msr_wait); - } - - if (status & BRK_ABRT) - uap->flags |= PMACZILOG_FLAG_BREAK; - - uap->prev_status = status; -} - -static void pmz_transmit_chars(struct uart_pmac_port *uap) -{ - struct circ_buf *xmit; - - if (ZS_IS_ASLEEP(uap)) - return; - if (ZS_IS_CONS(uap)) { - unsigned char status = read_zsreg(uap, R0); - - /* TX still busy? Just wait for the next TX done interrupt. - * - * It can occur because of how we do serial console writes. It would - * be nice to transmit console writes just like we normally would for - * a TTY line. (ie. buffered and TX interrupt driven). That is not - * easy because console writes cannot sleep. One solution might be - * to poll on enough port->xmit space becomming free. -DaveM - */ - if (!(status & Tx_BUF_EMP)) - return; - } - - uap->flags &= ~PMACZILOG_FLAG_TX_ACTIVE; - - if (ZS_REGS_HELD(uap)) { - pmz_load_zsregs(uap, uap->curregs); - uap->flags &= ~PMACZILOG_FLAG_REGS_HELD; - } - - if (ZS_TX_STOPPED(uap)) { - uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED; - goto ack_tx_int; - } - - /* Under some circumstances, we see interrupts reported for - * a closed channel. The interrupt mask in R1 is clear, but - * R3 still signals the interrupts and we see them when taking - * an interrupt for the other channel (this could be a qemu - * bug but since the ESCC doc doesn't specify precsiely whether - * R3 interrup status bits are masked by R1 interrupt enable - * bits, better safe than sorry). --BenH. - */ - if (!ZS_IS_OPEN(uap)) - goto ack_tx_int; - - if (uap->port.x_char) { - uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; - write_zsdata(uap, uap->port.x_char); - zssync(uap); - uap->port.icount.tx++; - uap->port.x_char = 0; - return; - } - - if (uap->port.state == NULL) - goto ack_tx_int; - xmit = &uap->port.state->xmit; - if (uart_circ_empty(xmit)) { - uart_write_wakeup(&uap->port); - goto ack_tx_int; - } - if (uart_tx_stopped(&uap->port)) - goto ack_tx_int; - - uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; - write_zsdata(uap, xmit->buf[xmit->tail]); - zssync(uap); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - uap->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uap->port); - - return; - -ack_tx_int: - write_zsreg(uap, R0, RES_Tx_P); - zssync(uap); -} - -/* Hrm... we register that twice, fixme later.... */ -static irqreturn_t pmz_interrupt(int irq, void *dev_id) -{ - struct uart_pmac_port *uap = dev_id; - struct uart_pmac_port *uap_a; - struct uart_pmac_port *uap_b; - int rc = IRQ_NONE; - struct tty_struct *tty; - u8 r3; - - uap_a = pmz_get_port_A(uap); - uap_b = uap_a->mate; - - spin_lock(&uap_a->port.lock); - r3 = read_zsreg(uap_a, R3); - -#ifdef DEBUG_HARD - pmz_debug("irq, r3: %x\n", r3); -#endif - /* Channel A */ - tty = NULL; - if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { - write_zsreg(uap_a, R0, RES_H_IUS); - zssync(uap_a); - if (r3 & CHAEXT) - pmz_status_handle(uap_a); - if (r3 & CHARxIP) - tty = pmz_receive_chars(uap_a); - if (r3 & CHATxIP) - pmz_transmit_chars(uap_a); - rc = IRQ_HANDLED; - } - spin_unlock(&uap_a->port.lock); - if (tty != NULL) - tty_flip_buffer_push(tty); - - if (uap_b->node == NULL) - goto out; - - spin_lock(&uap_b->port.lock); - tty = NULL; - if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { - write_zsreg(uap_b, R0, RES_H_IUS); - zssync(uap_b); - if (r3 & CHBEXT) - pmz_status_handle(uap_b); - if (r3 & CHBRxIP) - tty = pmz_receive_chars(uap_b); - if (r3 & CHBTxIP) - pmz_transmit_chars(uap_b); - rc = IRQ_HANDLED; - } - spin_unlock(&uap_b->port.lock); - if (tty != NULL) - tty_flip_buffer_push(tty); - - out: -#ifdef DEBUG_HARD - pmz_debug("irq done.\n"); -#endif - return rc; -} - -/* - * Peek the status register, lock not held by caller - */ -static inline u8 pmz_peek_status(struct uart_pmac_port *uap) -{ - unsigned long flags; - u8 status; - - spin_lock_irqsave(&uap->port.lock, flags); - status = read_zsreg(uap, R0); - spin_unlock_irqrestore(&uap->port.lock, flags); - - return status; -} - -/* - * Check if transmitter is empty - * The port lock is not held. - */ -static unsigned int pmz_tx_empty(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned char status; - - if (ZS_IS_ASLEEP(uap) || uap->node == NULL) - return TIOCSER_TEMT; - - status = pmz_peek_status(to_pmz(port)); - if (status & Tx_BUF_EMP) - return TIOCSER_TEMT; - return 0; -} - -/* - * Set Modem Control (RTS & DTR) bits - * The port lock is held and interrupts are disabled. - * Note: Shall we really filter out RTS on external ports or - * should that be dealt at higher level only ? - */ -static void pmz_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned char set_bits, clear_bits; - - /* Do nothing for irda for now... */ - if (ZS_IS_IRDA(uap)) - return; - /* We get called during boot with a port not up yet */ - if (ZS_IS_ASLEEP(uap) || - !(ZS_IS_OPEN(uap) || ZS_IS_CONS(uap))) - return; - - set_bits = clear_bits = 0; - - if (ZS_IS_INTMODEM(uap)) { - if (mctrl & TIOCM_RTS) - set_bits |= RTS; - else - clear_bits |= RTS; - } - if (mctrl & TIOCM_DTR) - set_bits |= DTR; - else - clear_bits |= DTR; - - /* NOTE: Not subject to 'transmitter active' rule. */ - uap->curregs[R5] |= set_bits; - uap->curregs[R5] &= ~clear_bits; - if (ZS_IS_ASLEEP(uap)) - return; - write_zsreg(uap, R5, uap->curregs[R5]); - pmz_debug("pmz_set_mctrl: set bits: %x, clear bits: %x -> %x\n", - set_bits, clear_bits, uap->curregs[R5]); - zssync(uap); -} - -/* - * Get Modem Control bits (only the input ones, the core will - * or that with a cached value of the control ones) - * The port lock is held and interrupts are disabled. - */ -static unsigned int pmz_get_mctrl(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned char status; - unsigned int ret; - - if (ZS_IS_ASLEEP(uap) || uap->node == NULL) - return 0; - - status = read_zsreg(uap, R0); - - ret = 0; - if (status & DCD) - ret |= TIOCM_CAR; - if (status & SYNC_HUNT) - ret |= TIOCM_DSR; - if (!(status & CTS)) - ret |= TIOCM_CTS; - - return ret; -} - -/* - * Stop TX side. Dealt like sunzilog at next Tx interrupt, - * though for DMA, we will have to do a bit more. - * The port lock is held and interrupts are disabled. - */ -static void pmz_stop_tx(struct uart_port *port) -{ - to_pmz(port)->flags |= PMACZILOG_FLAG_TX_STOPPED; -} - -/* - * Kick the Tx side. - * The port lock is held and interrupts are disabled. - */ -static void pmz_start_tx(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned char status; - - pmz_debug("pmz: start_tx()\n"); - - uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; - uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED; - - if (ZS_IS_ASLEEP(uap) || uap->node == NULL) - return; - - status = read_zsreg(uap, R0); - - /* TX busy? Just wait for the TX done interrupt. */ - if (!(status & Tx_BUF_EMP)) - return; - - /* Send the first character to jump-start the TX done - * IRQ sending engine. - */ - if (port->x_char) { - write_zsdata(uap, port->x_char); - zssync(uap); - port->icount.tx++; - port->x_char = 0; - } else { - struct circ_buf *xmit = &port->state->xmit; - - write_zsdata(uap, xmit->buf[xmit->tail]); - zssync(uap); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&uap->port); - } - pmz_debug("pmz: start_tx() done.\n"); -} - -/* - * Stop Rx side, basically disable emitting of - * Rx interrupts on the port. We don't disable the rx - * side of the chip proper though - * The port lock is held. - */ -static void pmz_stop_rx(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - - if (ZS_IS_ASLEEP(uap) || uap->node == NULL) - return; - - pmz_debug("pmz: stop_rx()()\n"); - - /* Disable all RX interrupts. */ - uap->curregs[R1] &= ~RxINT_MASK; - pmz_maybe_update_regs(uap); - - pmz_debug("pmz: stop_rx() done.\n"); -} - -/* - * Enable modem status change interrupts - * The port lock is held. - */ -static void pmz_enable_ms(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned char new_reg; - - if (ZS_IS_IRDA(uap) || uap->node == NULL) - return; - new_reg = uap->curregs[R15] | (DCDIE | SYNCIE | CTSIE); - if (new_reg != uap->curregs[R15]) { - uap->curregs[R15] = new_reg; - - if (ZS_IS_ASLEEP(uap)) - return; - /* NOTE: Not subject to 'transmitter active' rule. */ - write_zsreg(uap, R15, uap->curregs[R15]); - } -} - -/* - * Control break state emission - * The port lock is not held. - */ -static void pmz_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned char set_bits, clear_bits, new_reg; - unsigned long flags; - - if (uap->node == NULL) - return; - set_bits = clear_bits = 0; - - if (break_state) - set_bits |= SND_BRK; - else - clear_bits |= SND_BRK; - - spin_lock_irqsave(&port->lock, flags); - - new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits; - if (new_reg != uap->curregs[R5]) { - uap->curregs[R5] = new_reg; - - /* NOTE: Not subject to 'transmitter active' rule. */ - if (ZS_IS_ASLEEP(uap)) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } - write_zsreg(uap, R5, uap->curregs[R5]); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -#ifdef CONFIG_PPC_PMAC - -/* - * Turn power on or off to the SCC and associated stuff - * (port drivers, modem, IR port, etc.) - * Returns the number of milliseconds we should wait before - * trying to use the port. - */ -static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) -{ - int delay = 0; - int rc; - - if (state) { - rc = pmac_call_feature( - PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 1); - pmz_debug("port power on result: %d\n", rc); - if (ZS_IS_INTMODEM(uap)) { - rc = pmac_call_feature( - PMAC_FTR_MODEM_ENABLE, uap->node, 0, 1); - delay = 2500; /* wait for 2.5s before using */ - pmz_debug("modem power result: %d\n", rc); - } - } else { - /* TODO: Make that depend on a timer, don't power down - * immediately - */ - if (ZS_IS_INTMODEM(uap)) { - rc = pmac_call_feature( - PMAC_FTR_MODEM_ENABLE, uap->node, 0, 0); - pmz_debug("port power off result: %d\n", rc); - } - pmac_call_feature(PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 0); - } - return delay; -} - -#else - -static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) -{ - return 0; -} - -#endif /* !CONFIG_PPC_PMAC */ - -/* - * FixZeroBug....Works around a bug in the SCC receving channel. - * Inspired from Darwin code, 15 Sept. 2000 -DanM - * - * The following sequence prevents a problem that is seen with O'Hare ASICs - * (most versions -- also with some Heathrow and Hydra ASICs) where a zero - * at the input to the receiver becomes 'stuck' and locks up the receiver. - * This problem can occur as a result of a zero bit at the receiver input - * coincident with any of the following events: - * - * The SCC is initialized (hardware or software). - * A framing error is detected. - * The clocking option changes from synchronous or X1 asynchronous - * clocking to X16, X32, or X64 asynchronous clocking. - * The decoding mode is changed among NRZ, NRZI, FM0, or FM1. - * - * This workaround attempts to recover from the lockup condition by placing - * the SCC in synchronous loopback mode with a fast clock before programming - * any of the asynchronous modes. - */ -static void pmz_fix_zero_bug_scc(struct uart_pmac_port *uap) -{ - write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB); - zssync(uap); - udelay(10); - write_zsreg(uap, 9, (ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB) | NV); - zssync(uap); - - write_zsreg(uap, 4, X1CLK | MONSYNC); - write_zsreg(uap, 3, Rx8); - write_zsreg(uap, 5, Tx8 | RTS); - write_zsreg(uap, 9, NV); /* Didn't we already do this? */ - write_zsreg(uap, 11, RCBR | TCBR); - write_zsreg(uap, 12, 0); - write_zsreg(uap, 13, 0); - write_zsreg(uap, 14, (LOOPBAK | BRSRC)); - write_zsreg(uap, 14, (LOOPBAK | BRSRC | BRENAB)); - write_zsreg(uap, 3, Rx8 | RxENABLE); - write_zsreg(uap, 0, RES_EXT_INT); - write_zsreg(uap, 0, RES_EXT_INT); - write_zsreg(uap, 0, RES_EXT_INT); /* to kill some time */ - - /* The channel should be OK now, but it is probably receiving - * loopback garbage. - * Switch to asynchronous mode, disable the receiver, - * and discard everything in the receive buffer. - */ - write_zsreg(uap, 9, NV); - write_zsreg(uap, 4, X16CLK | SB_MASK); - write_zsreg(uap, 3, Rx8); - - while (read_zsreg(uap, 0) & Rx_CH_AV) { - (void)read_zsreg(uap, 8); - write_zsreg(uap, 0, RES_EXT_INT); - write_zsreg(uap, 0, ERR_RES); - } -} - -/* - * Real startup routine, powers up the hardware and sets up - * the SCC. Returns a delay in ms where you need to wait before - * actually using the port, this is typically the internal modem - * powerup delay. This routine expect the lock to be taken. - */ -static int __pmz_startup(struct uart_pmac_port *uap) -{ - int pwr_delay = 0; - - memset(&uap->curregs, 0, sizeof(uap->curregs)); - - /* Power up the SCC & underlying hardware (modem/irda) */ - pwr_delay = pmz_set_scc_power(uap, 1); - - /* Nice buggy HW ... */ - pmz_fix_zero_bug_scc(uap); - - /* Reset the channel */ - uap->curregs[R9] = 0; - write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB); - zssync(uap); - udelay(10); - write_zsreg(uap, 9, 0); - zssync(uap); - - /* Clear the interrupt registers */ - write_zsreg(uap, R1, 0); - write_zsreg(uap, R0, ERR_RES); - write_zsreg(uap, R0, ERR_RES); - write_zsreg(uap, R0, RES_H_IUS); - write_zsreg(uap, R0, RES_H_IUS); - - /* Setup some valid baud rate */ - uap->curregs[R4] = X16CLK | SB1; - uap->curregs[R3] = Rx8; - uap->curregs[R5] = Tx8 | RTS; - if (!ZS_IS_IRDA(uap)) - uap->curregs[R5] |= DTR; - uap->curregs[R12] = 0; - uap->curregs[R13] = 0; - uap->curregs[R14] = BRENAB; - - /* Clear handshaking, enable BREAK interrupts */ - uap->curregs[R15] = BRKIE; - - /* Master interrupt enable */ - uap->curregs[R9] |= NV | MIE; - - pmz_load_zsregs(uap, uap->curregs); - - /* Enable receiver and transmitter. */ - write_zsreg(uap, R3, uap->curregs[R3] |= RxENABLE); - write_zsreg(uap, R5, uap->curregs[R5] |= TxENABLE); - - /* Remember status for DCD/CTS changes */ - uap->prev_status = read_zsreg(uap, R0); - - return pwr_delay; -} - -static void pmz_irda_reset(struct uart_pmac_port *uap) -{ - uap->curregs[R5] |= DTR; - write_zsreg(uap, R5, uap->curregs[R5]); - zssync(uap); - mdelay(110); - uap->curregs[R5] &= ~DTR; - write_zsreg(uap, R5, uap->curregs[R5]); - zssync(uap); - mdelay(10); -} - -/* - * This is the "normal" startup routine, using the above one - * wrapped with the lock and doing a schedule delay - */ -static int pmz_startup(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned long flags; - int pwr_delay = 0; - - pmz_debug("pmz: startup()\n"); - - if (ZS_IS_ASLEEP(uap)) - return -EAGAIN; - if (uap->node == NULL) - return -ENODEV; - - mutex_lock(&pmz_irq_mutex); - - uap->flags |= PMACZILOG_FLAG_IS_OPEN; - - /* A console is never powered down. Else, power up and - * initialize the chip - */ - if (!ZS_IS_CONS(uap)) { - spin_lock_irqsave(&port->lock, flags); - pwr_delay = __pmz_startup(uap); - spin_unlock_irqrestore(&port->lock, flags); - } - - pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; - if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, - "SCC", uap)) { - pmz_error("Unable to register zs interrupt handler.\n"); - pmz_set_scc_power(uap, 0); - mutex_unlock(&pmz_irq_mutex); - return -ENXIO; - } - - mutex_unlock(&pmz_irq_mutex); - - /* Right now, we deal with delay by blocking here, I'll be - * smarter later on - */ - if (pwr_delay != 0) { - pmz_debug("pmz: delaying %d ms\n", pwr_delay); - msleep(pwr_delay); - } - - /* IrDA reset is done now */ - if (ZS_IS_IRDA(uap)) - pmz_irda_reset(uap); - - /* Enable interrupts emission from the chip */ - spin_lock_irqsave(&port->lock, flags); - uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; - if (!ZS_IS_EXTCLK(uap)) - uap->curregs[R1] |= EXT_INT_ENAB; - write_zsreg(uap, R1, uap->curregs[R1]); - spin_unlock_irqrestore(&port->lock, flags); - - pmz_debug("pmz: startup() done.\n"); - - return 0; -} - -static void pmz_shutdown(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned long flags; - - pmz_debug("pmz: shutdown()\n"); - - if (uap->node == NULL) - return; - - mutex_lock(&pmz_irq_mutex); - - /* Release interrupt handler */ - free_irq(uap->port.irq, uap); - - spin_lock_irqsave(&port->lock, flags); - - uap->flags &= ~PMACZILOG_FLAG_IS_OPEN; - - if (!ZS_IS_OPEN(uap->mate)) - pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON; - - /* Disable interrupts */ - if (!ZS_IS_ASLEEP(uap)) { - uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); - write_zsreg(uap, R1, uap->curregs[R1]); - zssync(uap); - } - - if (ZS_IS_CONS(uap) || ZS_IS_ASLEEP(uap)) { - spin_unlock_irqrestore(&port->lock, flags); - mutex_unlock(&pmz_irq_mutex); - return; - } - - /* Disable receiver and transmitter. */ - uap->curregs[R3] &= ~RxENABLE; - uap->curregs[R5] &= ~TxENABLE; - - /* Disable all interrupts and BRK assertion. */ - uap->curregs[R5] &= ~SND_BRK; - pmz_maybe_update_regs(uap); - - /* Shut the chip down */ - pmz_set_scc_power(uap, 0); - - spin_unlock_irqrestore(&port->lock, flags); - - mutex_unlock(&pmz_irq_mutex); - - pmz_debug("pmz: shutdown() done.\n"); -} - -/* Shared by TTY driver and serial console setup. The port lock is held - * and local interrupts are disabled. - */ -static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag, - unsigned int iflag, unsigned long baud) -{ - int brg; - - /* Switch to external clocking for IrDA high clock rates. That - * code could be re-used for Midi interfaces with different - * multipliers - */ - if (baud >= 115200 && ZS_IS_IRDA(uap)) { - uap->curregs[R4] = X1CLK; - uap->curregs[R11] = RCTRxCP | TCTRxCP; - uap->curregs[R14] = 0; /* BRG off */ - uap->curregs[R12] = 0; - uap->curregs[R13] = 0; - uap->flags |= PMACZILOG_FLAG_IS_EXTCLK; - } else { - switch (baud) { - case ZS_CLOCK/16: /* 230400 */ - uap->curregs[R4] = X16CLK; - uap->curregs[R11] = 0; - uap->curregs[R14] = 0; - break; - case ZS_CLOCK/32: /* 115200 */ - uap->curregs[R4] = X32CLK; - uap->curregs[R11] = 0; - uap->curregs[R14] = 0; - break; - default: - uap->curregs[R4] = X16CLK; - uap->curregs[R11] = TCBR | RCBR; - brg = BPS_TO_BRG(baud, ZS_CLOCK / 16); - uap->curregs[R12] = (brg & 255); - uap->curregs[R13] = ((brg >> 8) & 255); - uap->curregs[R14] = BRENAB; - } - uap->flags &= ~PMACZILOG_FLAG_IS_EXTCLK; - } - - /* Character size, stop bits, and parity. */ - uap->curregs[3] &= ~RxN_MASK; - uap->curregs[5] &= ~TxN_MASK; - - switch (cflag & CSIZE) { - case CS5: - uap->curregs[3] |= Rx5; - uap->curregs[5] |= Tx5; - uap->parity_mask = 0x1f; - break; - case CS6: - uap->curregs[3] |= Rx6; - uap->curregs[5] |= Tx6; - uap->parity_mask = 0x3f; - break; - case CS7: - uap->curregs[3] |= Rx7; - uap->curregs[5] |= Tx7; - uap->parity_mask = 0x7f; - break; - case CS8: - default: - uap->curregs[3] |= Rx8; - uap->curregs[5] |= Tx8; - uap->parity_mask = 0xff; - break; - }; - uap->curregs[4] &= ~(SB_MASK); - if (cflag & CSTOPB) - uap->curregs[4] |= SB2; - else - uap->curregs[4] |= SB1; - if (cflag & PARENB) - uap->curregs[4] |= PAR_ENAB; - else - uap->curregs[4] &= ~PAR_ENAB; - if (!(cflag & PARODD)) - uap->curregs[4] |= PAR_EVEN; - else - uap->curregs[4] &= ~PAR_EVEN; - - uap->port.read_status_mask = Rx_OVR; - if (iflag & INPCK) - uap->port.read_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & (BRKINT | PARMRK)) - uap->port.read_status_mask |= BRK_ABRT; - - uap->port.ignore_status_mask = 0; - if (iflag & IGNPAR) - uap->port.ignore_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & IGNBRK) { - uap->port.ignore_status_mask |= BRK_ABRT; - if (iflag & IGNPAR) - uap->port.ignore_status_mask |= Rx_OVR; - } - - if ((cflag & CREAD) == 0) - uap->port.ignore_status_mask = 0xff; -} - - -/* - * Set the irda codec on the imac to the specified baud rate. - */ -static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) -{ - u8 cmdbyte; - int t, version; - - switch (*baud) { - /* SIR modes */ - case 2400: - cmdbyte = 0x53; - break; - case 4800: - cmdbyte = 0x52; - break; - case 9600: - cmdbyte = 0x51; - break; - case 19200: - cmdbyte = 0x50; - break; - case 38400: - cmdbyte = 0x4f; - break; - case 57600: - cmdbyte = 0x4e; - break; - case 115200: - cmdbyte = 0x4d; - break; - /* The FIR modes aren't really supported at this point, how - * do we select the speed ? via the FCR on KeyLargo ? - */ - case 1152000: - cmdbyte = 0; - break; - case 4000000: - cmdbyte = 0; - break; - default: /* 9600 */ - cmdbyte = 0x51; - *baud = 9600; - break; - } - - /* Wait for transmitter to drain */ - t = 10000; - while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0 - || (read_zsreg(uap, R1) & ALL_SNT) == 0) { - if (--t <= 0) { - pmz_error("transmitter didn't drain\n"); - return; - } - udelay(10); - } - - /* Drain the receiver too */ - t = 100; - (void)read_zsdata(uap); - (void)read_zsdata(uap); - (void)read_zsdata(uap); - mdelay(10); - while (read_zsreg(uap, R0) & Rx_CH_AV) { - read_zsdata(uap); - mdelay(10); - if (--t <= 0) { - pmz_error("receiver didn't drain\n"); - return; - } - } - - /* Switch to command mode */ - uap->curregs[R5] |= DTR; - write_zsreg(uap, R5, uap->curregs[R5]); - zssync(uap); - mdelay(1); - - /* Switch SCC to 19200 */ - pmz_convert_to_zs(uap, CS8, 0, 19200); - pmz_load_zsregs(uap, uap->curregs); - mdelay(1); - - /* Write get_version command byte */ - write_zsdata(uap, 1); - t = 5000; - while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { - if (--t <= 0) { - pmz_error("irda_setup timed out on get_version byte\n"); - goto out; - } - udelay(10); - } - version = read_zsdata(uap); - - if (version < 4) { - pmz_info("IrDA: dongle version %d not supported\n", version); - goto out; - } - - /* Send speed mode */ - write_zsdata(uap, cmdbyte); - t = 5000; - while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { - if (--t <= 0) { - pmz_error("irda_setup timed out on speed mode byte\n"); - goto out; - } - udelay(10); - } - t = read_zsdata(uap); - if (t != cmdbyte) - pmz_error("irda_setup speed mode byte = %x (%x)\n", t, cmdbyte); - - pmz_info("IrDA setup for %ld bps, dongle version: %d\n", - *baud, version); - - (void)read_zsdata(uap); - (void)read_zsdata(uap); - (void)read_zsdata(uap); - - out: - /* Switch back to data mode */ - uap->curregs[R5] &= ~DTR; - write_zsreg(uap, R5, uap->curregs[R5]); - zssync(uap); - - (void)read_zsdata(uap); - (void)read_zsdata(uap); - (void)read_zsdata(uap); -} - - -static void __pmz_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned long baud; - - pmz_debug("pmz: set_termios()\n"); - - if (ZS_IS_ASLEEP(uap)) - return; - - memcpy(&uap->termios_cache, termios, sizeof(struct ktermios)); - - /* XXX Check which revs of machines actually allow 1 and 4Mb speeds - * on the IR dongle. Note that the IRTTY driver currently doesn't know - * about the FIR mode and high speed modes. So these are unused. For - * implementing proper support for these, we should probably add some - * DMA as well, at least on the Rx side, which isn't a simple thing - * at this point. - */ - if (ZS_IS_IRDA(uap)) { - /* Calc baud rate */ - baud = uart_get_baud_rate(port, termios, old, 1200, 4000000); - pmz_debug("pmz: switch IRDA to %ld bauds\n", baud); - /* Cet the irda codec to the right rate */ - pmz_irda_setup(uap, &baud); - /* Set final baud rate */ - pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud); - pmz_load_zsregs(uap, uap->curregs); - zssync(uap); - } else { - baud = uart_get_baud_rate(port, termios, old, 1200, 230400); - pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud); - /* Make sure modem status interrupts are correctly configured */ - if (UART_ENABLE_MS(&uap->port, termios->c_cflag)) { - uap->curregs[R15] |= DCDIE | SYNCIE | CTSIE; - uap->flags |= PMACZILOG_FLAG_MODEM_STATUS; - } else { - uap->curregs[R15] &= ~(DCDIE | SYNCIE | CTSIE); - uap->flags &= ~PMACZILOG_FLAG_MODEM_STATUS; - } - - /* Load registers to the chip */ - pmz_maybe_update_regs(uap); - } - uart_update_timeout(port, termios->c_cflag, baud); - - pmz_debug("pmz: set_termios() done.\n"); -} - -/* The port lock is not held. */ -static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_pmac_port *uap = to_pmz(port); - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - /* Disable IRQs on the port */ - uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); - write_zsreg(uap, R1, uap->curregs[R1]); - - /* Setup new port configuration */ - __pmz_set_termios(port, termios, old); - - /* Re-enable IRQs on the port */ - if (ZS_IS_OPEN(uap)) { - uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; - if (!ZS_IS_EXTCLK(uap)) - uap->curregs[R1] |= EXT_INT_ENAB; - write_zsreg(uap, R1, uap->curregs[R1]); - } - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *pmz_type(struct uart_port *port) -{ - struct uart_pmac_port *uap = to_pmz(port); - - if (ZS_IS_IRDA(uap)) - return "Z85c30 ESCC - Infrared port"; - else if (ZS_IS_INTMODEM(uap)) - return "Z85c30 ESCC - Internal modem"; - return "Z85c30 ESCC - Serial port"; -} - -/* We do not request/release mappings of the registers here, this - * happens at early serial probe time. - */ -static void pmz_release_port(struct uart_port *port) -{ -} - -static int pmz_request_port(struct uart_port *port) -{ - return 0; -} - -/* These do not need to do anything interesting either. */ -static void pmz_config_port(struct uart_port *port, int flags) -{ -} - -/* We do not support letting the user mess with the divisor, IRQ, etc. */ -static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -#ifdef CONFIG_CONSOLE_POLL - -static int pmz_poll_get_char(struct uart_port *port) -{ - struct uart_pmac_port *uap = (struct uart_pmac_port *)port; - - while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) - udelay(5); - return read_zsdata(uap); -} - -static void pmz_poll_put_char(struct uart_port *port, unsigned char c) -{ - struct uart_pmac_port *uap = (struct uart_pmac_port *)port; - - /* Wait for the transmit buffer to empty. */ - while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) - udelay(5); - write_zsdata(uap, c); -} - -#endif /* CONFIG_CONSOLE_POLL */ - -static struct uart_ops pmz_pops = { - .tx_empty = pmz_tx_empty, - .set_mctrl = pmz_set_mctrl, - .get_mctrl = pmz_get_mctrl, - .stop_tx = pmz_stop_tx, - .start_tx = pmz_start_tx, - .stop_rx = pmz_stop_rx, - .enable_ms = pmz_enable_ms, - .break_ctl = pmz_break_ctl, - .startup = pmz_startup, - .shutdown = pmz_shutdown, - .set_termios = pmz_set_termios, - .type = pmz_type, - .release_port = pmz_release_port, - .request_port = pmz_request_port, - .config_port = pmz_config_port, - .verify_port = pmz_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = pmz_poll_get_char, - .poll_put_char = pmz_poll_put_char, -#endif -}; - -#ifdef CONFIG_PPC_PMAC - -/* - * Setup one port structure after probing, HW is down at this point, - * Unlike sunzilog, we don't need to pre-init the spinlock as we don't - * register our console before uart_add_one_port() is called - */ -static int __init pmz_init_port(struct uart_pmac_port *uap) -{ - struct device_node *np = uap->node; - const char *conn; - const struct slot_names_prop { - int count; - char name[1]; - } *slots; - int len; - struct resource r_ports, r_rxdma, r_txdma; - - /* - * Request & map chip registers - */ - if (of_address_to_resource(np, 0, &r_ports)) - return -ENODEV; - uap->port.mapbase = r_ports.start; - uap->port.membase = ioremap(uap->port.mapbase, 0x1000); - - uap->control_reg = uap->port.membase; - uap->data_reg = uap->control_reg + 0x10; - - /* - * Request & map DBDMA registers - */ -#ifdef HAS_DBDMA - if (of_address_to_resource(np, 1, &r_txdma) == 0 && - of_address_to_resource(np, 2, &r_rxdma) == 0) - uap->flags |= PMACZILOG_FLAG_HAS_DMA; -#else - memset(&r_txdma, 0, sizeof(struct resource)); - memset(&r_rxdma, 0, sizeof(struct resource)); -#endif - if (ZS_HAS_DMA(uap)) { - uap->tx_dma_regs = ioremap(r_txdma.start, 0x100); - if (uap->tx_dma_regs == NULL) { - uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; - goto no_dma; - } - uap->rx_dma_regs = ioremap(r_rxdma.start, 0x100); - if (uap->rx_dma_regs == NULL) { - iounmap(uap->tx_dma_regs); - uap->tx_dma_regs = NULL; - uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; - goto no_dma; - } - uap->tx_dma_irq = irq_of_parse_and_map(np, 1); - uap->rx_dma_irq = irq_of_parse_and_map(np, 2); - } -no_dma: - - /* - * Detect port type - */ - if (of_device_is_compatible(np, "cobalt")) - uap->flags |= PMACZILOG_FLAG_IS_INTMODEM; - conn = of_get_property(np, "AAPL,connector", &len); - if (conn && (strcmp(conn, "infrared") == 0)) - uap->flags |= PMACZILOG_FLAG_IS_IRDA; - uap->port_type = PMAC_SCC_ASYNC; - /* 1999 Powerbook G3 has slot-names property instead */ - slots = of_get_property(np, "slot-names", &len); - if (slots && slots->count > 0) { - if (strcmp(slots->name, "IrDA") == 0) - uap->flags |= PMACZILOG_FLAG_IS_IRDA; - else if (strcmp(slots->name, "Modem") == 0) - uap->flags |= PMACZILOG_FLAG_IS_INTMODEM; - } - if (ZS_IS_IRDA(uap)) - uap->port_type = PMAC_SCC_IRDA; - if (ZS_IS_INTMODEM(uap)) { - struct device_node* i2c_modem = - of_find_node_by_name(NULL, "i2c-modem"); - if (i2c_modem) { - const char* mid = - of_get_property(i2c_modem, "modem-id", NULL); - if (mid) switch(*mid) { - case 0x04 : - case 0x05 : - case 0x07 : - case 0x08 : - case 0x0b : - case 0x0c : - uap->port_type = PMAC_SCC_I2S1; - } - printk(KERN_INFO "pmac_zilog: i2c-modem detected, id: %d\n", - mid ? (*mid) : 0); - of_node_put(i2c_modem); - } else { - printk(KERN_INFO "pmac_zilog: serial modem detected\n"); - } - } - - /* - * Init remaining bits of "port" structure - */ - uap->port.iotype = UPIO_MEM; - uap->port.irq = irq_of_parse_and_map(np, 0); - uap->port.uartclk = ZS_CLOCK; - uap->port.fifosize = 1; - uap->port.ops = &pmz_pops; - uap->port.type = PORT_PMAC_ZILOG; - uap->port.flags = 0; - - /* - * Fixup for the port on Gatwick for which the device-tree has - * missing interrupts. Normally, the macio_dev would contain - * fixed up interrupt info, but we use the device-tree directly - * here due to early probing so we need the fixup too. - */ - if (uap->port.irq == NO_IRQ && - np->parent && np->parent->parent && - of_device_is_compatible(np->parent->parent, "gatwick")) { - /* IRQs on gatwick are offset by 64 */ - uap->port.irq = irq_create_mapping(NULL, 64 + 15); - uap->tx_dma_irq = irq_create_mapping(NULL, 64 + 4); - uap->rx_dma_irq = irq_create_mapping(NULL, 64 + 5); - } - - /* Setup some valid baud rate information in the register - * shadows so we don't write crap there before baud rate is - * first initialized. - */ - pmz_convert_to_zs(uap, CS8, 0, 9600); - - return 0; -} - -/* - * Get rid of a port on module removal - */ -static void pmz_dispose_port(struct uart_pmac_port *uap) -{ - struct device_node *np; - - np = uap->node; - iounmap(uap->rx_dma_regs); - iounmap(uap->tx_dma_regs); - iounmap(uap->control_reg); - uap->node = NULL; - of_node_put(np); - memset(uap, 0, sizeof(struct uart_pmac_port)); -} - -/* - * Called upon match with an escc node in the device-tree. - */ -static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) -{ - int i; - - /* Iterate the pmz_ports array to find a matching entry - */ - for (i = 0; i < MAX_ZS_PORTS; i++) - if (pmz_ports[i].node == mdev->ofdev.dev.of_node) { - struct uart_pmac_port *uap = &pmz_ports[i]; - - uap->dev = mdev; - dev_set_drvdata(&mdev->ofdev.dev, uap); - if (macio_request_resources(uap->dev, "pmac_zilog")) - printk(KERN_WARNING "%s: Failed to request resource" - ", port still active\n", - uap->node->name); - else - uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED; - return 0; - } - return -ENODEV; -} - -/* - * That one should not be called, macio isn't really a hotswap device, - * we don't expect one of those serial ports to go away... - */ -static int pmz_detach(struct macio_dev *mdev) -{ - struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); - - if (!uap) - return -ENODEV; - - if (uap->flags & PMACZILOG_FLAG_RSRC_REQUESTED) { - macio_release_resources(uap->dev); - uap->flags &= ~PMACZILOG_FLAG_RSRC_REQUESTED; - } - dev_set_drvdata(&mdev->ofdev.dev, NULL); - uap->dev = NULL; - - return 0; -} - - -static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) -{ - struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); - struct uart_state *state; - unsigned long flags; - - if (uap == NULL) { - printk("HRM... pmz_suspend with NULL uap\n"); - return 0; - } - - if (pm_state.event == mdev->ofdev.dev.power.power_state.event) - return 0; - - pmz_debug("suspend, switching to state %d\n", pm_state.event); - - state = pmz_uart_reg.state + uap->port.line; - - mutex_lock(&pmz_irq_mutex); - mutex_lock(&state->port.mutex); - - spin_lock_irqsave(&uap->port.lock, flags); - - if (ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)) { - /* Disable receiver and transmitter. */ - uap->curregs[R3] &= ~RxENABLE; - uap->curregs[R5] &= ~TxENABLE; - - /* Disable all interrupts and BRK assertion. */ - uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); - uap->curregs[R5] &= ~SND_BRK; - pmz_load_zsregs(uap, uap->curregs); - uap->flags |= PMACZILOG_FLAG_IS_ASLEEP; - mb(); - } - - spin_unlock_irqrestore(&uap->port.lock, flags); - - if (ZS_IS_OPEN(uap) || ZS_IS_OPEN(uap->mate)) - if (ZS_IS_ASLEEP(uap->mate) && ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { - pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON; - disable_irq(uap->port.irq); - } - - if (ZS_IS_CONS(uap)) - uap->port.cons->flags &= ~CON_ENABLED; - - /* Shut the chip down */ - pmz_set_scc_power(uap, 0); - - mutex_unlock(&state->port.mutex); - mutex_unlock(&pmz_irq_mutex); - - pmz_debug("suspend, switching complete\n"); - - mdev->ofdev.dev.power.power_state = pm_state; - - return 0; -} - - -static int pmz_resume(struct macio_dev *mdev) -{ - struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); - struct uart_state *state; - unsigned long flags; - int pwr_delay = 0; - - if (uap == NULL) - return 0; - - if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON) - return 0; - - pmz_debug("resume, switching to state 0\n"); - - state = pmz_uart_reg.state + uap->port.line; - - mutex_lock(&pmz_irq_mutex); - mutex_lock(&state->port.mutex); - - spin_lock_irqsave(&uap->port.lock, flags); - if (!ZS_IS_OPEN(uap) && !ZS_IS_CONS(uap)) { - spin_unlock_irqrestore(&uap->port.lock, flags); - goto bail; - } - pwr_delay = __pmz_startup(uap); - - /* Take care of config that may have changed while asleep */ - __pmz_set_termios(&uap->port, &uap->termios_cache, NULL); - - if (ZS_IS_OPEN(uap)) { - /* Enable interrupts */ - uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; - if (!ZS_IS_EXTCLK(uap)) - uap->curregs[R1] |= EXT_INT_ENAB; - write_zsreg(uap, R1, uap->curregs[R1]); - } - - spin_unlock_irqrestore(&uap->port.lock, flags); - - if (ZS_IS_CONS(uap)) - uap->port.cons->flags |= CON_ENABLED; - - /* Re-enable IRQ on the controller */ - if (ZS_IS_OPEN(uap) && !ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { - pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; - enable_irq(uap->port.irq); - } - - bail: - mutex_unlock(&state->port.mutex); - mutex_unlock(&pmz_irq_mutex); - - /* Right now, we deal with delay by blocking here, I'll be - * smarter later on - */ - if (pwr_delay != 0) { - pmz_debug("pmz: delaying %d ms\n", pwr_delay); - msleep(pwr_delay); - } - - pmz_debug("resume, switching complete\n"); - - mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON; - - return 0; -} - -/* - * Probe all ports in the system and build the ports array, we register - * with the serial layer at this point, the macio-type probing is only - * used later to "attach" to the sysfs tree so we get power management - * events - */ -static int __init pmz_probe(void) -{ - struct device_node *node_p, *node_a, *node_b, *np; - int count = 0; - int rc; - - /* - * Find all escc chips in the system - */ - node_p = of_find_node_by_name(NULL, "escc"); - while (node_p) { - /* - * First get channel A/B node pointers - * - * TODO: Add routines with proper locking to do that... - */ - node_a = node_b = NULL; - for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) { - if (strncmp(np->name, "ch-a", 4) == 0) - node_a = of_node_get(np); - else if (strncmp(np->name, "ch-b", 4) == 0) - node_b = of_node_get(np); - } - if (!node_a && !node_b) { - of_node_put(node_a); - of_node_put(node_b); - printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n", - (!node_a) ? 'a' : 'b', node_p->full_name); - goto next; - } - - /* - * Fill basic fields in the port structures - */ - pmz_ports[count].mate = &pmz_ports[count+1]; - pmz_ports[count+1].mate = &pmz_ports[count]; - pmz_ports[count].flags = PMACZILOG_FLAG_IS_CHANNEL_A; - pmz_ports[count].node = node_a; - pmz_ports[count+1].node = node_b; - pmz_ports[count].port.line = count; - pmz_ports[count+1].port.line = count+1; - - /* - * Setup the ports for real - */ - rc = pmz_init_port(&pmz_ports[count]); - if (rc == 0 && node_b != NULL) - rc = pmz_init_port(&pmz_ports[count+1]); - if (rc != 0) { - of_node_put(node_a); - of_node_put(node_b); - memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port)); - memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port)); - goto next; - } - count += 2; -next: - node_p = of_find_node_by_name(node_p, "escc"); - } - pmz_ports_count = count; - - return 0; -} - -#else - -extern struct platform_device scc_a_pdev, scc_b_pdev; - -static int __init pmz_init_port(struct uart_pmac_port *uap) -{ - struct resource *r_ports; - int irq; - - r_ports = platform_get_resource(uap->node, IORESOURCE_MEM, 0); - irq = platform_get_irq(uap->node, 0); - if (!r_ports || !irq) - return -ENODEV; - - uap->port.mapbase = r_ports->start; - uap->port.membase = (unsigned char __iomem *) r_ports->start; - uap->port.iotype = UPIO_MEM; - uap->port.irq = irq; - uap->port.uartclk = ZS_CLOCK; - uap->port.fifosize = 1; - uap->port.ops = &pmz_pops; - uap->port.type = PORT_PMAC_ZILOG; - uap->port.flags = 0; - - uap->control_reg = uap->port.membase; - uap->data_reg = uap->control_reg + 4; - uap->port_type = 0; - - pmz_convert_to_zs(uap, CS8, 0, 9600); - - return 0; -} - -static int __init pmz_probe(void) -{ - int err; - - pmz_ports_count = 0; - - pmz_ports[0].mate = &pmz_ports[1]; - pmz_ports[0].port.line = 0; - pmz_ports[0].flags = PMACZILOG_FLAG_IS_CHANNEL_A; - pmz_ports[0].node = &scc_a_pdev; - err = pmz_init_port(&pmz_ports[0]); - if (err) - return err; - pmz_ports_count++; - - pmz_ports[1].mate = &pmz_ports[0]; - pmz_ports[1].port.line = 1; - pmz_ports[1].flags = 0; - pmz_ports[1].node = &scc_b_pdev; - err = pmz_init_port(&pmz_ports[1]); - if (err) - return err; - pmz_ports_count++; - - return 0; -} - -static void pmz_dispose_port(struct uart_pmac_port *uap) -{ - memset(uap, 0, sizeof(struct uart_pmac_port)); -} - -static int __init pmz_attach(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < pmz_ports_count; i++) - if (pmz_ports[i].node == pdev) - return 0; - return -ENODEV; -} - -static int __exit pmz_detach(struct platform_device *pdev) -{ - return 0; -} - -#endif /* !CONFIG_PPC_PMAC */ - -#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE - -static void pmz_console_write(struct console *con, const char *s, unsigned int count); -static int __init pmz_console_setup(struct console *co, char *options); - -static struct console pmz_console = { - .name = PMACZILOG_NAME, - .write = pmz_console_write, - .device = uart_console_device, - .setup = pmz_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &pmz_uart_reg, -}; - -#define PMACZILOG_CONSOLE &pmz_console -#else /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ -#define PMACZILOG_CONSOLE (NULL) -#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ - -/* - * Register the driver, console driver and ports with the serial - * core - */ -static int __init pmz_register(void) -{ - int i, rc; - - pmz_uart_reg.nr = pmz_ports_count; - pmz_uart_reg.cons = PMACZILOG_CONSOLE; - - /* - * Register this driver with the serial core - */ - rc = uart_register_driver(&pmz_uart_reg); - if (rc) - return rc; - - /* - * Register each port with the serial core - */ - for (i = 0; i < pmz_ports_count; i++) { - struct uart_pmac_port *uport = &pmz_ports[i]; - /* NULL node may happen on wallstreet */ - if (uport->node != NULL) - rc = uart_add_one_port(&pmz_uart_reg, &uport->port); - if (rc) - goto err_out; - } - - return 0; -err_out: - while (i-- > 0) { - struct uart_pmac_port *uport = &pmz_ports[i]; - uart_remove_one_port(&pmz_uart_reg, &uport->port); - } - uart_unregister_driver(&pmz_uart_reg); - return rc; -} - -#ifdef CONFIG_PPC_PMAC - -static struct of_device_id pmz_match[] = -{ - { - .name = "ch-a", - }, - { - .name = "ch-b", - }, - {}, -}; -MODULE_DEVICE_TABLE (of, pmz_match); - -static struct macio_driver pmz_driver = { - .driver = { - .name = "pmac_zilog", - .owner = THIS_MODULE, - .of_match_table = pmz_match, - }, - .probe = pmz_attach, - .remove = pmz_detach, - .suspend = pmz_suspend, - .resume = pmz_resume, -}; - -#else - -static struct platform_driver pmz_driver = { - .remove = __exit_p(pmz_detach), - .driver = { - .name = "scc", - .owner = THIS_MODULE, - }, -}; - -#endif /* !CONFIG_PPC_PMAC */ - -static int __init init_pmz(void) -{ - int rc, i; - printk(KERN_INFO "%s\n", version); - - /* - * First, we need to do a direct OF-based probe pass. We - * do that because we want serial console up before the - * macio stuffs calls us back, and since that makes it - * easier to pass the proper number of channels to - * uart_register_driver() - */ - if (pmz_ports_count == 0) - pmz_probe(); - - /* - * Bail early if no port found - */ - if (pmz_ports_count == 0) - return -ENODEV; - - /* - * Now we register with the serial layer - */ - rc = pmz_register(); - if (rc) { - printk(KERN_ERR - "pmac_zilog: Error registering serial device, disabling pmac_zilog.\n" - "pmac_zilog: Did another serial driver already claim the minors?\n"); - /* effectively "pmz_unprobe()" */ - for (i=0; i < pmz_ports_count; i++) - pmz_dispose_port(&pmz_ports[i]); - return rc; - } - - /* - * Then we register the macio driver itself - */ -#ifdef CONFIG_PPC_PMAC - return macio_register_driver(&pmz_driver); -#else - return platform_driver_probe(&pmz_driver, pmz_attach); -#endif -} - -static void __exit exit_pmz(void) -{ - int i; - -#ifdef CONFIG_PPC_PMAC - /* Get rid of macio-driver (detach from macio) */ - macio_unregister_driver(&pmz_driver); -#else - platform_driver_unregister(&pmz_driver); -#endif - - for (i = 0; i < pmz_ports_count; i++) { - struct uart_pmac_port *uport = &pmz_ports[i]; - if (uport->node != NULL) { - uart_remove_one_port(&pmz_uart_reg, &uport->port); - pmz_dispose_port(uport); - } - } - /* Unregister UART driver */ - uart_unregister_driver(&pmz_uart_reg); -} - -#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE - -static void pmz_console_putchar(struct uart_port *port, int ch) -{ - struct uart_pmac_port *uap = (struct uart_pmac_port *)port; - - /* Wait for the transmit buffer to empty. */ - while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) - udelay(5); - write_zsdata(uap, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - */ -static void pmz_console_write(struct console *con, const char *s, unsigned int count) -{ - struct uart_pmac_port *uap = &pmz_ports[con->index]; - unsigned long flags; - - if (ZS_IS_ASLEEP(uap)) - return; - spin_lock_irqsave(&uap->port.lock, flags); - - /* Turn of interrupts and enable the transmitter. */ - write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); - write_zsreg(uap, R5, uap->curregs[5] | TxENABLE | RTS | DTR); - - uart_console_write(&uap->port, s, count, pmz_console_putchar); - - /* Restore the values in the registers. */ - write_zsreg(uap, R1, uap->curregs[1]); - /* Don't disable the transmitter. */ - - spin_unlock_irqrestore(&uap->port.lock, flags); -} - -/* - * Setup the serial console - */ -static int __init pmz_console_setup(struct console *co, char *options) -{ - struct uart_pmac_port *uap; - struct uart_port *port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - unsigned long pwr_delay; - - /* - * XServe's default to 57600 bps - */ - if (of_machine_is_compatible("RackMac1,1") - || of_machine_is_compatible("RackMac1,2") - || of_machine_is_compatible("MacRISC4")) - baud = 57600; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= pmz_ports_count) - co->index = 0; - uap = &pmz_ports[co->index]; - if (uap->node == NULL) - return -ENODEV; - port = &uap->port; - - /* - * Mark port as beeing a console - */ - uap->flags |= PMACZILOG_FLAG_IS_CONS; - - /* - * Temporary fix for uart layer who didn't setup the spinlock yet - */ - spin_lock_init(&port->lock); - - /* - * Enable the hardware - */ - pwr_delay = __pmz_startup(uap); - if (pwr_delay) - mdelay(pwr_delay); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static int __init pmz_console_init(void) -{ - /* Probe ports */ - pmz_probe(); - - /* TODO: Autoprobe console based on OF */ - /* pmz_console.index = i; */ - register_console(&pmz_console); - - return 0; - -} -console_initcall(pmz_console_init); -#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ - -module_init(init_pmz); -module_exit(exit_pmz); diff --git a/drivers/serial/pmac_zilog.h b/drivers/serial/pmac_zilog.h deleted file mode 100644 index cbc34fbb1b20..000000000000 --- a/drivers/serial/pmac_zilog.h +++ /dev/null @@ -1,396 +0,0 @@ -#ifndef __PMAC_ZILOG_H__ -#define __PMAC_ZILOG_H__ - -#ifdef CONFIG_PPC_PMAC -#define pmz_debug(fmt, arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg) -#define pmz_error(fmt, arg...) dev_err(&uap->dev->ofdev.dev, fmt, ## arg) -#define pmz_info(fmt, arg...) dev_info(&uap->dev->ofdev.dev, fmt, ## arg) -#else -#define pmz_debug(fmt, arg...) dev_dbg(&uap->node->dev, fmt, ## arg) -#define pmz_error(fmt, arg...) dev_err(&uap->node->dev, fmt, ## arg) -#define pmz_info(fmt, arg...) dev_info(&uap->node->dev, fmt, ## arg) -#endif - -/* - * At most 2 ESCCs with 2 ports each - */ -#define MAX_ZS_PORTS 4 - -/* - * We wrap our port structure around the generic uart_port. - */ -#define NUM_ZSREGS 17 - -struct uart_pmac_port { - struct uart_port port; - struct uart_pmac_port *mate; - -#ifdef CONFIG_PPC_PMAC - /* macio_dev for the escc holding this port (maybe be null on - * early inited port) - */ - struct macio_dev *dev; - /* device node to this port, this points to one of 2 childs - * of "escc" node (ie. ch-a or ch-b) - */ - struct device_node *node; -#else - struct platform_device *node; -#endif - - /* Port type as obtained from device tree (IRDA, modem, ...) */ - int port_type; - u8 curregs[NUM_ZSREGS]; - - unsigned int flags; -#define PMACZILOG_FLAG_IS_CONS 0x00000001 -#define PMACZILOG_FLAG_IS_KGDB 0x00000002 -#define PMACZILOG_FLAG_MODEM_STATUS 0x00000004 -#define PMACZILOG_FLAG_IS_CHANNEL_A 0x00000008 -#define PMACZILOG_FLAG_REGS_HELD 0x00000010 -#define PMACZILOG_FLAG_TX_STOPPED 0x00000020 -#define PMACZILOG_FLAG_TX_ACTIVE 0x00000040 -#define PMACZILOG_FLAG_ENABLED 0x00000080 -#define PMACZILOG_FLAG_IS_IRDA 0x00000100 -#define PMACZILOG_FLAG_IS_INTMODEM 0x00000200 -#define PMACZILOG_FLAG_HAS_DMA 0x00000400 -#define PMACZILOG_FLAG_RSRC_REQUESTED 0x00000800 -#define PMACZILOG_FLAG_IS_ASLEEP 0x00001000 -#define PMACZILOG_FLAG_IS_OPEN 0x00002000 -#define PMACZILOG_FLAG_IS_IRQ_ON 0x00004000 -#define PMACZILOG_FLAG_IS_EXTCLK 0x00008000 -#define PMACZILOG_FLAG_BREAK 0x00010000 - - unsigned char parity_mask; - unsigned char prev_status; - - volatile u8 __iomem *control_reg; - volatile u8 __iomem *data_reg; - -#ifdef CONFIG_PPC_PMAC - unsigned int tx_dma_irq; - unsigned int rx_dma_irq; - volatile struct dbdma_regs __iomem *tx_dma_regs; - volatile struct dbdma_regs __iomem *rx_dma_regs; -#endif - - struct ktermios termios_cache; -}; - -#define to_pmz(p) ((struct uart_pmac_port *)(p)) - -static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap) -{ - if (uap->flags & PMACZILOG_FLAG_IS_CHANNEL_A) - return uap; - return uap->mate; -} - -/* - * Register accessors. Note that we don't need to enforce a recovery - * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip, - * though if we try to use this driver on older machines, we might have - * to add it back - */ -static inline u8 read_zsreg(struct uart_pmac_port *port, u8 reg) -{ - if (reg != 0) - writeb(reg, port->control_reg); - return readb(port->control_reg); -} - -static inline void write_zsreg(struct uart_pmac_port *port, u8 reg, u8 value) -{ - if (reg != 0) - writeb(reg, port->control_reg); - writeb(value, port->control_reg); -} - -static inline u8 read_zsdata(struct uart_pmac_port *port) -{ - return readb(port->data_reg); -} - -static inline void write_zsdata(struct uart_pmac_port *port, u8 data) -{ - writeb(data, port->data_reg); -} - -static inline void zssync(struct uart_pmac_port *port) -{ - (void)readb(port->control_reg); -} - -/* Conversion routines to/from brg time constants from/to bits - * per second. - */ -#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) -#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) - -#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ - -/* The Zilog register set */ - -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 -#define R7P 16 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ -#define RxINT_MASK 0x18 - -#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */ -#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */ -#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ -#define RxN_MASK 0xc0 - -/* Write Register 4 */ - -#define PAR_ENAB 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ -#define SB_MASK 0xc - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ -#define XCLK_MASK 0xC0 - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENABLE 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define TxN_MASK 0x60 -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 7' (Some enhanced feature control) */ -#define ENEXREAD 0x40 /* Enable read of some write registers */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENAB 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define EN85C30 1 /* Enable some 85c30-enhanced registers */ -#define ZCIE 2 /* Zero count IE */ -#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ -#define CHB_Tx_EMPTY 0x00 -#define CHB_EXT_STAT 0x02 -#define CHB_Rx_AVAIL 0x04 -#define CHB_SPECIAL 0x06 -#define CHA_Tx_EMPTY 0x08 -#define CHA_EXT_STAT 0x0a -#define CHA_Rx_AVAIL 0x0c -#define CHA_SPECIAL 0x0e -#define STATUS_MASK 0x06 - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Misc macros */ -#define ZS_CLEARERR(port) (write_zsreg(port, 0, ERR_RES)) -#define ZS_CLEARFIFO(port) do { volatile unsigned char garbage; \ - garbage = read_zsdata(port); \ - garbage = read_zsdata(port); \ - garbage = read_zsdata(port); \ - } while(0) - -#define ZS_IS_CONS(UP) ((UP)->flags & PMACZILOG_FLAG_IS_CONS) -#define ZS_IS_KGDB(UP) ((UP)->flags & PMACZILOG_FLAG_IS_KGDB) -#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & PMACZILOG_FLAG_IS_CHANNEL_A) -#define ZS_REGS_HELD(UP) ((UP)->flags & PMACZILOG_FLAG_REGS_HELD) -#define ZS_TX_STOPPED(UP) ((UP)->flags & PMACZILOG_FLAG_TX_STOPPED) -#define ZS_TX_ACTIVE(UP) ((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE) -#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS) -#define ZS_IS_IRDA(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA) -#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM) -#define ZS_HAS_DMA(UP) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA) -#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) -#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) -#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) -#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK) - -#endif /* __PMAC_ZILOG_H__ */ diff --git a/drivers/serial/pnx8xxx_uart.c b/drivers/serial/pnx8xxx_uart.c deleted file mode 100644 index 0aa75a97531c..000000000000 --- a/drivers/serial/pnx8xxx_uart.c +++ /dev/null @@ -1,854 +0,0 @@ -/* - * UART driver for PNX8XXX SoCs - * - * Author: Per Hallsmark per.hallsmark@mvista.com - * Ported to 2.6 kernel by EmbeddedAlley - * Reworked by Vitaly Wool - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of - * any kind, whether express or implied. - * - */ - -#if defined(CONFIG_SERIAL_PNX8XXX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* We'll be using StrongARM sa1100 serial port major/minor */ -#define SERIAL_PNX8XXX_MAJOR 204 -#define MINOR_START 5 - -#define NR_PORTS 2 - -#define PNX8XXX_ISR_PASS_LIMIT 256 - -/* - * Convert from ignore_status_mask or read_status_mask to FIFO - * and interrupt status bits - */ -#define SM_TO_FIFO(x) ((x) >> 10) -#define SM_TO_ISTAT(x) ((x) & 0x000001ff) -#define FIFO_TO_SM(x) ((x) << 10) -#define ISTAT_TO_SM(x) ((x) & 0x000001ff) - -/* - * This is the size of our serial port register set. - */ -#define UART_PORT_SIZE 0x1000 - -/* - * This determines how often we check the modem status signals - * for any change. They generally aren't connected to an IRQ - * so we have to poll them. We also check immediately before - * filling the TX fifo incase CTS has been dropped. - */ -#define MCTRL_TIMEOUT (250*HZ/1000) - -extern struct pnx8xxx_port pnx8xxx_ports[]; - -static inline int serial_in(struct pnx8xxx_port *sport, int offset) -{ - return (__raw_readl(sport->port.membase + offset)); -} - -static inline void serial_out(struct pnx8xxx_port *sport, int offset, int value) -{ - __raw_writel(value, sport->port.membase + offset); -} - -/* - * Handle any change of modem status signal since we were last called. - */ -static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport) -{ - unsigned int status, changed; - - status = sport->port.ops->get_mctrl(&sport->port); - changed = status ^ sport->old_status; - - if (changed == 0) - return; - - sport->old_status = status; - - if (changed & TIOCM_RI) - sport->port.icount.rng++; - if (changed & TIOCM_DSR) - sport->port.icount.dsr++; - if (changed & TIOCM_CAR) - uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); - if (changed & TIOCM_CTS) - uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - - wake_up_interruptible(&sport->port.state->port.delta_msr_wait); -} - -/* - * This is our per-port timeout handler, for checking the - * modem status signals. - */ -static void pnx8xxx_timeout(unsigned long data) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data; - unsigned long flags; - - if (sport->port.state) { - spin_lock_irqsave(&sport->port.lock, flags); - pnx8xxx_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); - - mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); - } -} - -/* - * interrupts disabled on entry - */ -static void pnx8xxx_stop_tx(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - u32 ien; - - /* Disable TX intr */ - ien = serial_in(sport, PNX8XXX_IEN); - serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLTX); - - /* Clear all pending TX intr */ - serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); -} - -/* - * interrupts may not be disabled on entry - */ -static void pnx8xxx_start_tx(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - u32 ien; - - /* Clear all pending TX intr */ - serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); - - /* Enable TX intr */ - ien = serial_in(sport, PNX8XXX_IEN); - serial_out(sport, PNX8XXX_IEN, ien | PNX8XXX_UART_INT_ALLTX); -} - -/* - * Interrupts enabled - */ -static void pnx8xxx_stop_rx(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - u32 ien; - - /* Disable RX intr */ - ien = serial_in(sport, PNX8XXX_IEN); - serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLRX); - - /* Clear all pending RX intr */ - serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX); -} - -/* - * Set the modem control timer to fire immediately. - */ -static void pnx8xxx_enable_ms(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - - mod_timer(&sport->timer, jiffies); -} - -static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) -{ - struct tty_struct *tty = sport->port.state->port.tty; - unsigned int status, ch, flg; - - status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | - ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); - while (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFIFO)) { - ch = serial_in(sport, PNX8XXX_FIFO) & 0xff; - - sport->port.icount.rx++; - - flg = TTY_NORMAL; - - /* - * note that the error handling code is - * out of the main execution path - */ - if (status & (FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE | - PNX8XXX_UART_FIFO_RXPAR | - PNX8XXX_UART_FIFO_RXBRK) | - ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))) { - if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXBRK)) { - status &= ~(FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | - FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)); - sport->port.icount.brk++; - if (uart_handle_break(&sport->port)) - goto ignore_char; - } else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) - sport->port.icount.parity++; - else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) - sport->port.icount.frame++; - if (status & ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN)) - sport->port.icount.overrun++; - - status &= sport->port.read_status_mask; - - if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) - flg = TTY_PARITY; - else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) - flg = TTY_FRAME; - -#ifdef SUPPORT_SYSRQ - sport->port.sysrq = 0; -#endif - } - - if (uart_handle_sysrq_char(&sport->port, ch)) - goto ignore_char; - - uart_insert_char(&sport->port, status, - ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN), ch, flg); - - ignore_char: - serial_out(sport, PNX8XXX_LCR, serial_in(sport, PNX8XXX_LCR) | - PNX8XXX_UART_LCR_RX_NEXT); - status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | - ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); - } - tty_flip_buffer_push(tty); -} - -static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - - if (sport->port.x_char) { - serial_out(sport, PNX8XXX_FIFO, sport->port.x_char); - sport->port.icount.tx++; - sport->port.x_char = 0; - return; - } - - /* - * Check the modem control lines before - * transmitting anything. - */ - pnx8xxx_mctrl_check(sport); - - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - pnx8xxx_stop_tx(&sport->port); - return; - } - - /* - * TX while bytes available - */ - while (((serial_in(sport, PNX8XXX_FIFO) & - PNX8XXX_UART_FIFO_TXFIFO) >> 16) < 16) { - serial_out(sport, PNX8XXX_FIFO, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - - if (uart_circ_empty(xmit)) - pnx8xxx_stop_tx(&sport->port); -} - -static irqreturn_t pnx8xxx_int(int irq, void *dev_id) -{ - struct pnx8xxx_port *sport = dev_id; - unsigned int status; - - spin_lock(&sport->port.lock); - /* Get the interrupts */ - status = serial_in(sport, PNX8XXX_ISTAT) & serial_in(sport, PNX8XXX_IEN); - - /* Byte or break signal received */ - if (status & (PNX8XXX_UART_INT_RX | PNX8XXX_UART_INT_BREAK)) - pnx8xxx_rx_chars(sport); - - /* TX holding register empty - transmit a byte */ - if (status & PNX8XXX_UART_INT_TX) - pnx8xxx_tx_chars(sport); - - /* Clear the ISTAT register */ - serial_out(sport, PNX8XXX_ICLR, status); - - spin_unlock(&sport->port.lock); - return IRQ_HANDLED; -} - -/* - * Return TIOCSER_TEMT when transmitter is not busy. - */ -static unsigned int pnx8xxx_tx_empty(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - - return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT; -} - -static unsigned int pnx8xxx_get_mctrl(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - unsigned int mctrl = TIOCM_DSR; - unsigned int msr; - - /* REVISIT */ - - msr = serial_in(sport, PNX8XXX_MCR); - - mctrl |= msr & PNX8XXX_UART_MCR_CTS ? TIOCM_CTS : 0; - mctrl |= msr & PNX8XXX_UART_MCR_DCD ? TIOCM_CAR : 0; - - return mctrl; -} - -static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -#if 0 /* FIXME */ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - unsigned int msr; -#endif -} - -/* - * Interrupts always disabled. - */ -static void pnx8xxx_break_ctl(struct uart_port *port, int break_state) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - unsigned long flags; - unsigned int lcr; - - spin_lock_irqsave(&sport->port.lock, flags); - lcr = serial_in(sport, PNX8XXX_LCR); - if (break_state == -1) - lcr |= PNX8XXX_UART_LCR_TXBREAK; - else - lcr &= ~PNX8XXX_UART_LCR_TXBREAK; - serial_out(sport, PNX8XXX_LCR, lcr); - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static int pnx8xxx_startup(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - int retval; - - /* - * Allocate the IRQ - */ - retval = request_irq(sport->port.irq, pnx8xxx_int, 0, - "pnx8xxx-uart", sport); - if (retval) - return retval; - - /* - * Finally, clear and enable interrupts - */ - - serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | - PNX8XXX_UART_INT_ALLTX); - - serial_out(sport, PNX8XXX_IEN, serial_in(sport, PNX8XXX_IEN) | - PNX8XXX_UART_INT_ALLRX | - PNX8XXX_UART_INT_ALLTX); - - /* - * Enable modem status interrupts - */ - spin_lock_irq(&sport->port.lock); - pnx8xxx_enable_ms(&sport->port); - spin_unlock_irq(&sport->port.lock); - - return 0; -} - -static void pnx8xxx_shutdown(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - int lcr; - - /* - * Stop our timer. - */ - del_timer_sync(&sport->timer); - - /* - * Disable all interrupts - */ - serial_out(sport, PNX8XXX_IEN, 0); - - /* - * Reset the Tx and Rx FIFOS, disable the break condition - */ - lcr = serial_in(sport, PNX8XXX_LCR); - lcr &= ~PNX8XXX_UART_LCR_TXBREAK; - lcr |= PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST; - serial_out(sport, PNX8XXX_LCR, lcr); - - /* - * Clear all interrupts - */ - serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | - PNX8XXX_UART_INT_ALLTX); - - /* - * Free the interrupt - */ - free_irq(sport->port.irq, sport); -} - -static void -pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - unsigned long flags; - unsigned int lcr_fcr, old_ien, baud, quot; - unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - - /* - * We only support CS7 and CS8. - */ - while ((termios->c_cflag & CSIZE) != CS7 && - (termios->c_cflag & CSIZE) != CS8) { - termios->c_cflag &= ~CSIZE; - termios->c_cflag |= old_csize; - old_csize = CS8; - } - - if ((termios->c_cflag & CSIZE) == CS8) - lcr_fcr = PNX8XXX_UART_LCR_8BIT; - else - lcr_fcr = 0; - - if (termios->c_cflag & CSTOPB) - lcr_fcr |= PNX8XXX_UART_LCR_2STOPB; - if (termios->c_cflag & PARENB) { - lcr_fcr |= PNX8XXX_UART_LCR_PAREN; - if (!(termios->c_cflag & PARODD)) - lcr_fcr |= PNX8XXX_UART_LCR_PAREVN; - } - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - spin_lock_irqsave(&sport->port.lock, flags); - - sport->port.read_status_mask = ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN) | - ISTAT_TO_SM(PNX8XXX_UART_INT_EMPTY) | - ISTAT_TO_SM(PNX8XXX_UART_INT_RX); - if (termios->c_iflag & INPCK) - sport->port.read_status_mask |= - FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | - FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); - if (termios->c_iflag & (BRKINT | PARMRK)) - sport->port.read_status_mask |= - ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); - - /* - * Characters to ignore - */ - sport->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= - FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | - FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); - if (termios->c_iflag & IGNBRK) { - sport->port.ignore_status_mask |= - ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= - ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN); - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - sport->port.ignore_status_mask |= - ISTAT_TO_SM(PNX8XXX_UART_INT_RX); - - del_timer_sync(&sport->timer); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * disable interrupts and drain transmitter - */ - old_ien = serial_in(sport, PNX8XXX_IEN); - serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX | - PNX8XXX_UART_INT_ALLRX)); - - while (serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA) - barrier(); - - /* then, disable everything */ - serial_out(sport, PNX8XXX_IEN, 0); - - /* Reset the Rx and Tx FIFOs too */ - lcr_fcr |= PNX8XXX_UART_LCR_TX_RST; - lcr_fcr |= PNX8XXX_UART_LCR_RX_RST; - - /* set the parity, stop bits and data size */ - serial_out(sport, PNX8XXX_LCR, lcr_fcr); - - /* set the baud rate */ - quot -= 1; - serial_out(sport, PNX8XXX_BAUD, quot); - - serial_out(sport, PNX8XXX_ICLR, -1); - - serial_out(sport, PNX8XXX_IEN, old_ien); - - if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) - pnx8xxx_enable_ms(&sport->port); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static const char *pnx8xxx_type(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - - return sport->port.type == PORT_PNX8XXX ? "PNX8XXX" : NULL; -} - -/* - * Release the memory region(s) being used by 'port'. - */ -static void pnx8xxx_release_port(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - - release_mem_region(sport->port.mapbase, UART_PORT_SIZE); -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int pnx8xxx_request_port(struct uart_port *port) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, - "pnx8xxx-uart") != NULL ? 0 : -EBUSY; -} - -/* - * Configure/autoconfigure the port. - */ -static void pnx8xxx_config_port(struct uart_port *port, int flags) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - - if (flags & UART_CONFIG_TYPE && - pnx8xxx_request_port(&sport->port) == 0) - sport->port.type = PORT_PNX8XXX; -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - * The only change we allow are to the flags and type, and - * even then only between PORT_PNX8XXX and PORT_UNKNOWN - */ -static int -pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_PNX8XXX) - ret = -EINVAL; - if (sport->port.irq != ser->irq) - ret = -EINVAL; - if (ser->io_type != SERIAL_IO_MEM) - ret = -EINVAL; - if (sport->port.uartclk / 16 != ser->baud_base) - ret = -EINVAL; - if ((void *)sport->port.mapbase != ser->iomem_base) - ret = -EINVAL; - if (sport->port.iobase != ser->port) - ret = -EINVAL; - if (ser->hub6 != 0) - ret = -EINVAL; - return ret; -} - -static struct uart_ops pnx8xxx_pops = { - .tx_empty = pnx8xxx_tx_empty, - .set_mctrl = pnx8xxx_set_mctrl, - .get_mctrl = pnx8xxx_get_mctrl, - .stop_tx = pnx8xxx_stop_tx, - .start_tx = pnx8xxx_start_tx, - .stop_rx = pnx8xxx_stop_rx, - .enable_ms = pnx8xxx_enable_ms, - .break_ctl = pnx8xxx_break_ctl, - .startup = pnx8xxx_startup, - .shutdown = pnx8xxx_shutdown, - .set_termios = pnx8xxx_set_termios, - .type = pnx8xxx_type, - .release_port = pnx8xxx_release_port, - .request_port = pnx8xxx_request_port, - .config_port = pnx8xxx_config_port, - .verify_port = pnx8xxx_verify_port, -}; - - -/* - * Setup the PNX8XXX serial ports. - * - * Note also that we support "console=ttySx" where "x" is either 0 or 1. - */ -static void __init pnx8xxx_init_ports(void) -{ - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0; i < NR_PORTS; i++) { - init_timer(&pnx8xxx_ports[i].timer); - pnx8xxx_ports[i].timer.function = pnx8xxx_timeout; - pnx8xxx_ports[i].timer.data = (unsigned long)&pnx8xxx_ports[i]; - pnx8xxx_ports[i].port.ops = &pnx8xxx_pops; - } -} - -#ifdef CONFIG_SERIAL_PNX8XXX_CONSOLE - -static void pnx8xxx_console_putchar(struct uart_port *port, int ch) -{ - struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; - int status; - - do { - /* Wait for UART_TX register to empty */ - status = serial_in(sport, PNX8XXX_FIFO); - } while (status & PNX8XXX_UART_FIFO_TXFIFO); - serial_out(sport, PNX8XXX_FIFO, ch); -} - -/* - * Interrupts are disabled on entering - */static void -pnx8xxx_console_write(struct console *co, const char *s, unsigned int count) -{ - struct pnx8xxx_port *sport = &pnx8xxx_ports[co->index]; - unsigned int old_ien, status; - - /* - * First, save IEN and then disable interrupts - */ - old_ien = serial_in(sport, PNX8XXX_IEN); - serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX | - PNX8XXX_UART_INT_ALLRX)); - - uart_console_write(&sport->port, s, count, pnx8xxx_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore IEN - */ - do { - /* Wait for UART_TX register to empty */ - status = serial_in(sport, PNX8XXX_FIFO); - } while (status & PNX8XXX_UART_FIFO_TXFIFO); - - /* Clear TX and EMPTY interrupt */ - serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_TX | - PNX8XXX_UART_INT_EMPTY); - - serial_out(sport, PNX8XXX_IEN, old_ien); -} - -static int __init -pnx8xxx_console_setup(struct console *co, char *options) -{ - struct pnx8xxx_port *sport; - 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. - */ - if (co->index == -1 || co->index >= NR_PORTS) - co->index = 0; - sport = &pnx8xxx_ports[co->index]; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&sport->port, co, baud, parity, bits, flow); -} - -static struct uart_driver pnx8xxx_reg; -static struct console pnx8xxx_console = { - .name = "ttyS", - .write = pnx8xxx_console_write, - .device = uart_console_device, - .setup = pnx8xxx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &pnx8xxx_reg, -}; - -static int __init pnx8xxx_rs_console_init(void) -{ - pnx8xxx_init_ports(); - register_console(&pnx8xxx_console); - return 0; -} -console_initcall(pnx8xxx_rs_console_init); - -#define PNX8XXX_CONSOLE &pnx8xxx_console -#else -#define PNX8XXX_CONSOLE NULL -#endif - -static struct uart_driver pnx8xxx_reg = { - .owner = THIS_MODULE, - .driver_name = "ttyS", - .dev_name = "ttyS", - .major = SERIAL_PNX8XXX_MAJOR, - .minor = MINOR_START, - .nr = NR_PORTS, - .cons = PNX8XXX_CONSOLE, -}; - -static int pnx8xxx_serial_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct pnx8xxx_port *sport = platform_get_drvdata(pdev); - - return uart_suspend_port(&pnx8xxx_reg, &sport->port); -} - -static int pnx8xxx_serial_resume(struct platform_device *pdev) -{ - struct pnx8xxx_port *sport = platform_get_drvdata(pdev); - - return uart_resume_port(&pnx8xxx_reg, &sport->port); -} - -static int pnx8xxx_serial_probe(struct platform_device *pdev) -{ - struct resource *res = pdev->resource; - int i; - - for (i = 0; i < pdev->num_resources; i++, res++) { - if (!(res->flags & IORESOURCE_MEM)) - continue; - - for (i = 0; i < NR_PORTS; i++) { - if (pnx8xxx_ports[i].port.mapbase != res->start) - continue; - - pnx8xxx_ports[i].port.dev = &pdev->dev; - uart_add_one_port(&pnx8xxx_reg, &pnx8xxx_ports[i].port); - platform_set_drvdata(pdev, &pnx8xxx_ports[i]); - break; - } - } - - return 0; -} - -static int pnx8xxx_serial_remove(struct platform_device *pdev) -{ - struct pnx8xxx_port *sport = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (sport) - uart_remove_one_port(&pnx8xxx_reg, &sport->port); - - return 0; -} - -static struct platform_driver pnx8xxx_serial_driver = { - .driver = { - .name = "pnx8xxx-uart", - .owner = THIS_MODULE, - }, - .probe = pnx8xxx_serial_probe, - .remove = pnx8xxx_serial_remove, - .suspend = pnx8xxx_serial_suspend, - .resume = pnx8xxx_serial_resume, -}; - -static int __init pnx8xxx_serial_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: PNX8XXX driver\n"); - - pnx8xxx_init_ports(); - - ret = uart_register_driver(&pnx8xxx_reg); - if (ret == 0) { - ret = platform_driver_register(&pnx8xxx_serial_driver); - if (ret) - uart_unregister_driver(&pnx8xxx_reg); - } - return ret; -} - -static void __exit pnx8xxx_serial_exit(void) -{ - platform_driver_unregister(&pnx8xxx_serial_driver); - uart_unregister_driver(&pnx8xxx_reg); -} - -module_init(pnx8xxx_serial_init); -module_exit(pnx8xxx_serial_exit); - -MODULE_AUTHOR("Embedded Alley Solutions, Inc."); -MODULE_DESCRIPTION("PNX8XXX SoCs serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_PNX8XXX_MAJOR); -MODULE_ALIAS("platform:pnx8xxx-uart"); diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c deleted file mode 100644 index 1102a39b44f5..000000000000 --- a/drivers/serial/pxa.c +++ /dev/null @@ -1,879 +0,0 @@ -/* - * linux/drivers/serial/pxa.c - * - * Based on drivers/serial/8250.c by Russell King. - * - * Author: Nicolas Pitre - * Created: Feb 20, 2003 - * Copyright: (C) 2003 Monta Vista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Note 1: This driver is made separate from the already too overloaded - * 8250.c because it needs some kirks of its own and that'll make it - * easier to add DMA support. - * - * Note 2: I'm too sick of device allocation policies for serial ports. - * If someone else wants to request an "official" allocation of major/minor - * for this driver please be my guest. And don't forget that new hardware - * to come from Intel might have more than 3 or 4 of those UARTs. Let's - * hope for a better port registration and dynamic device allocation scheme - * with the serial core maintainer satisfaction to appear soon. - */ - - -#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct uart_pxa_port { - struct uart_port port; - unsigned char ier; - unsigned char lcr; - unsigned char mcr; - unsigned int lsr_break_flag; - struct clk *clk; - char *name; -}; - -static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) -{ - offset <<= 2; - return readl(up->port.membase + offset); -} - -static inline void serial_out(struct uart_pxa_port *up, int offset, int value) -{ - offset <<= 2; - writel(value, up->port.membase + offset); -} - -static void serial_pxa_enable_ms(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); -} - -static void serial_pxa_stop_tx(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static void serial_pxa_stop_rx(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static inline void receive_chars(struct uart_pxa_port *up, int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned int ch, flag; - int max_count = 256; - - do { - ch = serial_in(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ignored. - */ - *status &= up->port.read_status_mask; - -#ifdef CONFIG_SERIAL_PXA_CONSOLE - if (up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } -#endif - if (*status & UART_LSR_BI) { - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - - uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); - - ignore_char: - *status = serial_in(up, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); -} - -static void transmit_chars(struct uart_pxa_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_out(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - serial_pxa_stop_tx(&up->port); - return; - } - - count = up->port.fifosize / 2; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - - if (uart_circ_empty(xmit)) - serial_pxa_stop_tx(&up->port); -} - -static void serial_pxa_start_tx(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } -} - -static inline void check_modem_status(struct uart_pxa_port *up) -{ - int status; - - status = serial_in(up, UART_MSR); - - if ((status & UART_MSR_ANY_DELTA) == 0) - return; - - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); -} - -/* - * This handles the interrupt from one port. - */ -static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) -{ - struct uart_pxa_port *up = dev_id; - unsigned int iir, lsr; - - iir = serial_in(up, UART_IIR); - if (iir & UART_IIR_NO_INT) - return IRQ_NONE; - lsr = serial_in(up, UART_LSR); - if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); - check_modem_status(up); - if (lsr & UART_LSR_THRE) - transmit_chars(up); - return IRQ_HANDLED; -} - -static unsigned int serial_pxa_tx_empty(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int serial_pxa_get_mctrl(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned char status; - unsigned int ret; - - status = serial_in(up, 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 serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned char mcr = 0; - - 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; - - mcr |= up->mcr; - - serial_out(up, UART_MCR, mcr); -} - -static void serial_pxa_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -#if 0 -static void serial_pxa_dma_init(struct pxa_uart *up) -{ - up->rxdma = - pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up); - if (up->rxdma < 0) - goto out; - up->txdma = - pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up); - if (up->txdma < 0) - goto err_txdma; - up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL); - if (!up->dmadesc) - goto err_alloc; - - /* ... */ -err_alloc: - pxa_free_dma(up->txdma); -err_rxdma: - pxa_free_dma(up->rxdma); -out: - return; -} -#endif - -static int serial_pxa_startup(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned long flags; - int retval; - - if (port->line == 3) /* HWUART */ - up->mcr |= UART_MCR_AFE; - else - up->mcr = 0; - - up->port.uartclk = clk_get_rate(up->clk); - - /* - * Allocate the IRQ - */ - retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up); - if (retval) - return retval; - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); - - /* - * Clear the interrupt registers. - */ - (void) serial_in(up, UART_LSR); - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - /* - * Now, initialize the UART - */ - serial_out(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - up->port.mctrl |= TIOCM_OUT2; - serial_pxa_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; - serial_out(up, UART_IER, up->ier); - - /* - * And clear the interrupt registers again for luck. - */ - (void) serial_in(up, UART_LSR); - (void) serial_in(up, UART_RX); - (void) serial_in(up, UART_IIR); - (void) serial_in(up, UART_MSR); - - return 0; -} - -static void serial_pxa_shutdown(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned long flags; - - free_irq(up->port.irq, up); - - /* - * Disable interrupts from this port - */ - up->ier = 0; - serial_out(up, UART_IER, 0); - - spin_lock_irqsave(&up->port.lock, flags); - up->port.mctrl &= ~TIOCM_OUT2; - serial_pxa_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition and FIFOs - */ - serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); - serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - serial_out(up, UART_FCR, 0); -} - -static void -serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned char cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - unsigned int dll; - - switch (termios->c_cflag & CSIZE) { - case CS5: - cval = UART_LCR_WLEN5; - break; - case CS6: - cval = UART_LCR_WLEN6; - break; - case CS7: - cval = UART_LCR_WLEN7; - break; - default: - case CS8: - cval = UART_LCR_WLEN8; - break; - } - - if (termios->c_cflag & CSTOPB) - cval |= UART_LCR_STOP; - if (termios->c_cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(termios->c_cflag & PARODD)) - cval |= UART_LCR_EPAR; - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - if ((up->port.uartclk / quot) < (2400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1; - else if ((up->port.uartclk / quot) < (230400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8; - else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32; - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Ensure the port will be enabled. - * This is required especially for serial console. - */ - up->ier |= UART_IER_UUE; - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characters to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - if (termios->c_cflag & CRTSCTS) - up->mcr |= UART_MCR_AFE; - else - up->mcr &= ~UART_MCR_AFE; - - serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ - serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ - - /* - * work around Errata #75 according to Intel(R) PXA27x Processor Family - * Specification Update (Nov 2005) - */ - dll = serial_in(up, UART_DLL); - WARN_ON(dll != (quot & 0xff)); - - serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ - serial_out(up, UART_LCR, cval); /* reset DLAB */ - up->lcr = cval; /* Save LCR */ - serial_pxa_set_mctrl(&up->port, up->port.mctrl); - serial_out(up, UART_FCR, fcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void -serial_pxa_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - - if (!state) - clk_enable(up->clk); - else - clk_disable(up->clk); -} - -static void serial_pxa_release_port(struct uart_port *port) -{ -} - -static int serial_pxa_request_port(struct uart_port *port) -{ - return 0; -} - -static void serial_pxa_config_port(struct uart_port *port, int flags) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - up->port.type = PORT_PXA; -} - -static int -serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* we don't want the core code to modify any port params */ - return -EINVAL; -} - -static const char * -serial_pxa_type(struct uart_port *port) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - return up->name; -} - -static struct uart_pxa_port *serial_pxa_ports[4]; -static struct uart_driver serial_pxa_reg; - -#ifdef CONFIG_SERIAL_PXA_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_pxa_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while ((status & BOTH_EMPTY) != BOTH_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) - udelay(1); - } -} - -static void serial_pxa_console_putchar(struct uart_port *port, int ch) -{ - struct uart_pxa_port *up = (struct uart_pxa_port *)port; - - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -/* - * 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. - */ -static void -serial_pxa_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_pxa_port *up = serial_pxa_ports[co->index]; - unsigned int ier; - - clk_enable(up->clk); - - /* - * First save the IER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, UART_IER_UUE); - - uart_console_write(&up->port, s, count, serial_pxa_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - serial_out(up, UART_IER, ier); - - clk_disable(up->clk); -} - -static int __init -serial_pxa_console_setup(struct console *co, char *options) -{ - struct uart_pxa_port *up; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index == -1 || co->index >= serial_pxa_reg.nr) - co->index = 0; - up = serial_pxa_ports[co->index]; - if (!up) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&up->port, co, baud, parity, bits, flow); -} - -static struct console serial_pxa_console = { - .name = "ttyS", - .write = serial_pxa_console_write, - .device = uart_console_device, - .setup = serial_pxa_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial_pxa_reg, -}; - -#define PXA_CONSOLE &serial_pxa_console -#else -#define PXA_CONSOLE NULL -#endif - -struct uart_ops serial_pxa_pops = { - .tx_empty = serial_pxa_tx_empty, - .set_mctrl = serial_pxa_set_mctrl, - .get_mctrl = serial_pxa_get_mctrl, - .stop_tx = serial_pxa_stop_tx, - .start_tx = serial_pxa_start_tx, - .stop_rx = serial_pxa_stop_rx, - .enable_ms = serial_pxa_enable_ms, - .break_ctl = serial_pxa_break_ctl, - .startup = serial_pxa_startup, - .shutdown = serial_pxa_shutdown, - .set_termios = serial_pxa_set_termios, - .pm = serial_pxa_pm, - .type = serial_pxa_type, - .release_port = serial_pxa_release_port, - .request_port = serial_pxa_request_port, - .config_port = serial_pxa_config_port, - .verify_port = serial_pxa_verify_port, -}; - -static struct uart_driver serial_pxa_reg = { - .owner = THIS_MODULE, - .driver_name = "PXA serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = 4, - .cons = PXA_CONSOLE, -}; - -#ifdef CONFIG_PM -static int serial_pxa_suspend(struct device *dev) -{ - struct uart_pxa_port *sport = dev_get_drvdata(dev); - - if (sport) - uart_suspend_port(&serial_pxa_reg, &sport->port); - - return 0; -} - -static int serial_pxa_resume(struct device *dev) -{ - struct uart_pxa_port *sport = dev_get_drvdata(dev); - - if (sport) - uart_resume_port(&serial_pxa_reg, &sport->port); - - return 0; -} - -static const struct dev_pm_ops serial_pxa_pm_ops = { - .suspend = serial_pxa_suspend, - .resume = serial_pxa_resume, -}; -#endif - -static int serial_pxa_probe(struct platform_device *dev) -{ - struct uart_pxa_port *sport; - struct resource *mmres, *irqres; - int ret; - - mmres = platform_get_resource(dev, IORESOURCE_MEM, 0); - irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0); - if (!mmres || !irqres) - return -ENODEV; - - sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL); - if (!sport) - return -ENOMEM; - - sport->clk = clk_get(&dev->dev, NULL); - if (IS_ERR(sport->clk)) { - ret = PTR_ERR(sport->clk); - goto err_free; - } - - sport->port.type = PORT_PXA; - sport->port.iotype = UPIO_MEM; - sport->port.mapbase = mmres->start; - sport->port.irq = irqres->start; - sport->port.fifosize = 64; - sport->port.ops = &serial_pxa_pops; - sport->port.line = dev->id; - sport->port.dev = &dev->dev; - sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - sport->port.uartclk = clk_get_rate(sport->clk); - - switch (dev->id) { - case 0: sport->name = "FFUART"; break; - case 1: sport->name = "BTUART"; break; - case 2: sport->name = "STUART"; break; - case 3: sport->name = "HWUART"; break; - default: - sport->name = "???"; - break; - } - - sport->port.membase = ioremap(mmres->start, mmres->end - mmres->start + 1); - if (!sport->port.membase) { - ret = -ENOMEM; - goto err_clk; - } - - serial_pxa_ports[dev->id] = sport; - - uart_add_one_port(&serial_pxa_reg, &sport->port); - platform_set_drvdata(dev, sport); - - return 0; - - err_clk: - clk_put(sport->clk); - err_free: - kfree(sport); - return ret; -} - -static int serial_pxa_remove(struct platform_device *dev) -{ - struct uart_pxa_port *sport = platform_get_drvdata(dev); - - platform_set_drvdata(dev, NULL); - - uart_remove_one_port(&serial_pxa_reg, &sport->port); - clk_put(sport->clk); - kfree(sport); - - return 0; -} - -static struct platform_driver serial_pxa_driver = { - .probe = serial_pxa_probe, - .remove = serial_pxa_remove, - - .driver = { - .name = "pxa2xx-uart", - .owner = THIS_MODULE, -#ifdef CONFIG_PM - .pm = &serial_pxa_pm_ops, -#endif - }, -}; - -int __init serial_pxa_init(void) -{ - int ret; - - ret = uart_register_driver(&serial_pxa_reg); - if (ret != 0) - return ret; - - ret = platform_driver_register(&serial_pxa_driver); - if (ret != 0) - uart_unregister_driver(&serial_pxa_reg); - - return ret; -} - -void __exit serial_pxa_exit(void) -{ - platform_driver_unregister(&serial_pxa_driver); - uart_unregister_driver(&serial_pxa_reg); -} - -module_init(serial_pxa_init); -module_exit(serial_pxa_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:pxa2xx-uart"); diff --git a/drivers/serial/s3c2400.c b/drivers/serial/s3c2400.c deleted file mode 100644 index fed1a9a1ffb4..000000000000 --- a/drivers/serial/s3c2400.c +++ /dev/null @@ -1,106 +0,0 @@ -/* linux/drivers/serial/s3c240.c - * - * Driver for Samsung SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2005 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#include "samsung.h" - -static int s3c2400_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - clk->divisor = 1; - clk->name = "pclk"; - - return 0; -} - -static int s3c2400_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - return 0; -} - -static int s3c2400_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - dbg("s3c2400_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - wr_regl(port, S3C2410_UCON, cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c2400_uart_inf = { - .name = "Samsung S3C2400 UART", - .type = PORT_S3C2400, - .fifosize = 16, - .rx_fifomask = S3C2410_UFSTAT_RXMASK, - .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, - .rx_fifofull = S3C2410_UFSTAT_RXFULL, - .tx_fifofull = S3C2410_UFSTAT_TXFULL, - .tx_fifomask = S3C2410_UFSTAT_TXMASK, - .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, - .get_clksrc = s3c2400_serial_getsource, - .set_clksrc = s3c2400_serial_setsource, - .reset_port = s3c2400_serial_resetport, -}; - -static int s3c2400_serial_probe(struct platform_device *dev) -{ - return s3c24xx_serial_probe(dev, &s3c2400_uart_inf); -} - -static struct platform_driver s3c2400_serial_driver = { - .probe = s3c2400_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c2400-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c2400_serial_driver, &s3c2400_uart_inf); - -static inline int s3c2400_serial_init(void) -{ - return s3c24xx_serial_init(&s3c2400_serial_driver, &s3c2400_uart_inf); -} - -static inline void s3c2400_serial_exit(void) -{ - platform_driver_unregister(&s3c2400_serial_driver); -} - -module_init(s3c2400_serial_init); -module_exit(s3c2400_serial_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_DESCRIPTION("Samsung S3C2400 SoC Serial port driver"); -MODULE_ALIAS("platform:s3c2400-uart"); diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c deleted file mode 100644 index 73f089d3efd6..000000000000 --- a/drivers/serial/s3c2410.c +++ /dev/null @@ -1,118 +0,0 @@ -/* linux/drivers/serial/s3c2410.c - * - * Driver for Samsung S3C2410 SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "samsung.h" - -static int s3c2410_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - if (strcmp(clk->name, "uclk") == 0) - ucon |= S3C2410_UCON_UCLK; - else - ucon &= ~S3C2410_UCON_UCLK; - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - -static int s3c2410_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - clk->divisor = 1; - clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk"; - - return 0; -} - -static int s3c2410_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - wr_regl(port, S3C2410_UCON, cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c2410_uart_inf = { - .name = "Samsung S3C2410 UART", - .type = PORT_S3C2410, - .fifosize = 16, - .rx_fifomask = S3C2410_UFSTAT_RXMASK, - .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, - .rx_fifofull = S3C2410_UFSTAT_RXFULL, - .tx_fifofull = S3C2410_UFSTAT_TXFULL, - .tx_fifomask = S3C2410_UFSTAT_TXMASK, - .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, - .get_clksrc = s3c2410_serial_getsource, - .set_clksrc = s3c2410_serial_setsource, - .reset_port = s3c2410_serial_resetport, -}; - -static int s3c2410_serial_probe(struct platform_device *dev) -{ - return s3c24xx_serial_probe(dev, &s3c2410_uart_inf); -} - -static struct platform_driver s3c2410_serial_driver = { - .probe = s3c2410_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c2410-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c2410_serial_driver, &s3c2410_uart_inf); - -static int __init s3c2410_serial_init(void) -{ - return s3c24xx_serial_init(&s3c2410_serial_driver, &s3c2410_uart_inf); -} - -static void __exit s3c2410_serial_exit(void) -{ - platform_driver_unregister(&s3c2410_serial_driver); -} - -module_init(s3c2410_serial_init); -module_exit(s3c2410_serial_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_DESCRIPTION("Samsung S3C2410 SoC Serial port driver"); -MODULE_ALIAS("platform:s3c2410-uart"); diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c deleted file mode 100644 index 1700b1a2fb7e..000000000000 --- a/drivers/serial/s3c2412.c +++ /dev/null @@ -1,152 +0,0 @@ -/* linux/drivers/serial/s3c2412.c - * - * Driver for Samsung S3C2412 and S3C2413 SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "samsung.h" - -static int s3c2412_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - ucon &= ~S3C2412_UCON_CLKMASK; - - if (strcmp(clk->name, "uclk") == 0) - ucon |= S3C2440_UCON_UCLK; - else if (strcmp(clk->name, "pclk") == 0) - ucon |= S3C2440_UCON_PCLK; - else if (strcmp(clk->name, "usysclk") == 0) - ucon |= S3C2412_UCON_USYSCLK; - else { - printk(KERN_ERR "unknown clock source %s\n", clk->name); - return -EINVAL; - } - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - - -static int s3c2412_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - switch (ucon & S3C2412_UCON_CLKMASK) { - case S3C2412_UCON_UCLK: - clk->divisor = 1; - clk->name = "uclk"; - break; - - case S3C2412_UCON_PCLK: - case S3C2412_UCON_PCLK2: - clk->divisor = 1; - clk->name = "pclk"; - break; - - case S3C2412_UCON_USYSCLK: - clk->divisor = 1; - clk->name = "usysclk"; - break; - } - - return 0; -} - -static int s3c2412_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - dbg("%s: port=%p (%08lx), cfg=%p\n", - __func__, port, port->mapbase, cfg); - - /* ensure we don't change the clock settings... */ - - ucon &= S3C2412_UCON_CLKMASK; - - wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c2412_uart_inf = { - .name = "Samsung S3C2412 UART", - .type = PORT_S3C2412, - .fifosize = 64, - .has_divslot = 1, - .rx_fifomask = S3C2440_UFSTAT_RXMASK, - .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, - .rx_fifofull = S3C2440_UFSTAT_RXFULL, - .tx_fifofull = S3C2440_UFSTAT_TXFULL, - .tx_fifomask = S3C2440_UFSTAT_TXMASK, - .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, - .get_clksrc = s3c2412_serial_getsource, - .set_clksrc = s3c2412_serial_setsource, - .reset_port = s3c2412_serial_resetport, -}; - -/* device management */ - -static int s3c2412_serial_probe(struct platform_device *dev) -{ - dbg("s3c2440_serial_probe: dev=%p\n", dev); - return s3c24xx_serial_probe(dev, &s3c2412_uart_inf); -} - -static struct platform_driver s3c2412_serial_driver = { - .probe = s3c2412_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c2412-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c2412_serial_driver, &s3c2412_uart_inf); - -static inline int s3c2412_serial_init(void) -{ - return s3c24xx_serial_init(&s3c2412_serial_driver, &s3c2412_uart_inf); -} - -static inline void s3c2412_serial_exit(void) -{ - platform_driver_unregister(&s3c2412_serial_driver); -} - -module_init(s3c2412_serial_init); -module_exit(s3c2412_serial_exit); - -MODULE_DESCRIPTION("Samsung S3C2412,S3C2413 SoC Serial port driver"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:s3c2412-uart"); diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c deleted file mode 100644 index 094cc3904b13..000000000000 --- a/drivers/serial/s3c2440.c +++ /dev/null @@ -1,181 +0,0 @@ -/* linux/drivers/serial/s3c2440.c - * - * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "samsung.h" - - -static int s3c2440_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - /* todo - proper fclk<>nonfclk switch. */ - - ucon &= ~S3C2440_UCON_CLKMASK; - - if (strcmp(clk->name, "uclk") == 0) - ucon |= S3C2440_UCON_UCLK; - else if (strcmp(clk->name, "pclk") == 0) - ucon |= S3C2440_UCON_PCLK; - else if (strcmp(clk->name, "fclk") == 0) - ucon |= S3C2440_UCON_FCLK; - else { - printk(KERN_ERR "unknown clock source %s\n", clk->name); - return -EINVAL; - } - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - - -static int s3c2440_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - unsigned long ucon0, ucon1, ucon2; - - switch (ucon & S3C2440_UCON_CLKMASK) { - case S3C2440_UCON_UCLK: - clk->divisor = 1; - clk->name = "uclk"; - break; - - case S3C2440_UCON_PCLK: - case S3C2440_UCON_PCLK2: - clk->divisor = 1; - clk->name = "pclk"; - break; - - case S3C2440_UCON_FCLK: - /* the fun of calculating the uart divisors on - * the s3c2440 */ - - ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON); - ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON); - ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON); - - printk("ucons: %08lx, %08lx, %08lx\n", ucon0, ucon1, ucon2); - - ucon0 &= S3C2440_UCON0_DIVMASK; - ucon1 &= S3C2440_UCON1_DIVMASK; - ucon2 &= S3C2440_UCON2_DIVMASK; - - if (ucon0 != 0) { - clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT; - clk->divisor += 6; - } else if (ucon1 != 0) { - clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT; - clk->divisor += 21; - } else if (ucon2 != 0) { - clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT; - clk->divisor += 36; - } else { - /* manual calims 44, seems to be 9 */ - clk->divisor = 9; - } - - clk->name = "fclk"; - break; - } - - return 0; -} - -static int s3c2440_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - /* ensure we don't change the clock settings... */ - - ucon &= (S3C2440_UCON0_DIVMASK | (3<<10)); - - wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c2440_uart_inf = { - .name = "Samsung S3C2440 UART", - .type = PORT_S3C2440, - .fifosize = 64, - .rx_fifomask = S3C2440_UFSTAT_RXMASK, - .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, - .rx_fifofull = S3C2440_UFSTAT_RXFULL, - .tx_fifofull = S3C2440_UFSTAT_TXFULL, - .tx_fifomask = S3C2440_UFSTAT_TXMASK, - .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, - .get_clksrc = s3c2440_serial_getsource, - .set_clksrc = s3c2440_serial_setsource, - .reset_port = s3c2440_serial_resetport, -}; - -/* device management */ - -static int s3c2440_serial_probe(struct platform_device *dev) -{ - dbg("s3c2440_serial_probe: dev=%p\n", dev); - return s3c24xx_serial_probe(dev, &s3c2440_uart_inf); -} - -static struct platform_driver s3c2440_serial_driver = { - .probe = s3c2440_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c2440-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf); - -static int __init s3c2440_serial_init(void) -{ - return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf); -} - -static void __exit s3c2440_serial_exit(void) -{ - platform_driver_unregister(&s3c2440_serial_driver); -} - -module_init(s3c2440_serial_init); -module_exit(s3c2440_serial_exit); - -MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:s3c2440-uart"); diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c deleted file mode 100644 index fad6083ca427..000000000000 --- a/drivers/serial/s3c24a0.c +++ /dev/null @@ -1,118 +0,0 @@ -/* linux/drivers/serial/s3c24a0.c - * - * Driver for Samsung S3C24A0 SoC onboard UARTs. - * - * Based on drivers/serial/s3c2410.c - * - * Author: Sandeep Patil - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "samsung.h" - -static int s3c24a0_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - if (strcmp(clk->name, "uclk") == 0) - ucon |= S3C2410_UCON_UCLK; - else - ucon &= ~S3C2410_UCON_UCLK; - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - -static int s3c24a0_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - clk->divisor = 1; - clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk"; - - return 0; -} - -static int s3c24a0_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - wr_regl(port, S3C2410_UCON, cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c24a0_uart_inf = { - .name = "Samsung S3C24A0 UART", - .type = PORT_S3C2410, - .fifosize = 16, - .rx_fifomask = S3C24A0_UFSTAT_RXMASK, - .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT, - .rx_fifofull = S3C24A0_UFSTAT_RXFULL, - .tx_fifofull = S3C24A0_UFSTAT_TXFULL, - .tx_fifomask = S3C24A0_UFSTAT_TXMASK, - .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT, - .get_clksrc = s3c24a0_serial_getsource, - .set_clksrc = s3c24a0_serial_setsource, - .reset_port = s3c24a0_serial_resetport, -}; - -static int s3c24a0_serial_probe(struct platform_device *dev) -{ - return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf); -} - -static struct platform_driver s3c24a0_serial_driver = { - .probe = s3c24a0_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c24a0-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); - -static int __init s3c24a0_serial_init(void) -{ - return s3c24xx_serial_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); -} - -static void __exit s3c24a0_serial_exit(void) -{ - platform_driver_unregister(&s3c24a0_serial_driver); -} - -module_init(s3c24a0_serial_init); -module_exit(s3c24a0_serial_exit); - diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c deleted file mode 100644 index 4be92ab50058..000000000000 --- a/drivers/serial/s3c6400.c +++ /dev/null @@ -1,152 +0,0 @@ -/* linux/drivers/serial/s3c6400.c - * - * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs. - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "samsung.h" - -static int s3c6400_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - if (strcmp(clk->name, "uclk0") == 0) { - ucon &= ~S3C6400_UCON_CLKMASK; - ucon |= S3C6400_UCON_UCLK0; - } else if (strcmp(clk->name, "uclk1") == 0) - ucon |= S3C6400_UCON_UCLK1; - else if (strcmp(clk->name, "pclk") == 0) { - /* See notes about transitioning from UCLK to PCLK */ - ucon &= ~S3C6400_UCON_UCLK0; - } else { - printk(KERN_ERR "unknown clock source %s\n", clk->name); - return -EINVAL; - } - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - - -static int s3c6400_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - u32 ucon = rd_regl(port, S3C2410_UCON); - - clk->divisor = 1; - - switch (ucon & S3C6400_UCON_CLKMASK) { - case S3C6400_UCON_UCLK0: - clk->name = "uclk0"; - break; - - case S3C6400_UCON_UCLK1: - clk->name = "uclk1"; - break; - - case S3C6400_UCON_PCLK: - case S3C6400_UCON_PCLK2: - clk->name = "pclk"; - break; - } - - return 0; -} - -static int s3c6400_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n", - port, port->mapbase, cfg); - - /* ensure we don't change the clock settings... */ - - ucon &= S3C6400_UCON_CLKMASK; - - wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -static struct s3c24xx_uart_info s3c6400_uart_inf = { - .name = "Samsung S3C6400 UART", - .type = PORT_S3C6400, - .fifosize = 64, - .has_divslot = 1, - .rx_fifomask = S3C2440_UFSTAT_RXMASK, - .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, - .rx_fifofull = S3C2440_UFSTAT_RXFULL, - .tx_fifofull = S3C2440_UFSTAT_TXFULL, - .tx_fifomask = S3C2440_UFSTAT_TXMASK, - .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, - .get_clksrc = s3c6400_serial_getsource, - .set_clksrc = s3c6400_serial_setsource, - .reset_port = s3c6400_serial_resetport, -}; - -/* device management */ - -static int s3c6400_serial_probe(struct platform_device *dev) -{ - dbg("s3c6400_serial_probe: dev=%p\n", dev); - return s3c24xx_serial_probe(dev, &s3c6400_uart_inf); -} - -static struct platform_driver s3c6400_serial_driver = { - .probe = s3c6400_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s3c6400-uart", - .owner = THIS_MODULE, - }, -}; - -s3c24xx_console_init(&s3c6400_serial_driver, &s3c6400_uart_inf); - -static int __init s3c6400_serial_init(void) -{ - return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf); -} - -static void __exit s3c6400_serial_exit(void) -{ - platform_driver_unregister(&s3c6400_serial_driver); -} - -module_init(s3c6400_serial_init); -module_exit(s3c6400_serial_exit); - -MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:s3c6400-uart"); diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c deleted file mode 100644 index 6ebccd70a707..000000000000 --- a/drivers/serial/s5pv210.c +++ /dev/null @@ -1,162 +0,0 @@ -/* linux/drivers/serial/s5pv210.c - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Based on drivers/serial/s3c6400.c - * - * Driver for Samsung S5PV210 SoC UARTs. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "samsung.h" - -static int s5pv210_serial_setsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - struct s3c2410_uartcfg *cfg = port->dev->platform_data; - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - if ((cfg->clocks_size) == 1) - return 0; - - if (strcmp(clk->name, "pclk") == 0) - ucon &= ~S5PV210_UCON_CLKMASK; - else if (strcmp(clk->name, "uclk1") == 0) - ucon |= S5PV210_UCON_CLKMASK; - else { - printk(KERN_ERR "unknown clock source %s\n", clk->name); - return -EINVAL; - } - - wr_regl(port, S3C2410_UCON, ucon); - return 0; -} - - -static int s5pv210_serial_getsource(struct uart_port *port, - struct s3c24xx_uart_clksrc *clk) -{ - struct s3c2410_uartcfg *cfg = port->dev->platform_data; - u32 ucon = rd_regl(port, S3C2410_UCON); - - clk->divisor = 1; - - if ((cfg->clocks_size) == 1) - return 0; - - switch (ucon & S5PV210_UCON_CLKMASK) { - case S5PV210_UCON_PCLK: - clk->name = "pclk"; - break; - case S5PV210_UCON_UCLK: - clk->name = "uclk1"; - break; - } - - return 0; -} - -static int s5pv210_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - unsigned long ucon = rd_regl(port, S3C2410_UCON); - - ucon &= S5PV210_UCON_CLKMASK; - wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); - wr_regl(port, S3C2410_ULCON, cfg->ulcon); - - /* reset both fifos */ - wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); - wr_regl(port, S3C2410_UFCON, cfg->ufcon); - - return 0; -} - -#define S5PV210_UART_DEFAULT_INFO(fifo_size) \ - .name = "Samsung S5PV210 UART0", \ - .type = PORT_S3C6400, \ - .fifosize = fifo_size, \ - .has_divslot = 1, \ - .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ - .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ - .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ - .tx_fifofull = S5PV210_UFSTAT_TXFULL, \ - .tx_fifomask = S5PV210_UFSTAT_TXMASK, \ - .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, \ - .get_clksrc = s5pv210_serial_getsource, \ - .set_clksrc = s5pv210_serial_setsource, \ - .reset_port = s5pv210_serial_resetport - -static struct s3c24xx_uart_info s5p_port_fifo256 = { - S5PV210_UART_DEFAULT_INFO(256), -}; - -static struct s3c24xx_uart_info s5p_port_fifo64 = { - S5PV210_UART_DEFAULT_INFO(64), -}; - -static struct s3c24xx_uart_info s5p_port_fifo16 = { - S5PV210_UART_DEFAULT_INFO(16), -}; - -static struct s3c24xx_uart_info *s5p_uart_inf[] = { - [0] = &s5p_port_fifo256, - [1] = &s5p_port_fifo64, - [2] = &s5p_port_fifo16, - [3] = &s5p_port_fifo16, -}; - -/* device management */ -static int s5p_serial_probe(struct platform_device *pdev) -{ - return s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]); -} - -static struct platform_driver s5p_serial_driver = { - .probe = s5p_serial_probe, - .remove = __devexit_p(s3c24xx_serial_remove), - .driver = { - .name = "s5pv210-uart", - .owner = THIS_MODULE, - }, -}; - -static int __init s5pv210_serial_console_init(void) -{ - return s3c24xx_serial_initconsole(&s5p_serial_driver, s5p_uart_inf); -} - -console_initcall(s5pv210_serial_console_init); - -static int __init s5p_serial_init(void) -{ - return s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf); -} - -static void __exit s5p_serial_exit(void) -{ - platform_driver_unregister(&s5p_serial_driver); -} - -module_init(s5p_serial_init); -module_exit(s5p_serial_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s5pv210-uart"); -MODULE_DESCRIPTION("Samsung S5PV210 UART Driver support"); -MODULE_AUTHOR("Thomas Abraham "); diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c deleted file mode 100644 index 2199d819a987..000000000000 --- a/drivers/serial/sa1100.c +++ /dev/null @@ -1,918 +0,0 @@ -/* - * linux/drivers/char/sa1100.c - * - * Driver for SA11x0 serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright (C) 2000 Deep Blue Solutions Ltd. - * - * 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 - */ - -#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* We've been assigned a range on the "Low-density serial ports" major */ -#define SERIAL_SA1100_MAJOR 204 -#define MINOR_START 5 - -#define NR_PORTS 3 - -#define SA1100_ISR_PASS_LIMIT 256 - -/* - * Convert from ignore_status_mask or read_status_mask to UTSR[01] - */ -#define SM_TO_UTSR0(x) ((x) & 0xff) -#define SM_TO_UTSR1(x) ((x) >> 8) -#define UTSR0_TO_SM(x) ((x)) -#define UTSR1_TO_SM(x) ((x) << 8) - -#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0) -#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1) -#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2) -#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3) -#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0) -#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1) -#define UART_GET_CHAR(sport) __raw_readl((sport)->port.membase + UTDR) - -#define UART_PUT_UTCR0(sport,v) __raw_writel((v),(sport)->port.membase + UTCR0) -#define UART_PUT_UTCR1(sport,v) __raw_writel((v),(sport)->port.membase + UTCR1) -#define UART_PUT_UTCR2(sport,v) __raw_writel((v),(sport)->port.membase + UTCR2) -#define UART_PUT_UTCR3(sport,v) __raw_writel((v),(sport)->port.membase + UTCR3) -#define UART_PUT_UTSR0(sport,v) __raw_writel((v),(sport)->port.membase + UTSR0) -#define UART_PUT_UTSR1(sport,v) __raw_writel((v),(sport)->port.membase + UTSR1) -#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR) - -/* - * This is the size of our serial port register set. - */ -#define UART_PORT_SIZE 0x24 - -/* - * This determines how often we check the modem status signals - * for any change. They generally aren't connected to an IRQ - * so we have to poll them. We also check immediately before - * filling the TX fifo incase CTS has been dropped. - */ -#define MCTRL_TIMEOUT (250*HZ/1000) - -struct sa1100_port { - struct uart_port port; - struct timer_list timer; - unsigned int old_status; -}; - -/* - * Handle any change of modem status signal since we were last called. - */ -static void sa1100_mctrl_check(struct sa1100_port *sport) -{ - unsigned int status, changed; - - status = sport->port.ops->get_mctrl(&sport->port); - changed = status ^ sport->old_status; - - if (changed == 0) - return; - - sport->old_status = status; - - if (changed & TIOCM_RI) - sport->port.icount.rng++; - if (changed & TIOCM_DSR) - sport->port.icount.dsr++; - if (changed & TIOCM_CAR) - uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); - if (changed & TIOCM_CTS) - uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - - wake_up_interruptible(&sport->port.state->port.delta_msr_wait); -} - -/* - * This is our per-port timeout handler, for checking the - * modem status signals. - */ -static void sa1100_timeout(unsigned long data) -{ - struct sa1100_port *sport = (struct sa1100_port *)data; - unsigned long flags; - - if (sport->port.state) { - spin_lock_irqsave(&sport->port.lock, flags); - sa1100_mctrl_check(sport); - spin_unlock_irqrestore(&sport->port.lock, flags); - - mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); - } -} - -/* - * interrupts disabled on entry - */ -static void sa1100_stop_tx(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - u32 utcr3; - - utcr3 = UART_GET_UTCR3(sport); - UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE); - sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS); -} - -/* - * port locked and interrupts disabled - */ -static void sa1100_start_tx(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - u32 utcr3; - - utcr3 = UART_GET_UTCR3(sport); - sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS); - UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE); -} - -/* - * Interrupts enabled - */ -static void sa1100_stop_rx(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - u32 utcr3; - - utcr3 = UART_GET_UTCR3(sport); - UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE); -} - -/* - * Set the modem control timer to fire immediately. - */ -static void sa1100_enable_ms(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - mod_timer(&sport->timer, jiffies); -} - -static void -sa1100_rx_chars(struct sa1100_port *sport) -{ - struct tty_struct *tty = sport->port.state->port.tty; - unsigned int status, ch, flg; - - status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | - UTSR0_TO_SM(UART_GET_UTSR0(sport)); - while (status & UTSR1_TO_SM(UTSR1_RNE)) { - ch = UART_GET_CHAR(sport); - - sport->port.icount.rx++; - - flg = TTY_NORMAL; - - /* - * note that the error handling code is - * out of the main execution path - */ - if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) { - if (status & UTSR1_TO_SM(UTSR1_PRE)) - sport->port.icount.parity++; - else if (status & UTSR1_TO_SM(UTSR1_FRE)) - sport->port.icount.frame++; - if (status & UTSR1_TO_SM(UTSR1_ROR)) - sport->port.icount.overrun++; - - status &= sport->port.read_status_mask; - - if (status & UTSR1_TO_SM(UTSR1_PRE)) - flg = TTY_PARITY; - else if (status & UTSR1_TO_SM(UTSR1_FRE)) - flg = TTY_FRAME; - -#ifdef SUPPORT_SYSRQ - sport->port.sysrq = 0; -#endif - } - - if (uart_handle_sysrq_char(&sport->port, ch)) - goto ignore_char; - - uart_insert_char(&sport->port, status, UTSR1_TO_SM(UTSR1_ROR), ch, flg); - - ignore_char: - status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | - UTSR0_TO_SM(UART_GET_UTSR0(sport)); - } - tty_flip_buffer_push(tty); -} - -static void sa1100_tx_chars(struct sa1100_port *sport) -{ - struct circ_buf *xmit = &sport->port.state->xmit; - - if (sport->port.x_char) { - UART_PUT_CHAR(sport, sport->port.x_char); - sport->port.icount.tx++; - sport->port.x_char = 0; - return; - } - - /* - * Check the modem control lines before - * transmitting anything. - */ - sa1100_mctrl_check(sport); - - if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - sa1100_stop_tx(&sport->port); - return; - } - - /* - * Tried using FIFO (not checking TNF) for fifo fill: - * still had the '4 bytes repeated' problem. - */ - while (UART_GET_UTSR1(sport) & UTSR1_TNF) { - UART_PUT_CHAR(sport, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - - if (uart_circ_empty(xmit)) - sa1100_stop_tx(&sport->port); -} - -static irqreturn_t sa1100_int(int irq, void *dev_id) -{ - struct sa1100_port *sport = dev_id; - unsigned int status, pass_counter = 0; - - spin_lock(&sport->port.lock); - status = UART_GET_UTSR0(sport); - status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; - do { - if (status & (UTSR0_RFS | UTSR0_RID)) { - /* Clear the receiver idle bit, if set */ - if (status & UTSR0_RID) - UART_PUT_UTSR0(sport, UTSR0_RID); - sa1100_rx_chars(sport); - } - - /* Clear the relevant break bits */ - if (status & (UTSR0_RBB | UTSR0_REB)) - UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB)); - - if (status & UTSR0_RBB) - sport->port.icount.brk++; - - if (status & UTSR0_REB) - uart_handle_break(&sport->port); - - if (status & UTSR0_TFS) - sa1100_tx_chars(sport); - if (pass_counter++ > SA1100_ISR_PASS_LIMIT) - break; - status = UART_GET_UTSR0(sport); - status &= SM_TO_UTSR0(sport->port.read_status_mask) | - ~UTSR0_TFS; - } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); - spin_unlock(&sport->port.lock); - - return IRQ_HANDLED; -} - -/* - * Return TIOCSER_TEMT when transmitter is not busy. - */ -static unsigned int sa1100_tx_empty(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT; -} - -static unsigned int sa1100_get_mctrl(struct uart_port *port) -{ - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; -} - -static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -/* - * Interrupts always disabled. - */ -static void sa1100_break_ctl(struct uart_port *port, int break_state) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - unsigned long flags; - unsigned int utcr3; - - spin_lock_irqsave(&sport->port.lock, flags); - utcr3 = UART_GET_UTCR3(sport); - if (break_state == -1) - utcr3 |= UTCR3_BRK; - else - utcr3 &= ~UTCR3_BRK; - UART_PUT_UTCR3(sport, utcr3); - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static int sa1100_startup(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - int retval; - - /* - * Allocate the IRQ - */ - retval = request_irq(sport->port.irq, sa1100_int, 0, - "sa11x0-uart", sport); - if (retval) - return retval; - - /* - * Finally, clear and enable interrupts - */ - UART_PUT_UTSR0(sport, -1); - UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE); - - /* - * Enable modem status interrupts - */ - spin_lock_irq(&sport->port.lock); - sa1100_enable_ms(&sport->port); - spin_unlock_irq(&sport->port.lock); - - return 0; -} - -static void sa1100_shutdown(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - /* - * Stop our timer. - */ - del_timer_sync(&sport->timer); - - /* - * Free the interrupt - */ - free_irq(sport->port.irq, sport); - - /* - * Disable all interrupts, port and break condition. - */ - UART_PUT_UTCR3(sport, 0); -} - -static void -sa1100_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - unsigned long flags; - unsigned int utcr0, old_utcr3, baud, quot; - unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; - - /* - * We only support CS7 and CS8. - */ - while ((termios->c_cflag & CSIZE) != CS7 && - (termios->c_cflag & CSIZE) != CS8) { - termios->c_cflag &= ~CSIZE; - termios->c_cflag |= old_csize; - old_csize = CS8; - } - - if ((termios->c_cflag & CSIZE) == CS8) - utcr0 = UTCR0_DSS; - else - utcr0 = 0; - - if (termios->c_cflag & CSTOPB) - utcr0 |= UTCR0_SBS; - if (termios->c_cflag & PARENB) { - utcr0 |= UTCR0_PE; - if (!(termios->c_cflag & PARODD)) - utcr0 |= UTCR0_OES; - } - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - spin_lock_irqsave(&sport->port.lock, flags); - - sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); - sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); - if (termios->c_iflag & INPCK) - sport->port.read_status_mask |= - UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); - if (termios->c_iflag & (BRKINT | PARMRK)) - sport->port.read_status_mask |= - UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); - - /* - * Characters to ignore - */ - sport->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= - UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); - if (termios->c_iflag & IGNBRK) { - sport->port.ignore_status_mask |= - UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - sport->port.ignore_status_mask |= - UTSR1_TO_SM(UTSR1_ROR); - } - - del_timer_sync(&sport->timer); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * disable interrupts and drain transmitter - */ - old_utcr3 = UART_GET_UTCR3(sport); - UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); - - while (UART_GET_UTSR1(sport) & UTSR1_TBY) - barrier(); - - /* then, disable everything */ - UART_PUT_UTCR3(sport, 0); - - /* set the parity, stop bits and data size */ - UART_PUT_UTCR0(sport, utcr0); - - /* set the baud rate */ - quot -= 1; - UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8)); - UART_PUT_UTCR2(sport, (quot & 0xff)); - - UART_PUT_UTSR0(sport, -1); - - UART_PUT_UTCR3(sport, old_utcr3); - - if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) - sa1100_enable_ms(&sport->port); - - spin_unlock_irqrestore(&sport->port.lock, flags); -} - -static const char *sa1100_type(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - return sport->port.type == PORT_SA1100 ? "SA1100" : NULL; -} - -/* - * Release the memory region(s) being used by 'port'. - */ -static void sa1100_release_port(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - release_mem_region(sport->port.mapbase, UART_PORT_SIZE); -} - -/* - * Request the memory region(s) being used by 'port'. - */ -static int sa1100_request_port(struct uart_port *port) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, - "sa11x0-uart") != NULL ? 0 : -EBUSY; -} - -/* - * Configure/autoconfigure the port. - */ -static void sa1100_config_port(struct uart_port *port, int flags) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - if (flags & UART_CONFIG_TYPE && - sa1100_request_port(&sport->port) == 0) - sport->port.type = PORT_SA1100; -} - -/* - * Verify the new serial_struct (for TIOCSSERIAL). - * The only change we allow are to the flags and type, and - * even then only between PORT_SA1100 and PORT_UNKNOWN - */ -static int -sa1100_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) - ret = -EINVAL; - if (sport->port.irq != ser->irq) - ret = -EINVAL; - if (ser->io_type != SERIAL_IO_MEM) - ret = -EINVAL; - if (sport->port.uartclk / 16 != ser->baud_base) - ret = -EINVAL; - if ((void *)sport->port.mapbase != ser->iomem_base) - ret = -EINVAL; - if (sport->port.iobase != ser->port) - ret = -EINVAL; - if (ser->hub6 != 0) - ret = -EINVAL; - return ret; -} - -static struct uart_ops sa1100_pops = { - .tx_empty = sa1100_tx_empty, - .set_mctrl = sa1100_set_mctrl, - .get_mctrl = sa1100_get_mctrl, - .stop_tx = sa1100_stop_tx, - .start_tx = sa1100_start_tx, - .stop_rx = sa1100_stop_rx, - .enable_ms = sa1100_enable_ms, - .break_ctl = sa1100_break_ctl, - .startup = sa1100_startup, - .shutdown = sa1100_shutdown, - .set_termios = sa1100_set_termios, - .type = sa1100_type, - .release_port = sa1100_release_port, - .request_port = sa1100_request_port, - .config_port = sa1100_config_port, - .verify_port = sa1100_verify_port, -}; - -static struct sa1100_port sa1100_ports[NR_PORTS]; - -/* - * Setup the SA1100 serial ports. Note that we don't include the IrDA - * port here since we have our own SIR/FIR driver (see drivers/net/irda) - * - * Note also that we support "console=ttySAx" where "x" is either 0 or 1. - * Which serial port this ends up being depends on the machine you're - * running this kernel on. I'm not convinced that this is a good idea, - * but that's the way it traditionally works. - * - * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer - * used here. - */ -static void __init sa1100_init_ports(void) -{ - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - for (i = 0; i < NR_PORTS; i++) { - sa1100_ports[i].port.uartclk = 3686400; - sa1100_ports[i].port.ops = &sa1100_pops; - sa1100_ports[i].port.fifosize = 8; - sa1100_ports[i].port.line = i; - sa1100_ports[i].port.iotype = UPIO_MEM; - init_timer(&sa1100_ports[i].timer); - sa1100_ports[i].timer.function = sa1100_timeout; - sa1100_ports[i].timer.data = (unsigned long)&sa1100_ports[i]; - } - - /* - * make transmit lines outputs, so that when the port - * is closed, the output is in the MARK state. - */ - PPDR |= PPC_TXD1 | PPC_TXD3; - PPSR |= PPC_TXD1 | PPC_TXD3; -} - -void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns) -{ - if (fns->get_mctrl) - sa1100_pops.get_mctrl = fns->get_mctrl; - if (fns->set_mctrl) - sa1100_pops.set_mctrl = fns->set_mctrl; - - sa1100_pops.pm = fns->pm; - sa1100_pops.set_wake = fns->set_wake; -} - -void __init sa1100_register_uart(int idx, int port) -{ - if (idx >= NR_PORTS) { - printk(KERN_ERR "%s: bad index number %d\n", __func__, idx); - return; - } - - switch (port) { - case 1: - sa1100_ports[idx].port.membase = (void __iomem *)&Ser1UTCR0; - sa1100_ports[idx].port.mapbase = _Ser1UTCR0; - sa1100_ports[idx].port.irq = IRQ_Ser1UART; - sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF; - break; - - case 2: - sa1100_ports[idx].port.membase = (void __iomem *)&Ser2UTCR0; - sa1100_ports[idx].port.mapbase = _Ser2UTCR0; - sa1100_ports[idx].port.irq = IRQ_Ser2ICP; - sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF; - break; - - case 3: - sa1100_ports[idx].port.membase = (void __iomem *)&Ser3UTCR0; - sa1100_ports[idx].port.mapbase = _Ser3UTCR0; - sa1100_ports[idx].port.irq = IRQ_Ser3UART; - sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF; - break; - - default: - printk(KERN_ERR "%s: bad port number %d\n", __func__, port); - } -} - - -#ifdef CONFIG_SERIAL_SA1100_CONSOLE -static void sa1100_console_putchar(struct uart_port *port, int ch) -{ - struct sa1100_port *sport = (struct sa1100_port *)port; - - while (!(UART_GET_UTSR1(sport) & UTSR1_TNF)) - barrier(); - UART_PUT_CHAR(sport, ch); -} - -/* - * Interrupts are disabled on entering - */ -static void -sa1100_console_write(struct console *co, const char *s, unsigned int count) -{ - struct sa1100_port *sport = &sa1100_ports[co->index]; - unsigned int old_utcr3, status; - - /* - * First, save UTCR3 and then disable interrupts - */ - old_utcr3 = UART_GET_UTCR3(sport); - UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | - UTCR3_TXE); - - uart_console_write(&sport->port, s, count, sa1100_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore UTCR3 - */ - do { - status = UART_GET_UTSR1(sport); - } while (status & UTSR1_TBY); - UART_PUT_UTCR3(sport, old_utcr3); -} - -/* - * If the port was already initialised (eg, by a boot loader), - * try to determine the current setup. - */ -static void __init -sa1100_console_get_options(struct sa1100_port *sport, int *baud, - int *parity, int *bits) -{ - unsigned int utcr3; - - utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE); - if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { - /* ok, the port was enabled */ - unsigned int utcr0, quot; - - utcr0 = UART_GET_UTCR0(sport); - - *parity = 'n'; - if (utcr0 & UTCR0_PE) { - if (utcr0 & UTCR0_OES) - *parity = 'e'; - else - *parity = 'o'; - } - - if (utcr0 & UTCR0_DSS) - *bits = 8; - else - *bits = 7; - - quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8; - quot &= 0xfff; - *baud = sport->port.uartclk / (16 * (quot + 1)); - } -} - -static int __init -sa1100_console_setup(struct console *co, char *options) -{ - struct sa1100_port *sport; - int baud = 9600; - 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. - */ - if (co->index == -1 || co->index >= NR_PORTS) - co->index = 0; - sport = &sa1100_ports[co->index]; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - sa1100_console_get_options(sport, &baud, &parity, &bits); - - return uart_set_options(&sport->port, co, baud, parity, bits, flow); -} - -static struct uart_driver sa1100_reg; -static struct console sa1100_console = { - .name = "ttySA", - .write = sa1100_console_write, - .device = uart_console_device, - .setup = sa1100_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sa1100_reg, -}; - -static int __init sa1100_rs_console_init(void) -{ - sa1100_init_ports(); - register_console(&sa1100_console); - return 0; -} -console_initcall(sa1100_rs_console_init); - -#define SA1100_CONSOLE &sa1100_console -#else -#define SA1100_CONSOLE NULL -#endif - -static struct uart_driver sa1100_reg = { - .owner = THIS_MODULE, - .driver_name = "ttySA", - .dev_name = "ttySA", - .major = SERIAL_SA1100_MAJOR, - .minor = MINOR_START, - .nr = NR_PORTS, - .cons = SA1100_CONSOLE, -}; - -static int sa1100_serial_suspend(struct platform_device *dev, pm_message_t state) -{ - struct sa1100_port *sport = platform_get_drvdata(dev); - - if (sport) - uart_suspend_port(&sa1100_reg, &sport->port); - - return 0; -} - -static int sa1100_serial_resume(struct platform_device *dev) -{ - struct sa1100_port *sport = platform_get_drvdata(dev); - - if (sport) - uart_resume_port(&sa1100_reg, &sport->port); - - return 0; -} - -static int sa1100_serial_probe(struct platform_device *dev) -{ - struct resource *res = dev->resource; - int i; - - for (i = 0; i < dev->num_resources; i++, res++) - if (res->flags & IORESOURCE_MEM) - break; - - if (i < dev->num_resources) { - for (i = 0; i < NR_PORTS; i++) { - if (sa1100_ports[i].port.mapbase != res->start) - continue; - - sa1100_ports[i].port.dev = &dev->dev; - uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); - platform_set_drvdata(dev, &sa1100_ports[i]); - break; - } - } - - return 0; -} - -static int sa1100_serial_remove(struct platform_device *pdev) -{ - struct sa1100_port *sport = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (sport) - uart_remove_one_port(&sa1100_reg, &sport->port); - - return 0; -} - -static struct platform_driver sa11x0_serial_driver = { - .probe = sa1100_serial_probe, - .remove = sa1100_serial_remove, - .suspend = sa1100_serial_suspend, - .resume = sa1100_serial_resume, - .driver = { - .name = "sa11x0-uart", - .owner = THIS_MODULE, - }, -}; - -static int __init sa1100_serial_init(void) -{ - int ret; - - printk(KERN_INFO "Serial: SA11x0 driver\n"); - - sa1100_init_ports(); - - ret = uart_register_driver(&sa1100_reg); - if (ret == 0) { - ret = platform_driver_register(&sa11x0_serial_driver); - if (ret) - uart_unregister_driver(&sa1100_reg); - } - return ret; -} - -static void __exit sa1100_serial_exit(void) -{ - platform_driver_unregister(&sa11x0_serial_driver); - uart_unregister_driver(&sa1100_reg); -} - -module_init(sa1100_serial_init); -module_exit(sa1100_serial_exit); - -MODULE_AUTHOR("Deep Blue Solutions Ltd"); -MODULE_DESCRIPTION("SA1100 generic serial port driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_SA1100_MAJOR); -MODULE_ALIAS("platform:sa11x0-uart"); diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c deleted file mode 100644 index 7ac2bf5167cd..000000000000 --- a/drivers/serial/samsung.c +++ /dev/null @@ -1,1487 +0,0 @@ -/* linux/drivers/serial/samsuing.c - * - * Driver core for Samsung SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * 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. -*/ - -/* Hote on 2410 error handling - * - * The s3c2410 manual has a love/hate affair with the contents of the - * UERSTAT register in the UART blocks, and keeps marking some of the - * error bits as reserved. Having checked with the s3c2410x01, - * it copes with BREAKs properly, so I am happy to ignore the RESERVED - * feature from the latter versions of the manual. - * - * If it becomes aparrent that latter versions of the 2410 remove these - * bits, then action will have to be taken to differentiate the versions - * and change the policy on BREAK - * - * BJD, 04-Nov-2004 -*/ - -#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include "samsung.h" - -/* UART name and device definitions */ - -#define S3C24XX_SERIAL_NAME "ttySAC" -#define S3C24XX_SERIAL_MAJOR 204 -#define S3C24XX_SERIAL_MINOR 64 - -/* macros to change one thing to another */ - -#define tx_enabled(port) ((port)->unused[0]) -#define rx_enabled(port) ((port)->unused[1]) - -/* flag to ignore all characters comming in */ -#define RXSTAT_DUMMY_READ (0x10000000) - -static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port) -{ - return container_of(port, struct s3c24xx_uart_port, port); -} - -/* translate a port to the device name */ - -static inline const char *s3c24xx_serial_portname(struct uart_port *port) -{ - return to_platform_device(port->dev)->name; -} - -static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) -{ - return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); -} - -static void s3c24xx_serial_rx_enable(struct uart_port *port) -{ - unsigned long flags; - unsigned int ucon, ufcon; - int count = 10000; - - spin_lock_irqsave(&port->lock, flags); - - while (--count && !s3c24xx_serial_txempty_nofifo(port)) - udelay(100); - - ufcon = rd_regl(port, S3C2410_UFCON); - ufcon |= S3C2410_UFCON_RESETRX; - wr_regl(port, S3C2410_UFCON, ufcon); - - ucon = rd_regl(port, S3C2410_UCON); - ucon |= S3C2410_UCON_RXIRQMODE; - wr_regl(port, S3C2410_UCON, ucon); - - rx_enabled(port) = 1; - spin_unlock_irqrestore(&port->lock, flags); -} - -static void s3c24xx_serial_rx_disable(struct uart_port *port) -{ - unsigned long flags; - unsigned int ucon; - - spin_lock_irqsave(&port->lock, flags); - - ucon = rd_regl(port, S3C2410_UCON); - ucon &= ~S3C2410_UCON_RXIRQMODE; - wr_regl(port, S3C2410_UCON, ucon); - - rx_enabled(port) = 0; - spin_unlock_irqrestore(&port->lock, flags); -} - -static void s3c24xx_serial_stop_tx(struct uart_port *port) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - - if (tx_enabled(port)) { - disable_irq_nosync(ourport->tx_irq); - tx_enabled(port) = 0; - if (port->flags & UPF_CONS_FLOW) - s3c24xx_serial_rx_enable(port); - } -} - -static void s3c24xx_serial_start_tx(struct uart_port *port) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - - if (!tx_enabled(port)) { - if (port->flags & UPF_CONS_FLOW) - s3c24xx_serial_rx_disable(port); - - enable_irq(ourport->tx_irq); - tx_enabled(port) = 1; - } -} - - -static void s3c24xx_serial_stop_rx(struct uart_port *port) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - - if (rx_enabled(port)) { - dbg("s3c24xx_serial_stop_rx: port=%p\n", port); - disable_irq_nosync(ourport->rx_irq); - rx_enabled(port) = 0; - } -} - -static void s3c24xx_serial_enable_ms(struct uart_port *port) -{ -} - -static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port) -{ - return to_ourport(port)->info; -} - -static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port) -{ - if (port->dev == NULL) - return NULL; - - return (struct s3c2410_uartcfg *)port->dev->platform_data; -} - -static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport, - unsigned long ufstat) -{ - struct s3c24xx_uart_info *info = ourport->info; - - if (ufstat & info->rx_fifofull) - return info->fifosize; - - return (ufstat & info->rx_fifomask) >> info->rx_fifoshift; -} - - -/* ? - where has parity gone?? */ -#define S3C2410_UERSTAT_PARITY (0x1000) - -static irqreturn_t -s3c24xx_serial_rx_chars(int irq, void *dev_id) -{ - struct s3c24xx_uart_port *ourport = dev_id; - struct uart_port *port = &ourport->port; - struct tty_struct *tty = port->state->port.tty; - unsigned int ufcon, ch, flag, ufstat, uerstat; - int max_count = 64; - - while (max_count-- > 0) { - ufcon = rd_regl(port, S3C2410_UFCON); - ufstat = rd_regl(port, S3C2410_UFSTAT); - - if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) - break; - - uerstat = rd_regl(port, S3C2410_UERSTAT); - ch = rd_regb(port, S3C2410_URXH); - - if (port->flags & UPF_CONS_FLOW) { - int txe = s3c24xx_serial_txempty_nofifo(port); - - if (rx_enabled(port)) { - if (!txe) { - rx_enabled(port) = 0; - continue; - } - } else { - if (txe) { - ufcon |= S3C2410_UFCON_RESETRX; - wr_regl(port, S3C2410_UFCON, ufcon); - rx_enabled(port) = 1; - goto out; - } - continue; - } - } - - /* insert the character into the buffer */ - - flag = TTY_NORMAL; - port->icount.rx++; - - if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) { - dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n", - ch, uerstat); - - /* check for break */ - if (uerstat & S3C2410_UERSTAT_BREAK) { - dbg("break!\n"); - port->icount.brk++; - if (uart_handle_break(port)) - goto ignore_char; - } - - if (uerstat & S3C2410_UERSTAT_FRAME) - port->icount.frame++; - if (uerstat & S3C2410_UERSTAT_OVERRUN) - port->icount.overrun++; - - uerstat &= port->read_status_mask; - - if (uerstat & S3C2410_UERSTAT_BREAK) - flag = TTY_BREAK; - else if (uerstat & S3C2410_UERSTAT_PARITY) - flag = TTY_PARITY; - else if (uerstat & (S3C2410_UERSTAT_FRAME | - S3C2410_UERSTAT_OVERRUN)) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; - - uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, - ch, flag); - - ignore_char: - continue; - } - tty_flip_buffer_push(tty); - - out: - return IRQ_HANDLED; -} - -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; - int count = 256; - - if (port->x_char) { - wr_regb(port, S3C2410_UTXH, port->x_char); - port->icount.tx++; - port->x_char = 0; - goto out; - } - - /* if there isnt anything more to transmit, or the uart is now - * stopped, disable the uart and exit - */ - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - s3c24xx_serial_stop_tx(port); - goto out; - } - - /* try and drain the buffer... */ - - while (!uart_circ_empty(xmit) && count-- > 0) { - if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) - break; - - wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - s3c24xx_serial_stop_tx(port); - - out: - return IRQ_HANDLED; -} - -static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); - unsigned long ufcon = rd_regl(port, S3C2410_UFCON); - - if (ufcon & S3C2410_UFCON_FIFOMODE) { - if ((ufstat & info->tx_fifomask) != 0 || - (ufstat & info->tx_fifofull)) - return 0; - - return 1; - } - - return s3c24xx_serial_txempty_nofifo(port); -} - -/* no modem control lines */ -static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) -{ - unsigned int umstat = rd_regb(port, S3C2410_UMSTAT); - - if (umstat & S3C2410_UMSTAT_CTS) - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; - else - return TIOCM_CAR | TIOCM_DSR; -} - -static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* todo - possibly remove AFC and do manual CTS */ -} - -static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) -{ - unsigned long flags; - unsigned int ucon; - - spin_lock_irqsave(&port->lock, flags); - - ucon = rd_regl(port, S3C2410_UCON); - - if (break_state) - ucon |= S3C2410_UCON_SBREAK; - else - ucon &= ~S3C2410_UCON_SBREAK; - - wr_regl(port, S3C2410_UCON, ucon); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void s3c24xx_serial_shutdown(struct uart_port *port) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - - if (ourport->tx_claimed) { - free_irq(ourport->tx_irq, ourport); - tx_enabled(port) = 0; - ourport->tx_claimed = 0; - } - - if (ourport->rx_claimed) { - free_irq(ourport->rx_irq, ourport); - ourport->rx_claimed = 0; - rx_enabled(port) = 0; - } -} - - -static int s3c24xx_serial_startup(struct uart_port *port) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - int ret; - - dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", - port->mapbase, port->membase); - - rx_enabled(port) = 1; - - ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, - s3c24xx_serial_portname(port), ourport); - - if (ret != 0) { - printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); - return ret; - } - - ourport->rx_claimed = 1; - - dbg("requesting tx irq...\n"); - - tx_enabled(port) = 1; - - ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, - s3c24xx_serial_portname(port), ourport); - - if (ret) { - printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); - goto err; - } - - ourport->tx_claimed = 1; - - dbg("s3c24xx_serial_startup ok\n"); - - /* the port reset code should have done the correct - * register setup for the port controls */ - - return ret; - - err: - s3c24xx_serial_shutdown(port); - return ret; -} - -/* power power management control */ - -static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, - unsigned int old) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - - ourport->pm_level = level; - - switch (level) { - case 3: - if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) - clk_disable(ourport->baudclk); - - clk_disable(ourport->clk); - break; - - case 0: - clk_enable(ourport->clk); - - if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) - clk_enable(ourport->baudclk); - - break; - default: - printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level); - } -} - -/* baud rate calculation - * - * The UARTs on the S3C2410/S3C2440 can take their clocks from a number - * of different sources, including the peripheral clock ("pclk") and an - * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk") - * with a programmable extra divisor. - * - * The following code goes through the clock sources, and calculates the - * baud clocks (and the resultant actual baud rates) and then tries to - * pick the closest one and select that. - * -*/ - - -#define MAX_CLKS (8) - -static struct s3c24xx_uart_clksrc tmp_clksrc = { - .name = "pclk", - .min_baud = 0, - .max_baud = 0, - .divisor = 1, -}; - -static inline int -s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - - return (info->get_clksrc)(port, c); -} - -static inline int -s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - - return (info->set_clksrc)(port, c); -} - -struct baud_calc { - struct s3c24xx_uart_clksrc *clksrc; - unsigned int calc; - unsigned int divslot; - unsigned int quot; - struct clk *src; -}; - -static int s3c24xx_serial_calcbaud(struct baud_calc *calc, - struct uart_port *port, - struct s3c24xx_uart_clksrc *clksrc, - unsigned int baud) -{ - struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long rate; - - calc->src = clk_get(port->dev, clksrc->name); - if (calc->src == NULL || IS_ERR(calc->src)) - return 0; - - rate = clk_get_rate(calc->src); - rate /= clksrc->divisor; - - calc->clksrc = clksrc; - - if (ourport->info->has_divslot) { - unsigned long div = rate / baud; - - /* The UDIVSLOT register on the newer UARTs allows us to - * get a divisor adjustment of 1/16th on the baud clock. - * - * We don't keep the UDIVSLOT value (the 16ths we calculated - * by not multiplying the baud by 16) as it is easy enough - * to recalculate. - */ - - calc->quot = div / 16; - calc->calc = rate / div; - } else { - calc->quot = (rate + (8 * baud)) / (16 * baud); - calc->calc = (rate / (calc->quot * 16)); - } - - calc->quot--; - return 1; -} - -static unsigned int s3c24xx_serial_getclk(struct uart_port *port, - struct s3c24xx_uart_clksrc **clksrc, - struct clk **clk, - unsigned int baud) -{ - struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); - struct s3c24xx_uart_clksrc *clkp; - struct baud_calc res[MAX_CLKS]; - struct baud_calc *resptr, *best, *sptr; - int i; - - clkp = cfg->clocks; - best = NULL; - - if (cfg->clocks_size < 2) { - if (cfg->clocks_size == 0) - clkp = &tmp_clksrc; - - /* check to see if we're sourcing fclk, and if so we're - * going to have to update the clock source - */ - - if (strcmp(clkp->name, "fclk") == 0) { - struct s3c24xx_uart_clksrc src; - - s3c24xx_serial_getsource(port, &src); - - /* check that the port already using fclk, and if - * not, then re-select fclk - */ - - if (strcmp(src.name, clkp->name) == 0) { - s3c24xx_serial_setsource(port, clkp); - s3c24xx_serial_getsource(port, &src); - } - - clkp->divisor = src.divisor; - } - - s3c24xx_serial_calcbaud(res, port, clkp, baud); - best = res; - resptr = best + 1; - } else { - resptr = res; - - for (i = 0; i < cfg->clocks_size; i++, clkp++) { - if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud)) - resptr++; - } - } - - /* ok, we now need to select the best clock we found */ - - if (!best) { - unsigned int deviation = (1<<30)|((1<<30)-1); - int calc_deviation; - - for (sptr = res; sptr < resptr; sptr++) { - calc_deviation = baud - sptr->calc; - if (calc_deviation < 0) - calc_deviation = -calc_deviation; - - if (calc_deviation < deviation) { - best = sptr; - deviation = calc_deviation; - } - } - } - - /* store results to pass back */ - - *clksrc = best->clksrc; - *clk = best->src; - - return best->quot; -} - -/* udivslot_table[] - * - * This table takes the fractional value of the baud divisor and gives - * the recommended setting for the UDIVSLOT register. - */ -static u16 udivslot_table[16] = { - [0] = 0x0000, - [1] = 0x0080, - [2] = 0x0808, - [3] = 0x0888, - [4] = 0x2222, - [5] = 0x4924, - [6] = 0x4A52, - [7] = 0x54AA, - [8] = 0x5555, - [9] = 0xD555, - [10] = 0xD5D5, - [11] = 0xDDD5, - [12] = 0xDDDD, - [13] = 0xDFDD, - [14] = 0xDFDF, - [15] = 0xFFDF, -}; - -static void s3c24xx_serial_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); - struct s3c24xx_uart_port *ourport = to_ourport(port); - struct s3c24xx_uart_clksrc *clksrc = NULL; - struct clk *clk = NULL; - unsigned long flags; - unsigned int baud, quot; - unsigned int ulcon; - unsigned int umcon; - unsigned int udivslot = 0; - - /* - * We don't support modem control lines. - */ - termios->c_cflag &= ~(HUPCL | CMSPAR); - termios->c_cflag |= CLOCAL; - - /* - * Ask the core to calculate the divisor for us. - */ - - baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); - - if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) - quot = port->custom_divisor; - else - quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); - - /* check to see if we need to change clock source */ - - if (ourport->clksrc != clksrc || ourport->baudclk != clk) { - dbg("selecting clock %p\n", clk); - s3c24xx_serial_setsource(port, clksrc); - - if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { - clk_disable(ourport->baudclk); - ourport->baudclk = NULL; - } - - clk_enable(clk); - - ourport->clksrc = clksrc; - ourport->baudclk = clk; - ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; - } - - if (ourport->info->has_divslot) { - unsigned int div = ourport->baudclk_rate / baud; - - if (cfg->has_fracval) { - udivslot = (div & 15); - dbg("fracval = %04x\n", udivslot); - } else { - udivslot = udivslot_table[div & 15]; - dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); - } - } - - switch (termios->c_cflag & CSIZE) { - case CS5: - dbg("config: 5bits/char\n"); - ulcon = S3C2410_LCON_CS5; - break; - case CS6: - dbg("config: 6bits/char\n"); - ulcon = S3C2410_LCON_CS6; - break; - case CS7: - dbg("config: 7bits/char\n"); - ulcon = S3C2410_LCON_CS7; - break; - case CS8: - default: - dbg("config: 8bits/char\n"); - ulcon = S3C2410_LCON_CS8; - break; - } - - /* preserve original lcon IR settings */ - ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); - - if (termios->c_cflag & CSTOPB) - ulcon |= S3C2410_LCON_STOPB; - - umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; - - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & PARODD) - ulcon |= S3C2410_LCON_PODD; - else - ulcon |= S3C2410_LCON_PEVEN; - } else { - ulcon |= S3C2410_LCON_PNONE; - } - - spin_lock_irqsave(&port->lock, flags); - - dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n", - ulcon, quot, udivslot); - - wr_regl(port, S3C2410_ULCON, ulcon); - wr_regl(port, S3C2410_UBRDIV, quot); - wr_regl(port, S3C2410_UMCON, umcon); - - if (ourport->info->has_divslot) - wr_regl(port, S3C2443_DIVSLOT, udivslot); - - dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", - rd_regl(port, S3C2410_ULCON), - rd_regl(port, S3C2410_UCON), - rd_regl(port, S3C2410_UFCON)); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - /* - * Which character status flags are we interested in? - */ - port->read_status_mask = S3C2410_UERSTAT_OVERRUN; - if (termios->c_iflag & INPCK) - port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; - - /* - * Which character status flags should we ignore? - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN; - if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) - port->ignore_status_mask |= S3C2410_UERSTAT_FRAME; - - /* - * Ignore all characters if CREAD is not set. - */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= RXSTAT_DUMMY_READ; - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *s3c24xx_serial_type(struct uart_port *port) -{ - switch (port->type) { - case PORT_S3C2410: - return "S3C2410"; - case PORT_S3C2440: - return "S3C2440"; - case PORT_S3C2412: - return "S3C2412"; - case PORT_S3C6400: - return "S3C6400/10"; - default: - return NULL; - } -} - -#define MAP_SIZE (0x100) - -static void s3c24xx_serial_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, MAP_SIZE); -} - -static int s3c24xx_serial_request_port(struct uart_port *port) -{ - const char *name = s3c24xx_serial_portname(port); - return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; -} - -static void s3c24xx_serial_config_port(struct uart_port *port, int flags) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - - if (flags & UART_CONFIG_TYPE && - s3c24xx_serial_request_port(port) == 0) - port->type = info->type; -} - -/* - * verify the new serial_struct (for TIOCSSERIAL). - */ -static int -s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - - if (ser->type != PORT_UNKNOWN && ser->type != info->type) - return -EINVAL; - - return 0; -} - - -#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE - -static struct console s3c24xx_serial_console; - -#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console -#else -#define S3C24XX_SERIAL_CONSOLE NULL -#endif - -static struct uart_ops s3c24xx_serial_ops = { - .pm = s3c24xx_serial_pm, - .tx_empty = s3c24xx_serial_tx_empty, - .get_mctrl = s3c24xx_serial_get_mctrl, - .set_mctrl = s3c24xx_serial_set_mctrl, - .stop_tx = s3c24xx_serial_stop_tx, - .start_tx = s3c24xx_serial_start_tx, - .stop_rx = s3c24xx_serial_stop_rx, - .enable_ms = s3c24xx_serial_enable_ms, - .break_ctl = s3c24xx_serial_break_ctl, - .startup = s3c24xx_serial_startup, - .shutdown = s3c24xx_serial_shutdown, - .set_termios = s3c24xx_serial_set_termios, - .type = s3c24xx_serial_type, - .release_port = s3c24xx_serial_release_port, - .request_port = s3c24xx_serial_request_port, - .config_port = s3c24xx_serial_config_port, - .verify_port = s3c24xx_serial_verify_port, -}; - - -static struct uart_driver s3c24xx_uart_drv = { - .owner = THIS_MODULE, - .dev_name = "s3c2410_serial", - .nr = CONFIG_SERIAL_SAMSUNG_UARTS, - .cons = S3C24XX_SERIAL_CONSOLE, - .driver_name = S3C24XX_SERIAL_NAME, - .major = S3C24XX_SERIAL_MAJOR, - .minor = S3C24XX_SERIAL_MINOR, -}; - -static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { - [0] = { - .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), - .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX0, - .uartclk = 0, - .fifosize = 16, - .ops = &s3c24xx_serial_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 0, - } - }, - [1] = { - .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), - .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX1, - .uartclk = 0, - .fifosize = 16, - .ops = &s3c24xx_serial_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 1, - } - }, -#if CONFIG_SERIAL_SAMSUNG_UARTS > 2 - - [2] = { - .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), - .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX2, - .uartclk = 0, - .fifosize = 16, - .ops = &s3c24xx_serial_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 2, - } - }, -#endif -#if CONFIG_SERIAL_SAMSUNG_UARTS > 3 - [3] = { - .port = { - .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), - .iotype = UPIO_MEM, - .irq = IRQ_S3CUART_RX3, - .uartclk = 0, - .fifosize = 16, - .ops = &s3c24xx_serial_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 3, - } - } -#endif -}; - -/* s3c24xx_serial_resetport - * - * wrapper to call the specific reset for this port (reset the fifos - * and the settings) -*/ - -static inline int s3c24xx_serial_resetport(struct uart_port *port, - struct s3c2410_uartcfg *cfg) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - - return (info->reset_port)(port, cfg); -} - - -#ifdef CONFIG_CPU_FREQ - -static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, - unsigned long val, void *data) -{ - struct s3c24xx_uart_port *port; - struct uart_port *uport; - - port = container_of(nb, struct s3c24xx_uart_port, freq_transition); - uport = &port->port; - - /* check to see if port is enabled */ - - if (port->pm_level != 0) - return 0; - - /* try and work out if the baudrate is changing, we can detect - * a change in rate, but we do not have support for detecting - * a disturbance in the clock-rate over the change. - */ - - if (IS_ERR(port->clk)) - goto exit; - - if (port->baudclk_rate == clk_get_rate(port->clk)) - goto exit; - - if (val == CPUFREQ_PRECHANGE) { - /* we should really shut the port down whilst the - * frequency change is in progress. */ - - } else if (val == CPUFREQ_POSTCHANGE) { - struct ktermios *termios; - struct tty_struct *tty; - - if (uport->state == NULL) - goto exit; - - tty = uport->state->port.tty; - - if (tty == NULL) - goto exit; - - termios = tty->termios; - - if (termios == NULL) { - printk(KERN_WARNING "%s: no termios?\n", __func__); - goto exit; - } - - s3c24xx_serial_set_termios(uport, termios, NULL); - } - - exit: - return 0; -} - -static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) -{ - port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; - - return cpufreq_register_notifier(&port->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) -{ - cpufreq_unregister_notifier(&port->freq_transition, - CPUFREQ_TRANSITION_NOTIFIER); -} - -#else -static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) -{ - return 0; -} - -static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) -{ -} -#endif - -/* s3c24xx_serial_init_port - * - * initialise a single serial port from the platform device given - */ - -static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, - struct s3c24xx_uart_info *info, - struct platform_device *platdev) -{ - struct uart_port *port = &ourport->port; - struct s3c2410_uartcfg *cfg; - struct resource *res; - int ret; - - dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev); - - if (platdev == NULL) - return -ENODEV; - - cfg = s3c24xx_dev_to_cfg(&platdev->dev); - - if (port->mapbase != 0) - return 0; - - if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) { - printk(KERN_ERR "%s: port %d bigger than %d\n", __func__, - cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS); - return -ERANGE; - } - - /* setup info for port */ - port->dev = &platdev->dev; - ourport->info = info; - - /* copy the info in from provided structure */ - ourport->port.fifosize = info->fifosize; - - dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport); - - port->uartclk = 1; - - if (cfg->uart_flags & UPF_CONS_FLOW) { - dbg("s3c24xx_serial_init_port: enabling flow control\n"); - port->flags |= UPF_CONS_FLOW; - } - - /* sort our the physical and virtual addresses for each UART */ - - res = platform_get_resource(platdev, IORESOURCE_MEM, 0); - if (res == NULL) { - printk(KERN_ERR "failed to find memory resource for uart\n"); - return -EINVAL; - } - - dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); - - port->mapbase = res->start; - port->membase = S3C_VA_UART + (res->start & 0xfffff); - ret = platform_get_irq(platdev, 0); - if (ret < 0) - port->irq = 0; - else { - port->irq = ret; - ourport->rx_irq = ret; - ourport->tx_irq = ret + 1; - } - - ret = platform_get_irq(platdev, 1); - if (ret > 0) - ourport->tx_irq = ret; - - ourport->clk = clk_get(&platdev->dev, "uart"); - - dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", - port->mapbase, port->membase, port->irq, - ourport->rx_irq, ourport->tx_irq, port->uartclk); - - /* reset the fifos (and setup the uart) */ - s3c24xx_serial_resetport(port, cfg); - return 0; -} - -static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct uart_port *port = s3c24xx_dev_to_port(dev); - struct s3c24xx_uart_port *ourport = to_ourport(port); - - return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name); -} - -static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); - -/* Device driver serial port probe */ - -static int probe_index; - -int s3c24xx_serial_probe(struct platform_device *dev, - struct s3c24xx_uart_info *info) -{ - struct s3c24xx_uart_port *ourport; - int ret; - - dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); - - ourport = &s3c24xx_serial_ports[probe_index]; - probe_index++; - - dbg("%s: initialising port %p...\n", __func__, ourport); - - ret = s3c24xx_serial_init_port(ourport, info, dev); - if (ret < 0) - goto probe_err; - - dbg("%s: adding port\n", __func__); - uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); - platform_set_drvdata(dev, &ourport->port); - - ret = device_create_file(&dev->dev, &dev_attr_clock_source); - if (ret < 0) - printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); - - ret = s3c24xx_serial_cpufreq_register(ourport); - if (ret < 0) - dev_err(&dev->dev, "failed to add cpufreq notifier\n"); - - return 0; - - probe_err: - return ret; -} - -EXPORT_SYMBOL_GPL(s3c24xx_serial_probe); - -int __devexit s3c24xx_serial_remove(struct platform_device *dev) -{ - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); - - if (port) { - s3c24xx_serial_cpufreq_deregister(to_ourport(port)); - device_remove_file(&dev->dev, &dev_attr_clock_source); - uart_remove_one_port(&s3c24xx_uart_drv, port); - } - - return 0; -} - -EXPORT_SYMBOL_GPL(s3c24xx_serial_remove); - -/* UART power management code */ - -#ifdef CONFIG_PM - -static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state) -{ - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); - - if (port) - uart_suspend_port(&s3c24xx_uart_drv, port); - - return 0; -} - -static int s3c24xx_serial_resume(struct platform_device *dev) -{ - struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); - struct s3c24xx_uart_port *ourport = to_ourport(port); - - if (port) { - clk_enable(ourport->clk); - s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); - clk_disable(ourport->clk); - - uart_resume_port(&s3c24xx_uart_drv, port); - } - - return 0; -} -#endif - -int s3c24xx_serial_init(struct platform_driver *drv, - struct s3c24xx_uart_info *info) -{ - dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); - -#ifdef CONFIG_PM - drv->suspend = s3c24xx_serial_suspend; - drv->resume = s3c24xx_serial_resume; -#endif - - return platform_driver_register(drv); -} - -EXPORT_SYMBOL_GPL(s3c24xx_serial_init); - -/* module initialisation code */ - -static int __init s3c24xx_serial_modinit(void) -{ - int ret; - - ret = uart_register_driver(&s3c24xx_uart_drv); - if (ret < 0) { - printk(KERN_ERR "failed to register UART driver\n"); - return -1; - } - - return 0; -} - -static void __exit s3c24xx_serial_modexit(void) -{ - uart_unregister_driver(&s3c24xx_uart_drv); -} - -module_init(s3c24xx_serial_modinit); -module_exit(s3c24xx_serial_modexit); - -/* Console code */ - -#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE - -static struct uart_port *cons_uart; - -static int -s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) -{ - struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned long ufstat, utrstat; - - if (ufcon & S3C2410_UFCON_FIFOMODE) { - /* fifo mode - check amount of data in fifo registers... */ - - ufstat = rd_regl(port, S3C2410_UFSTAT); - return (ufstat & info->tx_fifofull) ? 0 : 1; - } - - /* in non-fifo mode, we go and use the tx buffer empty */ - - utrstat = rd_regl(port, S3C2410_UTRSTAT); - return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; -} - -static void -s3c24xx_serial_console_putchar(struct uart_port *port, int ch) -{ - unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); - while (!s3c24xx_serial_console_txrdy(port, ufcon)) - barrier(); - wr_regb(cons_uart, S3C2410_UTXH, ch); -} - -static void -s3c24xx_serial_console_write(struct console *co, const char *s, - unsigned int count) -{ - uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); -} - -static void __init -s3c24xx_serial_get_options(struct uart_port *port, int *baud, - int *parity, int *bits) -{ - struct s3c24xx_uart_clksrc clksrc; - struct clk *clk; - unsigned int ulcon; - unsigned int ucon; - unsigned int ubrdiv; - unsigned long rate; - - ulcon = rd_regl(port, S3C2410_ULCON); - ucon = rd_regl(port, S3C2410_UCON); - ubrdiv = rd_regl(port, S3C2410_UBRDIV); - - dbg("s3c24xx_serial_get_options: port=%p\n" - "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", - port, ulcon, ucon, ubrdiv); - - if ((ucon & 0xf) != 0) { - /* consider the serial port configured if the tx/rx mode set */ - - switch (ulcon & S3C2410_LCON_CSMASK) { - case S3C2410_LCON_CS5: - *bits = 5; - break; - case S3C2410_LCON_CS6: - *bits = 6; - break; - case S3C2410_LCON_CS7: - *bits = 7; - break; - default: - case S3C2410_LCON_CS8: - *bits = 8; - break; - } - - switch (ulcon & S3C2410_LCON_PMASK) { - case S3C2410_LCON_PEVEN: - *parity = 'e'; - break; - - case S3C2410_LCON_PODD: - *parity = 'o'; - break; - - case S3C2410_LCON_PNONE: - default: - *parity = 'n'; - } - - /* now calculate the baud rate */ - - s3c24xx_serial_getsource(port, &clksrc); - - clk = clk_get(port->dev, clksrc.name); - if (!IS_ERR(clk) && clk != NULL) - rate = clk_get_rate(clk) / clksrc.divisor; - else - rate = 1; - - - *baud = rate / (16 * (ubrdiv + 1)); - dbg("calculated baud %d\n", *baud); - } - -} - -/* s3c24xx_serial_init_ports - * - * initialise the serial ports from the machine provided initialisation - * data. -*/ - -static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) -{ - struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; - struct platform_device **platdev_ptr; - int i; - - dbg("s3c24xx_serial_init_ports: initialising ports...\n"); - - platdev_ptr = s3c24xx_uart_devs; - - for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { - s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); - } - - return 0; -} - -static int __init -s3c24xx_serial_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n", - co, co->index, options); - - /* is this a valid port */ - - if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS) - co->index = 0; - - port = &s3c24xx_serial_ports[co->index].port; - - /* is the port configured? */ - - if (port->mapbase == 0x0) { - co->index = 0; - port = &s3c24xx_serial_ports[co->index].port; - } - - cons_uart = port; - - dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index); - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - s3c24xx_serial_get_options(port, &baud, &parity, &bits); - - dbg("s3c24xx_serial_console_setup: baud %d\n", baud); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -/* s3c24xx_serial_initconsole - * - * initialise the console from one of the uart drivers -*/ - -static struct console s3c24xx_serial_console = { - .name = S3C24XX_SERIAL_NAME, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .write = s3c24xx_serial_console_write, - .setup = s3c24xx_serial_console_setup -}; - -int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info **info) - -{ - struct platform_device *dev = s3c24xx_uart_devs[0]; - - dbg("s3c24xx_serial_initconsole\n"); - - /* select driver based on the cpu */ - - if (dev == NULL) { - printk(KERN_ERR "s3c24xx: no devices for console init\n"); - return 0; - } - - if (strcmp(dev->name, drv->driver.name) != 0) - return 0; - - s3c24xx_serial_console.data = &s3c24xx_uart_drv; - s3c24xx_serial_init_ports(info); - - register_console(&s3c24xx_serial_console); - return 0; -} - -#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */ - -MODULE_DESCRIPTION("Samsung SoC Serial port driver"); -MODULE_AUTHOR("Ben Dooks "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h deleted file mode 100644 index 0ac06a07d25f..000000000000 --- a/drivers/serial/samsung.h +++ /dev/null @@ -1,120 +0,0 @@ -/* linux/drivers/serial/samsung.h - * - * Driver for Samsung SoC onboard UARTs. - * - * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * - * 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. -*/ - -struct s3c24xx_uart_info { - char *name; - unsigned int type; - unsigned int fifosize; - unsigned long rx_fifomask; - unsigned long rx_fifoshift; - unsigned long rx_fifofull; - unsigned long tx_fifomask; - unsigned long tx_fifoshift; - unsigned long tx_fifofull; - - /* uart port features */ - - unsigned int has_divslot:1; - - /* clock source control */ - - int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); - int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); - - /* uart controls */ - int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *); -}; - -struct s3c24xx_uart_port { - unsigned char rx_claimed; - unsigned char tx_claimed; - unsigned int pm_level; - unsigned long baudclk_rate; - - unsigned int rx_irq; - unsigned int tx_irq; - - struct s3c24xx_uart_info *info; - struct s3c24xx_uart_clksrc *clksrc; - struct clk *clk; - struct clk *baudclk; - struct uart_port port; - -#ifdef CONFIG_CPU_FREQ - struct notifier_block freq_transition; -#endif -}; - -/* conversion functions */ - -#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev) -#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data) - -/* register access controls */ - -#define portaddr(port, reg) ((port)->membase + (reg)) - -#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) -#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) - -#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) -#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) - -extern int s3c24xx_serial_probe(struct platform_device *dev, - struct s3c24xx_uart_info *uart); - -extern int __devexit s3c24xx_serial_remove(struct platform_device *dev); - -extern int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info **uart); - -extern int s3c24xx_serial_init(struct platform_driver *drv, - struct s3c24xx_uart_info *info); - -#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE - -#define s3c24xx_console_init(__drv, __inf) \ -static int __init s3c_serial_console_init(void) \ -{ \ - struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \ - int i; \ - \ - for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \ - uinfo[i] = __inf; \ - return s3c24xx_serial_initconsole(__drv, uinfo); \ -} \ - \ -console_initcall(s3c_serial_console_init) - -#else -#define s3c24xx_console_init(drv, inf) extern void no_console(void) -#endif - -#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG - -extern void printascii(const char *); - -static void dbg(const char *fmt, ...) -{ - va_list va; - char buff[256]; - - va_start(va, fmt); - vsprintf(buff, fmt, va); - va_end(va); - - printascii(buff); -} - -#else -#define dbg(x...) do { } while (0) -#endif diff --git a/drivers/serial/sb1250-duart.c b/drivers/serial/sb1250-duart.c deleted file mode 100644 index a2f2b3254499..000000000000 --- a/drivers/serial/sb1250-duart.c +++ /dev/null @@ -1,976 +0,0 @@ -/* - * drivers/serial/sb1250-duart.c - * - * Support for the asynchronous serial interface (DUART) included - * in the BCM1250 and derived System-On-a-Chip (SOC) devices. - * - * Copyright (c) 2007 Maciej W. Rozycki - * - * Derived from drivers/char/sb1250_duart.c for which the following - * copyright applies: - * - * Copyright (c) 2000, 2001, 2002, 2003, 2004 Broadcom Corporation - * - * 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. - * - * References: - * - * "BCM1250/BCM1125/BCM1125H User Manual", Broadcom Corporation - */ - -#if defined(CONFIG_SERIAL_SB1250_DUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - - -#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) -#include -#include - -#define SBD_CHANREGS(line) A_BCM1480_DUART_CHANREG((line), 0) -#define SBD_CTRLREGS(line) A_BCM1480_DUART_CTRLREG((line), 0) -#define SBD_INT(line) (K_BCM1480_INT_UART_0 + (line)) - -#define DUART_CHANREG_SPACING BCM1480_DUART_CHANREG_SPACING - -#define R_DUART_IMRREG(line) R_BCM1480_DUART_IMRREG(line) -#define R_DUART_INCHREG(line) R_BCM1480_DUART_INCHREG(line) -#define R_DUART_ISRREG(line) R_BCM1480_DUART_ISRREG(line) - -#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X) -#include -#include - -#define SBD_CHANREGS(line) A_DUART_CHANREG((line), 0) -#define SBD_CTRLREGS(line) A_DUART_CTRLREG(0) -#define SBD_INT(line) (K_INT_UART_0 + (line)) - -#else -#error invalid SB1250 UART configuration - -#endif - - -MODULE_AUTHOR("Maciej W. Rozycki "); -MODULE_DESCRIPTION("BCM1xxx on-chip DUART serial driver"); -MODULE_LICENSE("GPL"); - - -#define DUART_MAX_CHIP 2 -#define DUART_MAX_SIDE 2 - -/* - * Per-port state. - */ -struct sbd_port { - struct sbd_duart *duart; - struct uart_port port; - unsigned char __iomem *memctrl; - int tx_stopped; - int initialised; -}; - -/* - * Per-DUART state for the shared register space. - */ -struct sbd_duart { - struct sbd_port sport[2]; - unsigned long mapctrl; - atomic_t map_guard; -}; - -#define to_sport(uport) container_of(uport, struct sbd_port, port) - -static struct sbd_duart sbd_duarts[DUART_MAX_CHIP]; - - -/* - * Reading and writing SB1250 DUART registers. - * - * There are three register spaces: two per-channel ones and - * a shared one. We have to define accessors appropriately. - * All registers are 64-bit and all but the Baud Rate Clock - * registers only define 8 least significant bits. There is - * also a workaround to take into account. Raw accessors use - * the full register width, but cooked ones truncate it - * intentionally so that the rest of the driver does not care. - */ -static u64 __read_sbdchn(struct sbd_port *sport, int reg) -{ - void __iomem *csr = sport->port.membase + reg; - - return __raw_readq(csr); -} - -static u64 __read_sbdshr(struct sbd_port *sport, int reg) -{ - void __iomem *csr = sport->memctrl + reg; - - return __raw_readq(csr); -} - -static void __write_sbdchn(struct sbd_port *sport, int reg, u64 value) -{ - void __iomem *csr = sport->port.membase + reg; - - __raw_writeq(value, csr); -} - -static void __write_sbdshr(struct sbd_port *sport, int reg, u64 value) -{ - void __iomem *csr = sport->memctrl + reg; - - __raw_writeq(value, csr); -} - -/* - * In bug 1956, we get glitches that can mess up uart registers. This - * "read-mode-reg after any register access" is an accepted workaround. - */ -static void __war_sbd1956(struct sbd_port *sport) -{ - __read_sbdchn(sport, R_DUART_MODE_REG_1); - __read_sbdchn(sport, R_DUART_MODE_REG_2); -} - -static unsigned char read_sbdchn(struct sbd_port *sport, int reg) -{ - unsigned char retval; - - retval = __read_sbdchn(sport, reg); - if (SIBYTE_1956_WAR) - __war_sbd1956(sport); - return retval; -} - -static unsigned char read_sbdshr(struct sbd_port *sport, int reg) -{ - unsigned char retval; - - retval = __read_sbdshr(sport, reg); - if (SIBYTE_1956_WAR) - __war_sbd1956(sport); - return retval; -} - -static void write_sbdchn(struct sbd_port *sport, int reg, unsigned int value) -{ - __write_sbdchn(sport, reg, value); - if (SIBYTE_1956_WAR) - __war_sbd1956(sport); -} - -static void write_sbdshr(struct sbd_port *sport, int reg, unsigned int value) -{ - __write_sbdshr(sport, reg, value); - if (SIBYTE_1956_WAR) - __war_sbd1956(sport); -} - - -static int sbd_receive_ready(struct sbd_port *sport) -{ - return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_RX_RDY; -} - -static int sbd_receive_drain(struct sbd_port *sport) -{ - int loops = 10000; - - while (sbd_receive_ready(sport) && --loops) - read_sbdchn(sport, R_DUART_RX_HOLD); - return loops; -} - -static int __maybe_unused sbd_transmit_ready(struct sbd_port *sport) -{ - return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_RDY; -} - -static int __maybe_unused sbd_transmit_drain(struct sbd_port *sport) -{ - int loops = 10000; - - while (!sbd_transmit_ready(sport) && --loops) - udelay(2); - return loops; -} - -static int sbd_transmit_empty(struct sbd_port *sport) -{ - return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_EMT; -} - -static int sbd_line_drain(struct sbd_port *sport) -{ - int loops = 10000; - - while (!sbd_transmit_empty(sport) && --loops) - udelay(2); - return loops; -} - - -static unsigned int sbd_tx_empty(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - - return sbd_transmit_empty(sport) ? TIOCSER_TEMT : 0; -} - -static unsigned int sbd_get_mctrl(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - unsigned int mctrl, status; - - status = read_sbdshr(sport, R_DUART_IN_PORT); - status >>= (uport->line) % 2; - mctrl = (!(status & M_DUART_IN_PIN0_VAL) ? TIOCM_CTS : 0) | - (!(status & M_DUART_IN_PIN4_VAL) ? TIOCM_CAR : 0) | - (!(status & M_DUART_RIN0_PIN) ? TIOCM_RNG : 0) | - (!(status & M_DUART_IN_PIN2_VAL) ? TIOCM_DSR : 0); - return mctrl; -} - -static void sbd_set_mctrl(struct uart_port *uport, unsigned int mctrl) -{ - struct sbd_port *sport = to_sport(uport); - unsigned int clr = 0, set = 0, mode2; - - if (mctrl & TIOCM_DTR) - set |= M_DUART_SET_OPR2; - else - clr |= M_DUART_CLR_OPR2; - if (mctrl & TIOCM_RTS) - set |= M_DUART_SET_OPR0; - else - clr |= M_DUART_CLR_OPR0; - clr <<= (uport->line) % 2; - set <<= (uport->line) % 2; - - mode2 = read_sbdchn(sport, R_DUART_MODE_REG_2); - mode2 &= ~M_DUART_CHAN_MODE; - if (mctrl & TIOCM_LOOP) - mode2 |= V_DUART_CHAN_MODE_LCL_LOOP; - else - mode2 |= V_DUART_CHAN_MODE_NORMAL; - - write_sbdshr(sport, R_DUART_CLEAR_OPR, clr); - write_sbdshr(sport, R_DUART_SET_OPR, set); - write_sbdchn(sport, R_DUART_MODE_REG_2, mode2); -} - -static void sbd_stop_tx(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS); - sport->tx_stopped = 1; -}; - -static void sbd_start_tx(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - unsigned int mask; - - /* Enable tx interrupts. */ - mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); - mask |= M_DUART_IMR_TX; - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); - - /* Go!, go!, go!... */ - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN); - sport->tx_stopped = 0; -}; - -static void sbd_stop_rx(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0); -}; - -static void sbd_enable_ms(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - - write_sbdchn(sport, R_DUART_AUXCTL_X, - M_DUART_CIN_CHNG_ENA | M_DUART_CTS_CHNG_ENA); -} - -static void sbd_break_ctl(struct uart_port *uport, int break_state) -{ - struct sbd_port *sport = to_sport(uport); - - if (break_state == -1) - write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_START_BREAK); - else - write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_STOP_BREAK); -} - - -static void sbd_receive_chars(struct sbd_port *sport) -{ - struct uart_port *uport = &sport->port; - struct uart_icount *icount; - unsigned int status, ch, flag; - int count; - - for (count = 16; count; count--) { - status = read_sbdchn(sport, R_DUART_STATUS); - if (!(status & M_DUART_RX_RDY)) - break; - - ch = read_sbdchn(sport, R_DUART_RX_HOLD); - - flag = TTY_NORMAL; - - icount = &uport->icount; - icount->rx++; - - if (unlikely(status & - (M_DUART_RCVD_BRK | M_DUART_FRM_ERR | - M_DUART_PARITY_ERR | M_DUART_OVRUN_ERR))) { - if (status & M_DUART_RCVD_BRK) { - icount->brk++; - if (uart_handle_break(uport)) - continue; - } else if (status & M_DUART_FRM_ERR) - icount->frame++; - else if (status & M_DUART_PARITY_ERR) - icount->parity++; - if (status & M_DUART_OVRUN_ERR) - icount->overrun++; - - status &= uport->read_status_mask; - if (status & M_DUART_RCVD_BRK) - flag = TTY_BREAK; - else if (status & M_DUART_FRM_ERR) - flag = TTY_FRAME; - else if (status & M_DUART_PARITY_ERR) - flag = TTY_PARITY; - } - - if (uart_handle_sysrq_char(uport, ch)) - continue; - - uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag); - } - - tty_flip_buffer_push(uport->state->port.tty); -} - -static void sbd_transmit_chars(struct sbd_port *sport) -{ - struct uart_port *uport = &sport->port; - struct circ_buf *xmit = &sport->port.state->xmit; - unsigned int mask; - int stop_tx; - - /* XON/XOFF chars. */ - if (sport->port.x_char) { - write_sbdchn(sport, R_DUART_TX_HOLD, sport->port.x_char); - sport->port.icount.tx++; - sport->port.x_char = 0; - return; - } - - /* If nothing to do or stopped or hardware stopped. */ - stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)); - - /* Send char. */ - if (!stop_tx) { - write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - sport->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&sport->port); - } - - /* Are we are done? */ - if (stop_tx || uart_circ_empty(xmit)) { - /* Disable tx interrupts. */ - mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); - mask &= ~M_DUART_IMR_TX; - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); - } -} - -static void sbd_status_handle(struct sbd_port *sport) -{ - struct uart_port *uport = &sport->port; - unsigned int delta; - - delta = read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2)); - delta >>= (uport->line) % 2; - - if (delta & (M_DUART_IN_PIN0_VAL << S_DUART_IN_PIN_CHNG)) - uart_handle_cts_change(uport, !(delta & M_DUART_IN_PIN0_VAL)); - - if (delta & (M_DUART_IN_PIN2_VAL << S_DUART_IN_PIN_CHNG)) - uport->icount.dsr++; - - if (delta & ((M_DUART_IN_PIN2_VAL | M_DUART_IN_PIN0_VAL) << - S_DUART_IN_PIN_CHNG)) - wake_up_interruptible(&uport->state->port.delta_msr_wait); -} - -static irqreturn_t sbd_interrupt(int irq, void *dev_id) -{ - struct sbd_port *sport = dev_id; - struct uart_port *uport = &sport->port; - irqreturn_t status = IRQ_NONE; - unsigned int intstat; - int count; - - for (count = 16; count; count--) { - intstat = read_sbdshr(sport, - R_DUART_ISRREG((uport->line) % 2)); - intstat &= read_sbdshr(sport, - R_DUART_IMRREG((uport->line) % 2)); - intstat &= M_DUART_ISR_ALL; - if (!intstat) - break; - - if (intstat & M_DUART_ISR_RX) - sbd_receive_chars(sport); - if (intstat & M_DUART_ISR_IN) - sbd_status_handle(sport); - if (intstat & M_DUART_ISR_TX) - sbd_transmit_chars(sport); - - status = IRQ_HANDLED; - } - - return status; -} - - -static int sbd_startup(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - unsigned int mode1; - int ret; - - ret = request_irq(sport->port.irq, sbd_interrupt, - IRQF_SHARED, "sb1250-duart", sport); - if (ret) - return ret; - - /* Clear the receive FIFO. */ - sbd_receive_drain(sport); - - /* Clear the interrupt registers. */ - write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT); - read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2)); - - /* Set rx/tx interrupt to FIFO available. */ - mode1 = read_sbdchn(sport, R_DUART_MODE_REG_1); - mode1 &= ~(M_DUART_RX_IRQ_SEL_RXFULL | M_DUART_TX_IRQ_SEL_TXEMPT); - write_sbdchn(sport, R_DUART_MODE_REG_1, mode1); - - /* Disable tx, enable rx. */ - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_EN); - sport->tx_stopped = 1; - - /* Enable interrupts. */ - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), - M_DUART_IMR_IN | M_DUART_IMR_RX); - - return 0; -} - -static void sbd_shutdown(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_DIS); - sport->tx_stopped = 1; - free_irq(sport->port.irq, sport); -} - - -static void sbd_init_port(struct sbd_port *sport) -{ - struct uart_port *uport = &sport->port; - - if (sport->initialised) - return; - - /* There is no DUART reset feature, so just set some sane defaults. */ - write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_TX); - write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_RX); - write_sbdchn(sport, R_DUART_MODE_REG_1, V_DUART_BITS_PER_CHAR_8); - write_sbdchn(sport, R_DUART_MODE_REG_2, 0); - write_sbdchn(sport, R_DUART_FULL_CTL, - V_DUART_INT_TIME(0) | V_DUART_SIG_FULL(15)); - write_sbdchn(sport, R_DUART_OPCR_X, 0); - write_sbdchn(sport, R_DUART_AUXCTL_X, 0); - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0); - - sport->initialised = 1; -} - -static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, - struct ktermios *old_termios) -{ - struct sbd_port *sport = to_sport(uport); - unsigned int mode1 = 0, mode2 = 0, aux = 0; - unsigned int mode1mask = 0, mode2mask = 0, auxmask = 0; - unsigned int oldmode1, oldmode2, oldaux; - unsigned int baud, brg; - unsigned int command; - - mode1mask |= ~(M_DUART_PARITY_MODE | M_DUART_PARITY_TYPE_ODD | - M_DUART_BITS_PER_CHAR); - mode2mask |= ~M_DUART_STOP_BIT_LEN_2; - auxmask |= ~M_DUART_CTS_CHNG_ENA; - - /* Byte size. */ - switch (termios->c_cflag & CSIZE) { - case CS5: - case CS6: - /* Unsupported, leave unchanged. */ - mode1mask |= M_DUART_PARITY_MODE; - break; - case CS7: - mode1 |= V_DUART_BITS_PER_CHAR_7; - break; - case CS8: - default: - mode1 |= V_DUART_BITS_PER_CHAR_8; - break; - } - - /* Parity and stop bits. */ - if (termios->c_cflag & CSTOPB) - mode2 |= M_DUART_STOP_BIT_LEN_2; - else - mode2 |= M_DUART_STOP_BIT_LEN_1; - if (termios->c_cflag & PARENB) - mode1 |= V_DUART_PARITY_MODE_ADD; - else - mode1 |= V_DUART_PARITY_MODE_NONE; - if (termios->c_cflag & PARODD) - mode1 |= M_DUART_PARITY_TYPE_ODD; - else - mode1 |= M_DUART_PARITY_TYPE_EVEN; - - baud = uart_get_baud_rate(uport, termios, old_termios, 1200, 5000000); - brg = V_DUART_BAUD_RATE(baud); - /* The actual lower bound is 1221bps, so compensate. */ - if (brg > M_DUART_CLK_COUNTER) - brg = M_DUART_CLK_COUNTER; - - uart_update_timeout(uport, termios->c_cflag, baud); - - uport->read_status_mask = M_DUART_OVRUN_ERR; - if (termios->c_iflag & INPCK) - uport->read_status_mask |= M_DUART_FRM_ERR | - M_DUART_PARITY_ERR; - if (termios->c_iflag & (BRKINT | PARMRK)) - uport->read_status_mask |= M_DUART_RCVD_BRK; - - uport->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - uport->ignore_status_mask |= M_DUART_FRM_ERR | - M_DUART_PARITY_ERR; - if (termios->c_iflag & IGNBRK) { - uport->ignore_status_mask |= M_DUART_RCVD_BRK; - if (termios->c_iflag & IGNPAR) - uport->ignore_status_mask |= M_DUART_OVRUN_ERR; - } - - if (termios->c_cflag & CREAD) - command = M_DUART_RX_EN; - else - command = M_DUART_RX_DIS; - - if (termios->c_cflag & CRTSCTS) - aux |= M_DUART_CTS_CHNG_ENA; - else - aux &= ~M_DUART_CTS_CHNG_ENA; - - spin_lock(&uport->lock); - - if (sport->tx_stopped) - command |= M_DUART_TX_DIS; - else - command |= M_DUART_TX_EN; - - oldmode1 = read_sbdchn(sport, R_DUART_MODE_REG_1) & mode1mask; - oldmode2 = read_sbdchn(sport, R_DUART_MODE_REG_2) & mode2mask; - oldaux = read_sbdchn(sport, R_DUART_AUXCTL_X) & auxmask; - - if (!sport->tx_stopped) - sbd_line_drain(sport); - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_DIS); - - write_sbdchn(sport, R_DUART_MODE_REG_1, mode1 | oldmode1); - write_sbdchn(sport, R_DUART_MODE_REG_2, mode2 | oldmode2); - write_sbdchn(sport, R_DUART_CLK_SEL, brg); - write_sbdchn(sport, R_DUART_AUXCTL_X, aux | oldaux); - - write_sbdchn(sport, R_DUART_CMD, command); - - spin_unlock(&uport->lock); -} - - -static const char *sbd_type(struct uart_port *uport) -{ - return "SB1250 DUART"; -} - -static void sbd_release_port(struct uart_port *uport) -{ - struct sbd_port *sport = to_sport(uport); - struct sbd_duart *duart = sport->duart; - int map_guard; - - iounmap(sport->memctrl); - sport->memctrl = NULL; - iounmap(uport->membase); - uport->membase = NULL; - - map_guard = atomic_add_return(-1, &duart->map_guard); - if (!map_guard) - release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING); - release_mem_region(uport->mapbase, DUART_CHANREG_SPACING); -} - -static int sbd_map_port(struct uart_port *uport) -{ - const char *err = KERN_ERR "sbd: Cannot map MMIO\n"; - struct sbd_port *sport = to_sport(uport); - struct sbd_duart *duart = sport->duart; - - if (!uport->membase) - uport->membase = ioremap_nocache(uport->mapbase, - DUART_CHANREG_SPACING); - if (!uport->membase) { - printk(err); - return -ENOMEM; - } - - if (!sport->memctrl) - sport->memctrl = ioremap_nocache(duart->mapctrl, - DUART_CHANREG_SPACING); - if (!sport->memctrl) { - printk(err); - iounmap(uport->membase); - uport->membase = NULL; - return -ENOMEM; - } - - return 0; -} - -static int sbd_request_port(struct uart_port *uport) -{ - const char *err = KERN_ERR "sbd: Unable to reserve MMIO resource\n"; - struct sbd_duart *duart = to_sport(uport)->duart; - int map_guard; - int ret = 0; - - if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING, - "sb1250-duart")) { - printk(err); - return -EBUSY; - } - map_guard = atomic_add_return(1, &duart->map_guard); - if (map_guard == 1) { - if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING, - "sb1250-duart")) { - atomic_add(-1, &duart->map_guard); - printk(err); - ret = -EBUSY; - } - } - if (!ret) { - ret = sbd_map_port(uport); - if (ret) { - map_guard = atomic_add_return(-1, &duart->map_guard); - if (!map_guard) - release_mem_region(duart->mapctrl, - DUART_CHANREG_SPACING); - } - } - if (ret) { - release_mem_region(uport->mapbase, DUART_CHANREG_SPACING); - return ret; - } - return 0; -} - -static void sbd_config_port(struct uart_port *uport, int flags) -{ - struct sbd_port *sport = to_sport(uport); - - if (flags & UART_CONFIG_TYPE) { - if (sbd_request_port(uport)) - return; - - uport->type = PORT_SB1250_DUART; - - sbd_init_port(sport); - } -} - -static int sbd_verify_port(struct uart_port *uport, struct serial_struct *ser) -{ - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_SB1250_DUART) - ret = -EINVAL; - if (ser->irq != uport->irq) - ret = -EINVAL; - if (ser->baud_base != uport->uartclk / 16) - ret = -EINVAL; - return ret; -} - - -static const struct uart_ops sbd_ops = { - .tx_empty = sbd_tx_empty, - .set_mctrl = sbd_set_mctrl, - .get_mctrl = sbd_get_mctrl, - .stop_tx = sbd_stop_tx, - .start_tx = sbd_start_tx, - .stop_rx = sbd_stop_rx, - .enable_ms = sbd_enable_ms, - .break_ctl = sbd_break_ctl, - .startup = sbd_startup, - .shutdown = sbd_shutdown, - .set_termios = sbd_set_termios, - .type = sbd_type, - .release_port = sbd_release_port, - .request_port = sbd_request_port, - .config_port = sbd_config_port, - .verify_port = sbd_verify_port, -}; - -/* Initialize SB1250 DUART port structures. */ -static void __init sbd_probe_duarts(void) -{ - static int probed; - int chip, side; - int max_lines, line; - - if (probed) - return; - - /* Set the number of available units based on the SOC type. */ - switch (soc_type) { - case K_SYS_SOC_TYPE_BCM1x55: - case K_SYS_SOC_TYPE_BCM1x80: - max_lines = 4; - break; - default: - /* Assume at least two serial ports at the normal address. */ - max_lines = 2; - break; - } - - probed = 1; - - for (chip = 0, line = 0; chip < DUART_MAX_CHIP && line < max_lines; - chip++) { - sbd_duarts[chip].mapctrl = SBD_CTRLREGS(line); - - for (side = 0; side < DUART_MAX_SIDE && line < max_lines; - side++, line++) { - struct sbd_port *sport = &sbd_duarts[chip].sport[side]; - struct uart_port *uport = &sport->port; - - sport->duart = &sbd_duarts[chip]; - - uport->irq = SBD_INT(line); - uport->uartclk = 100000000 / 20 * 16; - uport->fifosize = 16; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &sbd_ops; - uport->line = line; - uport->mapbase = SBD_CHANREGS(line); - } - } -} - - -#ifdef CONFIG_SERIAL_SB1250_DUART_CONSOLE -/* - * Serial console stuff. Very basic, polling driver for doing serial - * console output. The console_sem is held by the caller, so we - * shouldn't be interrupted for more console activity. - */ -static void sbd_console_putchar(struct uart_port *uport, int ch) -{ - struct sbd_port *sport = to_sport(uport); - - sbd_transmit_drain(sport); - write_sbdchn(sport, R_DUART_TX_HOLD, ch); -} - -static void sbd_console_write(struct console *co, const char *s, - unsigned int count) -{ - int chip = co->index / DUART_MAX_SIDE; - int side = co->index % DUART_MAX_SIDE; - struct sbd_port *sport = &sbd_duarts[chip].sport[side]; - struct uart_port *uport = &sport->port; - unsigned long flags; - unsigned int mask; - - /* Disable transmit interrupts and enable the transmitter. */ - spin_lock_irqsave(&uport->lock, flags); - mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), - mask & ~M_DUART_IMR_TX); - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN); - spin_unlock_irqrestore(&uport->lock, flags); - - uart_console_write(&sport->port, s, count, sbd_console_putchar); - - /* Restore transmit interrupts and the transmitter enable. */ - spin_lock_irqsave(&uport->lock, flags); - sbd_line_drain(sport); - if (sport->tx_stopped) - write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS); - write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); - spin_unlock_irqrestore(&uport->lock, flags); -} - -static int __init sbd_console_setup(struct console *co, char *options) -{ - int chip = co->index / DUART_MAX_SIDE; - int side = co->index % DUART_MAX_SIDE; - struct sbd_port *sport = &sbd_duarts[chip].sport[side]; - struct uart_port *uport = &sport->port; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - if (!sport->duart) - return -ENXIO; - - ret = sbd_map_port(uport); - if (ret) - return ret; - - sbd_init_port(sport); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(uport, co, baud, parity, bits, flow); -} - -static struct uart_driver sbd_reg; -static struct console sbd_console = { - .name = "duart", - .write = sbd_console_write, - .device = uart_console_device, - .setup = sbd_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sbd_reg -}; - -static int __init sbd_serial_console_init(void) -{ - sbd_probe_duarts(); - register_console(&sbd_console); - - return 0; -} - -console_initcall(sbd_serial_console_init); - -#define SERIAL_SB1250_DUART_CONSOLE &sbd_console -#else -#define SERIAL_SB1250_DUART_CONSOLE NULL -#endif /* CONFIG_SERIAL_SB1250_DUART_CONSOLE */ - - -static struct uart_driver sbd_reg = { - .owner = THIS_MODULE, - .driver_name = "sb1250_duart", - .dev_name = "duart", - .major = TTY_MAJOR, - .minor = SB1250_DUART_MINOR_BASE, - .nr = DUART_MAX_CHIP * DUART_MAX_SIDE, - .cons = SERIAL_SB1250_DUART_CONSOLE, -}; - -/* Set up the driver and register it. */ -static int __init sbd_init(void) -{ - int i, ret; - - sbd_probe_duarts(); - - ret = uart_register_driver(&sbd_reg); - if (ret) - return ret; - - for (i = 0; i < DUART_MAX_CHIP * DUART_MAX_SIDE; i++) { - struct sbd_duart *duart = &sbd_duarts[i / DUART_MAX_SIDE]; - struct sbd_port *sport = &duart->sport[i % DUART_MAX_SIDE]; - struct uart_port *uport = &sport->port; - - if (sport->duart) - uart_add_one_port(&sbd_reg, uport); - } - - return 0; -} - -/* Unload the driver. Unregister stuff, get ready to go away. */ -static void __exit sbd_exit(void) -{ - int i; - - for (i = DUART_MAX_CHIP * DUART_MAX_SIDE - 1; i >= 0; i--) { - struct sbd_duart *duart = &sbd_duarts[i / DUART_MAX_SIDE]; - struct sbd_port *sport = &duart->sport[i % DUART_MAX_SIDE]; - struct uart_port *uport = &sport->port; - - if (sport->duart) - uart_remove_one_port(&sbd_reg, uport); - } - - uart_unregister_driver(&sbd_reg); -} - -module_init(sbd_init); -module_exit(sbd_exit); diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c deleted file mode 100644 index 75038ad2b242..000000000000 --- a/drivers/serial/sc26xx.c +++ /dev/null @@ -1,757 +0,0 @@ -/* - * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices. - * - * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#define SC26XX_MAJOR 204 -#define SC26XX_MINOR_START 205 -#define SC26XX_NR 2 - -struct uart_sc26xx_port { - struct uart_port port[2]; - u8 dsr_mask[2]; - u8 cts_mask[2]; - u8 dcd_mask[2]; - u8 ri_mask[2]; - u8 dtr_mask[2]; - u8 rts_mask[2]; - u8 imr; -}; - -/* register common to both ports */ -#define RD_ISR 0x14 -#define RD_IPR 0x34 - -#define WR_ACR 0x10 -#define WR_IMR 0x14 -#define WR_OPCR 0x34 -#define WR_OPR_SET 0x38 -#define WR_OPR_CLR 0x3C - -/* access common register */ -#define READ_SC(p, r) readb((p)->membase + RD_##r) -#define WRITE_SC(p, r, v) writeb((v), (p)->membase + WR_##r) - -/* register per port */ -#define RD_PORT_MRx 0x00 -#define RD_PORT_SR 0x04 -#define RD_PORT_RHR 0x0c - -#define WR_PORT_MRx 0x00 -#define WR_PORT_CSR 0x04 -#define WR_PORT_CR 0x08 -#define WR_PORT_THR 0x0c - -/* SR bits */ -#define SR_BREAK (1 << 7) -#define SR_FRAME (1 << 6) -#define SR_PARITY (1 << 5) -#define SR_OVERRUN (1 << 4) -#define SR_TXRDY (1 << 2) -#define SR_RXRDY (1 << 0) - -#define CR_RES_MR (1 << 4) -#define CR_RES_RX (2 << 4) -#define CR_RES_TX (3 << 4) -#define CR_STRT_BRK (6 << 4) -#define CR_STOP_BRK (7 << 4) -#define CR_DIS_TX (1 << 3) -#define CR_ENA_TX (1 << 2) -#define CR_DIS_RX (1 << 1) -#define CR_ENA_RX (1 << 0) - -/* ISR bits */ -#define ISR_RXRDYB (1 << 5) -#define ISR_TXRDYB (1 << 4) -#define ISR_RXRDYA (1 << 1) -#define ISR_TXRDYA (1 << 0) - -/* IMR bits */ -#define IMR_RXRDY (1 << 1) -#define IMR_TXRDY (1 << 0) - -/* access port register */ -static inline u8 read_sc_port(struct uart_port *p, u8 reg) -{ - return readb(p->membase + p->line * 0x20 + reg); -} - -static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val) -{ - writeb(val, p->membase + p->line * 0x20 + reg); -} - -#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r) -#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v) - -static void sc26xx_enable_irq(struct uart_port *port, int mask) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - up->imr |= mask << (line * 4); - WRITE_SC(port, IMR, up->imr); -} - -static void sc26xx_disable_irq(struct uart_port *port, int mask) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - up->imr &= ~(mask << (line * 4)); - WRITE_SC(port, IMR, up->imr); -} - -static struct tty_struct *receive_chars(struct uart_port *port) -{ - struct tty_struct *tty = NULL; - int limit = 10000; - unsigned char ch; - char flag; - u8 status; - - if (port->state != NULL) /* Unopened serial console */ - tty = port->state->port.tty; - - while (limit-- > 0) { - status = READ_SC_PORT(port, SR); - if (!(status & SR_RXRDY)) - break; - ch = READ_SC_PORT(port, RHR); - - flag = TTY_NORMAL; - port->icount.rx++; - - if (unlikely(status & (SR_BREAK | SR_FRAME | - SR_PARITY | SR_OVERRUN))) { - if (status & SR_BREAK) { - status &= ~(SR_PARITY | SR_FRAME); - port->icount.brk++; - if (uart_handle_break(port)) - continue; - } else if (status & SR_PARITY) - port->icount.parity++; - else if (status & SR_FRAME) - port->icount.frame++; - if (status & SR_OVERRUN) - port->icount.overrun++; - - status &= port->read_status_mask; - if (status & SR_BREAK) - flag = TTY_BREAK; - else if (status & SR_PARITY) - flag = TTY_PARITY; - else if (status & SR_FRAME) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - continue; - - if (status & port->ignore_status_mask) - continue; - - tty_insert_flip_char(tty, ch, flag); - } - return tty; -} - -static void transmit_chars(struct uart_port *port) -{ - struct circ_buf *xmit; - - if (!port->state) - return; - - xmit = &port->state->xmit; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - sc26xx_disable_irq(port, IMR_TXRDY); - return; - } - while (!uart_circ_empty(xmit)) { - if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) - break; - - WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static irqreturn_t sc26xx_interrupt(int irq, void *dev_id) -{ - struct uart_sc26xx_port *up = dev_id; - struct tty_struct *tty; - unsigned long flags; - u8 isr; - - spin_lock_irqsave(&up->port[0].lock, flags); - - tty = NULL; - isr = READ_SC(&up->port[0], ISR); - if (isr & ISR_TXRDYA) - transmit_chars(&up->port[0]); - if (isr & ISR_RXRDYA) - tty = receive_chars(&up->port[0]); - - spin_unlock(&up->port[0].lock); - - if (tty) - tty_flip_buffer_push(tty); - - spin_lock(&up->port[1].lock); - - tty = NULL; - if (isr & ISR_TXRDYB) - transmit_chars(&up->port[1]); - if (isr & ISR_RXRDYB) - tty = receive_chars(&up->port[1]); - - spin_unlock_irqrestore(&up->port[1].lock, flags); - - if (tty) - tty_flip_buffer_push(tty); - - return IRQ_HANDLED; -} - -/* port->lock is not held. */ -static unsigned int sc26xx_tx_empty(struct uart_port *port) -{ - return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0; -} - -/* port->lock held by caller. */ -static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_sc26xx_port *up; - int line = port->line; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - - if (up->dtr_mask[line]) { - if (mctrl & TIOCM_DTR) - WRITE_SC(port, OPR_SET, up->dtr_mask[line]); - else - WRITE_SC(port, OPR_CLR, up->dtr_mask[line]); - } - if (up->rts_mask[line]) { - if (mctrl & TIOCM_RTS) - WRITE_SC(port, OPR_SET, up->rts_mask[line]); - else - WRITE_SC(port, OPR_CLR, up->rts_mask[line]); - } -} - -/* port->lock is held by caller and interrupts are disabled. */ -static unsigned int sc26xx_get_mctrl(struct uart_port *port) -{ - struct uart_sc26xx_port *up; - int line = port->line; - unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; - u8 ipr; - - port -= line; - up = container_of(port, struct uart_sc26xx_port, port[0]); - ipr = READ_SC(port, IPR) ^ 0xff; - - if (up->dsr_mask[line]) { - mctrl &= ~TIOCM_DSR; - mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0; - } - if (up->cts_mask[line]) { - mctrl &= ~TIOCM_CTS; - mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0; - } - if (up->dcd_mask[line]) { - mctrl &= ~TIOCM_CAR; - mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0; - } - if (up->ri_mask[line]) { - mctrl &= ~TIOCM_RNG; - mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0; - } - return mctrl; -} - -/* port->lock held by caller. */ -static void sc26xx_stop_tx(struct uart_port *port) -{ - return; -} - -/* port->lock held by caller. */ -static void sc26xx_start_tx(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - while (!uart_circ_empty(xmit)) { - if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) { - sc26xx_enable_irq(port, IMR_TXRDY); - break; - } - WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } -} - -/* port->lock held by caller. */ -static void sc26xx_stop_rx(struct uart_port *port) -{ -} - -/* port->lock held by caller. */ -static void sc26xx_enable_ms(struct uart_port *port) -{ -} - -/* port->lock is not held. */ -static void sc26xx_break_ctl(struct uart_port *port, int break_state) -{ - if (break_state == -1) - WRITE_SC_PORT(port, CR, CR_STRT_BRK); - else - WRITE_SC_PORT(port, CR, CR_STOP_BRK); -} - -/* port->lock is not held. */ -static int sc26xx_startup(struct uart_port *port) -{ - sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); - WRITE_SC(port, OPCR, 0); - - /* reset tx and rx */ - WRITE_SC_PORT(port, CR, CR_RES_RX); - WRITE_SC_PORT(port, CR, CR_RES_TX); - - /* start rx/tx */ - WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); - - /* enable irqs */ - sc26xx_enable_irq(port, IMR_RXRDY); - return 0; -} - -/* port->lock is not held. */ -static void sc26xx_shutdown(struct uart_port *port) -{ - /* disable interrupst */ - sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); - - /* stop tx/rx */ - WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); -} - -/* port->lock is not held. */ -static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - unsigned int quot = uart_get_divisor(port, baud); - unsigned int iflag, cflag; - unsigned long flags; - u8 mr1, mr2, csr; - - spin_lock_irqsave(&port->lock, flags); - - while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) - udelay(2); - - WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); - - iflag = termios->c_iflag; - cflag = termios->c_cflag; - - port->read_status_mask = SR_OVERRUN; - if (iflag & INPCK) - port->read_status_mask |= SR_PARITY | SR_FRAME; - if (iflag & (BRKINT | PARMRK)) - port->read_status_mask |= SR_BREAK; - - port->ignore_status_mask = 0; - if (iflag & IGNBRK) - port->ignore_status_mask |= SR_BREAK; - if ((cflag & CREAD) == 0) - port->ignore_status_mask |= SR_BREAK | SR_FRAME | - SR_PARITY | SR_OVERRUN; - - switch (cflag & CSIZE) { - case CS5: - mr1 = 0x00; - break; - case CS6: - mr1 = 0x01; - break; - case CS7: - mr1 = 0x02; - break; - default: - case CS8: - mr1 = 0x03; - break; - } - mr2 = 0x07; - if (cflag & CSTOPB) - mr2 = 0x0f; - if (cflag & PARENB) { - if (cflag & PARODD) - mr1 |= (1 << 2); - } else - mr1 |= (2 << 3); - - switch (baud) { - case 50: - csr = 0x00; - break; - case 110: - csr = 0x11; - break; - case 134: - csr = 0x22; - break; - case 200: - csr = 0x33; - break; - case 300: - csr = 0x44; - break; - case 600: - csr = 0x55; - break; - case 1200: - csr = 0x66; - break; - case 2400: - csr = 0x88; - break; - case 4800: - csr = 0x99; - break; - default: - case 9600: - csr = 0xbb; - break; - case 19200: - csr = 0xcc; - break; - } - - WRITE_SC_PORT(port, CR, CR_RES_MR); - WRITE_SC_PORT(port, MRx, mr1); - WRITE_SC_PORT(port, MRx, mr2); - - WRITE_SC(port, ACR, 0x80); - WRITE_SC_PORT(port, CSR, csr); - - /* reset tx and rx */ - WRITE_SC_PORT(port, CR, CR_RES_RX); - WRITE_SC_PORT(port, CR, CR_RES_TX); - - WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); - while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) - udelay(2); - - /* XXX */ - uart_update_timeout(port, cflag, - (port->uartclk / (16 * quot))); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *sc26xx_type(struct uart_port *port) -{ - return "SC26XX"; -} - -static void sc26xx_release_port(struct uart_port *port) -{ -} - -static int sc26xx_request_port(struct uart_port *port) -{ - return 0; -} - -static void sc26xx_config_port(struct uart_port *port, int flags) -{ -} - -static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static struct uart_ops sc26xx_ops = { - .tx_empty = sc26xx_tx_empty, - .set_mctrl = sc26xx_set_mctrl, - .get_mctrl = sc26xx_get_mctrl, - .stop_tx = sc26xx_stop_tx, - .start_tx = sc26xx_start_tx, - .stop_rx = sc26xx_stop_rx, - .enable_ms = sc26xx_enable_ms, - .break_ctl = sc26xx_break_ctl, - .startup = sc26xx_startup, - .shutdown = sc26xx_shutdown, - .set_termios = sc26xx_set_termios, - .type = sc26xx_type, - .release_port = sc26xx_release_port, - .request_port = sc26xx_request_port, - .config_port = sc26xx_config_port, - .verify_port = sc26xx_verify_port, -}; - -static struct uart_port *sc26xx_port; - -#ifdef CONFIG_SERIAL_SC26XX_CONSOLE -static void sc26xx_console_putchar(struct uart_port *port, char c) -{ - unsigned long flags; - int limit = 1000000; - - spin_lock_irqsave(&port->lock, flags); - - while (limit-- > 0) { - if (READ_SC_PORT(port, SR) & SR_TXRDY) { - WRITE_SC_PORT(port, THR, c); - break; - } - udelay(2); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void sc26xx_console_write(struct console *con, const char *s, unsigned n) -{ - struct uart_port *port = sc26xx_port; - int i; - - for (i = 0; i < n; i++) { - if (*s == '\n') - sc26xx_console_putchar(port, '\r'); - sc26xx_console_putchar(port, *s++); - } -} - -static int __init sc26xx_console_setup(struct console *con, char *options) -{ - struct uart_port *port = sc26xx_port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (port->type != PORT_SC26XX) - return -1; - - printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, con, baud, parity, bits, flow); -} - -static struct uart_driver sc26xx_reg; -static struct console sc26xx_console = { - .name = "ttySC", - .write = sc26xx_console_write, - .device = uart_console_device, - .setup = sc26xx_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sc26xx_reg, -}; -#define SC26XX_CONSOLE &sc26xx_console -#else -#define SC26XX_CONSOLE NULL -#endif - -static struct uart_driver sc26xx_reg = { - .owner = THIS_MODULE, - .driver_name = "SC26xx", - .dev_name = "ttySC", - .major = SC26XX_MAJOR, - .minor = SC26XX_MINOR_START, - .nr = SC26XX_NR, - .cons = SC26XX_CONSOLE, -}; - -static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos) -{ - unsigned int bit = (flags >> bitpos) & 15; - - return bit ? (1 << (bit - 1)) : 0; -} - -static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up, - int line, unsigned int data) -{ - up->dtr_mask[line] = sc26xx_flags2mask(data, 0); - up->rts_mask[line] = sc26xx_flags2mask(data, 4); - up->dsr_mask[line] = sc26xx_flags2mask(data, 8); - up->cts_mask[line] = sc26xx_flags2mask(data, 12); - up->dcd_mask[line] = sc26xx_flags2mask(data, 16); - up->ri_mask[line] = sc26xx_flags2mask(data, 20); -} - -static int __devinit sc26xx_probe(struct platform_device *dev) -{ - struct resource *res; - struct uart_sc26xx_port *up; - unsigned int *sc26xx_data = dev->dev.platform_data; - int err; - - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - up = kzalloc(sizeof *up, GFP_KERNEL); - if (unlikely(!up)) - return -ENOMEM; - - up->port[0].line = 0; - up->port[0].ops = &sc26xx_ops; - up->port[0].type = PORT_SC26XX; - up->port[0].uartclk = (29491200 / 16); /* arbitrary */ - - up->port[0].mapbase = res->start; - up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40); - up->port[0].iotype = UPIO_MEM; - up->port[0].irq = platform_get_irq(dev, 0); - - up->port[0].dev = &dev->dev; - - sc26xx_init_masks(up, 0, sc26xx_data[0]); - - sc26xx_port = &up->port[0]; - - up->port[1].line = 1; - up->port[1].ops = &sc26xx_ops; - up->port[1].type = PORT_SC26XX; - up->port[1].uartclk = (29491200 / 16); /* arbitrary */ - - up->port[1].mapbase = up->port[0].mapbase; - up->port[1].membase = up->port[0].membase; - up->port[1].iotype = UPIO_MEM; - up->port[1].irq = up->port[0].irq; - - up->port[1].dev = &dev->dev; - - sc26xx_init_masks(up, 1, sc26xx_data[1]); - - err = uart_register_driver(&sc26xx_reg); - if (err) - goto out_free_port; - - sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor; - - err = uart_add_one_port(&sc26xx_reg, &up->port[0]); - if (err) - goto out_unregister_driver; - - err = uart_add_one_port(&sc26xx_reg, &up->port[1]); - if (err) - goto out_remove_port0; - - err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up); - if (err) - goto out_remove_ports; - - dev_set_drvdata(&dev->dev, up); - return 0; - -out_remove_ports: - uart_remove_one_port(&sc26xx_reg, &up->port[1]); -out_remove_port0: - uart_remove_one_port(&sc26xx_reg, &up->port[0]); - -out_unregister_driver: - uart_unregister_driver(&sc26xx_reg); - -out_free_port: - kfree(up); - sc26xx_port = NULL; - return err; -} - - -static int __exit sc26xx_driver_remove(struct platform_device *dev) -{ - struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); - - free_irq(up->port[0].irq, up); - - uart_remove_one_port(&sc26xx_reg, &up->port[0]); - uart_remove_one_port(&sc26xx_reg, &up->port[1]); - - uart_unregister_driver(&sc26xx_reg); - - kfree(up); - sc26xx_port = NULL; - - dev_set_drvdata(&dev->dev, NULL); - return 0; -} - -static struct platform_driver sc26xx_driver = { - .probe = sc26xx_probe, - .remove = __devexit_p(sc26xx_driver_remove), - .driver = { - .name = "SC26xx", - .owner = THIS_MODULE, - }, -}; - -static int __init sc26xx_init(void) -{ - return platform_driver_register(&sc26xx_driver); -} - -static void __exit sc26xx_exit(void) -{ - platform_driver_unregister(&sc26xx_driver); -} - -module_init(sc26xx_init); -module_exit(sc26xx_exit); - - -MODULE_AUTHOR("Thomas Bogendörfer"); -MODULE_DESCRIPTION("SC681/SC2692 serial driver"); -MODULE_VERSION("1.0"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:SC26xx"); diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c deleted file mode 100644 index 460a72d91bb7..000000000000 --- a/drivers/serial/serial_core.c +++ /dev/null @@ -1,2578 +0,0 @@ -/* - * linux/drivers/char/core.c - * - * Driver core for serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * - * Copyright 1999 ARM Limited - * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. - * - * 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 /* for serial_state and serial_icounter_struct */ -#include -#include -#include - -#include -#include - -/* - * This is used to lock changes in serial line configuration. - */ -static DEFINE_MUTEX(port_mutex); - -/* - * lockdep: port->lock is initialized in two places, but we - * want only one lock-class: - */ -static struct lock_class_key port_lock_key; - -#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) - -#ifdef CONFIG_SERIAL_CORE_CONSOLE -#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) -#else -#define uart_console(port) (0) -#endif - -static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, - struct ktermios *old_termios); -static void __uart_wait_until_sent(struct uart_port *port, int timeout); -static void uart_change_pm(struct uart_state *state, int pm_state); - -/* - * This routine is used by the interrupt handler to schedule processing in - * the software interrupt portion of the driver. - */ -void uart_write_wakeup(struct uart_port *port) -{ - struct uart_state *state = port->state; - /* - * This means you called this function _after_ the port was - * closed. No cookie for you. - */ - BUG_ON(!state); - tasklet_schedule(&state->tlet); -} - -static void uart_stop(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - port->ops->stop_tx(port); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void __uart_start(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; - - if (!uart_circ_empty(&state->xmit) && state->xmit.buf && - !tty->stopped && !tty->hw_stopped) - port->ops->start_tx(port); -} - -static void uart_start(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - __uart_start(tty); - spin_unlock_irqrestore(&port->lock, flags); -} - -static void uart_tasklet_action(unsigned long data) -{ - struct uart_state *state = (struct uart_state *)data; - tty_wakeup(state->port.tty); -} - -static inline void -uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) -{ - unsigned long flags; - unsigned int old; - - spin_lock_irqsave(&port->lock, flags); - old = port->mctrl; - port->mctrl = (old & ~clear) | set; - if (old != port->mctrl) - port->ops->set_mctrl(port, port->mctrl); - spin_unlock_irqrestore(&port->lock, flags); -} - -#define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) -#define uart_clear_mctrl(port, clear) uart_update_mctrl(port, 0, clear) - -/* - * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port mutex. - */ -static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) -{ - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - unsigned long page; - int retval = 0; - - if (port->flags & ASYNC_INITIALIZED) - return 0; - - /* - * Set the TTY IO error marker - we will only clear this - * once we have successfully opened the port. Also set - * up the tty->alt_speed kludge - */ - set_bit(TTY_IO_ERROR, &tty->flags); - - if (uport->type == PORT_UNKNOWN) - return 0; - - /* - * Initialise and allocate the transmit and temporary - * buffer. - */ - if (!state->xmit.buf) { - /* This is protected by the per port mutex */ - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - state->xmit.buf = (unsigned char *) page; - uart_circ_clear(&state->xmit); - } - - retval = uport->ops->startup(uport); - if (retval == 0) { - if (init_hw) { - /* - * Initialise the hardware port settings. - */ - uart_change_speed(tty, state, NULL); - - /* - * Setup the RTS and DTR signals once the - * port is open and ready to respond. - */ - if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); - } - - if (port->flags & ASYNC_CTS_FLOW) { - spin_lock_irq(&uport->lock); - if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) - tty->hw_stopped = 1; - spin_unlock_irq(&uport->lock); - } - - set_bit(ASYNCB_INITIALIZED, &port->flags); - - clear_bit(TTY_IO_ERROR, &tty->flags); - } - - if (retval && capable(CAP_SYS_ADMIN)) - retval = 0; - - return retval; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. Calls to - * uart_shutdown are serialised by the per-port semaphore. - */ -static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) -{ - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - - /* - * Set the TTY IO error marker - */ - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - - if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { - /* - * Turn off DTR and RTS early. - */ - if (!tty || (tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free - * the irq here so the queue might never be woken up. Note - * that we won't end up waiting on delta_msr_wait again since - * any outstanding file descriptors should be pointing at - * hung_up_tty_fops now. - */ - wake_up_interruptible(&port->delta_msr_wait); - - /* - * Free the IRQ and disable the port. - */ - uport->ops->shutdown(uport); - - /* - * Ensure that the IRQ handler isn't running on another CPU. - */ - synchronize_irq(uport->irq); - } - - /* - * kill off our tasklet - */ - tasklet_kill(&state->tlet); - - /* - * Free the transmit buffer page. - */ - if (state->xmit.buf) { - free_page((unsigned long)state->xmit.buf); - state->xmit.buf = NULL; - } -} - -/** - * uart_update_timeout - update per-port FIFO timeout. - * @port: uart_port structure describing the port - * @cflag: termios cflag value - * @baud: speed of the port - * - * Set the port FIFO timeout value. The @cflag value should - * reflect the actual hardware settings. - */ -void -uart_update_timeout(struct uart_port *port, unsigned int cflag, - unsigned int baud) -{ - unsigned int bits; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - bits = 7; - break; - case CS6: - bits = 8; - break; - case CS7: - bits = 9; - break; - default: - bits = 10; - break; /* CS8 */ - } - - if (cflag & CSTOPB) - bits++; - if (cflag & PARENB) - bits++; - - /* - * The total number of bits to be transmitted in the fifo. - */ - bits = bits * port->fifosize; - - /* - * Figure the timeout to send the above number of bits. - * Add .02 seconds of slop - */ - port->timeout = (HZ * bits) / baud + HZ/50; -} - -EXPORT_SYMBOL(uart_update_timeout); - -/** - * uart_get_baud_rate - return baud rate for a particular port - * @port: uart_port structure describing the port in question. - * @termios: desired termios settings. - * @old: old termios (or NULL) - * @min: minimum acceptable baud rate - * @max: maximum acceptable baud rate - * - * Decode the termios structure into a numeric baud rate, - * taking account of the magic 38400 baud rate (with spd_* - * flags), and mapping the %B0 rate to 9600 baud. - * - * If the new baud rate is invalid, try the old termios setting. - * If it's still invalid, we try 9600 baud. - * - * Update the @termios structure to reflect the baud rate - * we're actually going to be using. Don't do this for the case - * where B0 is requested ("hang up"). - */ -unsigned int -uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, - struct ktermios *old, unsigned int min, unsigned int max) -{ - unsigned int try, baud, altbaud = 38400; - int hung_up = 0; - upf_t flags = port->flags & UPF_SPD_MASK; - - if (flags == UPF_SPD_HI) - altbaud = 57600; - else if (flags == UPF_SPD_VHI) - altbaud = 115200; - else if (flags == UPF_SPD_SHI) - altbaud = 230400; - else if (flags == UPF_SPD_WARP) - altbaud = 460800; - - for (try = 0; try < 2; try++) { - baud = tty_termios_baud_rate(termios); - - /* - * The spd_hi, spd_vhi, spd_shi, spd_warp kludge... - * Die! Die! Die! - */ - if (baud == 38400) - baud = altbaud; - - /* - * Special case: B0 rate. - */ - if (baud == 0) { - hung_up = 1; - baud = 9600; - } - - if (baud >= min && baud <= max) - return baud; - - /* - * Oops, the quotient was zero. Try again with - * the old baud rate if possible. - */ - termios->c_cflag &= ~CBAUD; - if (old) { - baud = tty_termios_baud_rate(old); - if (!hung_up) - tty_termios_encode_baud_rate(termios, - baud, baud); - old = NULL; - continue; - } - - /* - * As a last resort, if the range cannot be met then clip to - * the nearest chip supported rate. - */ - if (!hung_up) { - if (baud <= min) - tty_termios_encode_baud_rate(termios, - min + 1, min + 1); - else - tty_termios_encode_baud_rate(termios, - max - 1, max - 1); - } - } - /* Should never happen */ - WARN_ON(1); - return 0; -} - -EXPORT_SYMBOL(uart_get_baud_rate); - -/** - * uart_get_divisor - return uart clock divisor - * @port: uart_port structure describing the port. - * @baud: desired baud rate - * - * Calculate the uart clock divisor for the port. - */ -unsigned int -uart_get_divisor(struct uart_port *port, unsigned int baud) -{ - unsigned int quot; - - /* - * Old custom speed handling. - */ - 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; -} - -EXPORT_SYMBOL(uart_get_divisor); - -/* FIXME: Consistent locking policy */ -static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, - struct ktermios *old_termios) -{ - struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; - struct ktermios *termios; - - /* - * If we have no tty, termios, or the port does not exist, - * then we can't set the parameters for this port. - */ - if (!tty || !tty->termios || uport->type == PORT_UNKNOWN) - return; - - termios = tty->termios; - - /* - * Set flags based on termios cflag - */ - if (termios->c_cflag & CRTSCTS) - set_bit(ASYNCB_CTS_FLOW, &port->flags); - else - clear_bit(ASYNCB_CTS_FLOW, &port->flags); - - if (termios->c_cflag & CLOCAL) - clear_bit(ASYNCB_CHECK_CD, &port->flags); - else - set_bit(ASYNCB_CHECK_CD, &port->flags); - - uport->ops->set_termios(uport, termios, old_termios); -} - -static inline int __uart_put_char(struct 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 uart_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct uart_state *state = tty->driver_data; - - return __uart_put_char(state->uart_port, &state->xmit, ch); -} - -static void uart_flush_chars(struct tty_struct *tty) -{ - uart_start(tty); -} - -static int uart_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port; - struct circ_buf *circ; - unsigned long flags; - int c, ret = 0; - - /* - * This means you called this function _after_ the port was - * closed. No cookie for you. - */ - if (!state) { - WARN_ON(1); - return -EL3HLT; - } - - port = state->uart_port; - circ = &state->xmit; - - if (!circ->buf) - return 0; - - spin_lock_irqsave(&port->lock, flags); - 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; - } - spin_unlock_irqrestore(&port->lock, flags); - - uart_start(tty); - return ret; -} - -static int uart_write_room(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - unsigned long flags; - int ret; - - spin_lock_irqsave(&state->uart_port->lock, flags); - ret = uart_circ_chars_free(&state->xmit); - spin_unlock_irqrestore(&state->uart_port->lock, flags); - return ret; -} - -static int uart_chars_in_buffer(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - unsigned long flags; - int ret; - - spin_lock_irqsave(&state->uart_port->lock, flags); - ret = uart_circ_chars_pending(&state->xmit); - spin_unlock_irqrestore(&state->uart_port->lock, flags); - return ret; -} - -static void uart_flush_buffer(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port; - unsigned long flags; - - /* - * This means you called this function _after_ the port was - * closed. No cookie for you. - */ - if (!state) { - WARN_ON(1); - return; - } - - port = state->uart_port; - pr_debug("uart_flush_buffer(%d) called\n", tty->index); - - spin_lock_irqsave(&port->lock, flags); - uart_circ_clear(&state->xmit); - if (port->ops->flush_buffer) - port->ops->flush_buffer(port); - spin_unlock_irqrestore(&port->lock, flags); - tty_wakeup(tty); -} - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void uart_send_xchar(struct tty_struct *tty, char ch) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_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 uart_throttle(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - - if (I_IXOFF(tty)) - uart_send_xchar(tty, STOP_CHAR(tty)); - - if (tty->termios->c_cflag & CRTSCTS) - uart_clear_mctrl(state->uart_port, TIOCM_RTS); -} - -static void uart_unthrottle(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; - - if (I_IXOFF(tty)) { - if (port->x_char) - port->x_char = 0; - else - uart_send_xchar(tty, START_CHAR(tty)); - } - - if (tty->termios->c_cflag & CRTSCTS) - uart_set_mctrl(port, TIOCM_RTS); -} - -static int uart_get_info(struct uart_state *state, - struct serial_struct __user *retinfo) -{ - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - struct serial_struct tmp; - - memset(&tmp, 0, sizeof(tmp)); - - /* Ensure the state we copy is consistent and no hardware changes - occur as we go */ - mutex_lock(&port->mutex); - - tmp.type = uport->type; - tmp.line = uport->line; - tmp.port = uport->iobase; - if (HIGH_BITS_OFFSET) - tmp.port_high = (long) uport->iobase >> HIGH_BITS_OFFSET; - tmp.irq = uport->irq; - tmp.flags = uport->flags; - tmp.xmit_fifo_size = uport->fifosize; - tmp.baud_base = uport->uartclk / 16; - tmp.close_delay = port->close_delay / 10; - tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : - port->closing_wait / 10; - tmp.custom_divisor = uport->custom_divisor; - tmp.hub6 = uport->hub6; - tmp.io_type = uport->iotype; - tmp.iomem_reg_shift = uport->regshift; - tmp.iomem_base = (void *)(unsigned long)uport->mapbase; - - mutex_unlock(&port->mutex); - - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int uart_set_info(struct tty_struct *tty, struct uart_state *state, - struct serial_struct __user *newinfo) -{ - struct serial_struct new_serial; - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - unsigned long new_port; - unsigned int change_irq, change_port, closing_wait; - unsigned int old_custom_divisor, close_delay; - upf_t 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); - close_delay = new_serial.close_delay * 10; - closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; - - /* - * This semaphore protects port->count. It is also - * very useful to prevent opens. Also, take the - * port configuration semaphore to make sure that a - * module insertion/removal doesn't change anything - * under us. - */ - mutex_lock(&port->mutex); - - change_irq = !(uport->flags & UPF_FIXED_PORT) - && new_serial.irq != uport->irq; - - /* - * Since changing the 'type' of the port changes its resource - * allocations, we should treat type changes the same as - * IO port changes. - */ - change_port = !(uport->flags & UPF_FIXED_PORT) - && (new_port != uport->iobase || - (unsigned long)new_serial.iomem_base != uport->mapbase || - new_serial.hub6 != uport->hub6 || - new_serial.io_type != uport->iotype || - new_serial.iomem_reg_shift != uport->regshift || - new_serial.type != uport->type); - - old_flags = uport->flags; - new_flags = new_serial.flags; - old_custom_divisor = uport->custom_divisor; - - if (!capable(CAP_SYS_ADMIN)) { - retval = -EPERM; - if (change_irq || change_port || - (new_serial.baud_base != uport->uartclk / 16) || - (close_delay != port->close_delay) || - (closing_wait != port->closing_wait) || - (new_serial.xmit_fifo_size && - new_serial.xmit_fifo_size != uport->fifosize) || - (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) - goto exit; - uport->flags = ((uport->flags & ~UPF_USR_MASK) | - (new_flags & UPF_USR_MASK)); - uport->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - /* - * Ask the low level driver to verify the settings. - */ - if (uport->ops->verify_port) - retval = uport->ops->verify_port(uport, &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; - - /* - * Make sure that we are the sole user of this port. - */ - if (tty_port_users(port) > 1) - goto exit; - - /* - * We need to shutdown the serial port at the old - * port/type/irq combination. - */ - uart_shutdown(tty, state); - } - - if (change_port) { - unsigned long old_iobase, old_mapbase; - unsigned int old_type, old_iotype, old_hub6, old_shift; - - old_iobase = uport->iobase; - old_mapbase = uport->mapbase; - old_type = uport->type; - old_hub6 = uport->hub6; - old_iotype = uport->iotype; - old_shift = uport->regshift; - - /* - * Free and release old regions - */ - if (old_type != PORT_UNKNOWN) - uport->ops->release_port(uport); - - uport->iobase = new_port; - uport->type = new_serial.type; - uport->hub6 = new_serial.hub6; - uport->iotype = new_serial.io_type; - uport->regshift = new_serial.iomem_reg_shift; - uport->mapbase = (unsigned long)new_serial.iomem_base; - - /* - * Claim and map the new regions - */ - if (uport->type != PORT_UNKNOWN) { - retval = uport->ops->request_port(uport); - } else { - /* Always success - Jean II */ - retval = 0; - } - - /* - * If we fail to request resources for the - * new port, try to restore the old settings. - */ - if (retval && old_type != PORT_UNKNOWN) { - uport->iobase = old_iobase; - uport->type = old_type; - uport->hub6 = old_hub6; - uport->iotype = old_iotype; - uport->regshift = old_shift; - uport->mapbase = old_mapbase; - retval = uport->ops->request_port(uport); - /* - * If we failed to restore the old settings, - * we fail like this. - */ - if (retval) - uport->type = PORT_UNKNOWN; - - /* - * We failed anyway. - */ - retval = -EBUSY; - /* Added to return the correct error -Ram Gupta */ - goto exit; - } - } - - if (change_irq) - uport->irq = new_serial.irq; - if (!(uport->flags & UPF_FIXED_PORT)) - uport->uartclk = new_serial.baud_base * 16; - uport->flags = (uport->flags & ~UPF_CHANGE_MASK) | - (new_flags & UPF_CHANGE_MASK); - uport->custom_divisor = new_serial.custom_divisor; - port->close_delay = close_delay; - port->closing_wait = closing_wait; - if (new_serial.xmit_fifo_size) - uport->fifosize = new_serial.xmit_fifo_size; - if (port->tty) - port->tty->low_latency = - (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; - - check_and_exit: - retval = 0; - if (uport->type == PORT_UNKNOWN) - goto exit; - if (port->flags & ASYNC_INITIALIZED) { - if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || - old_custom_divisor != uport->custom_divisor) { - /* - * If they're setting up a custom divisor or speed, - * instead of clearing it, then bitch about it. No - * need to rate-limit; it's CAP_SYS_ADMIN only. - */ - if (uport->flags & UPF_SPD_MASK) { - char buf[64]; - printk(KERN_NOTICE - "%s sets custom speed on %s. This " - "is deprecated.\n", current->comm, - tty_name(port->tty, buf)); - } - uart_change_speed(tty, state, NULL); - } - } else - retval = uart_startup(tty, state, 1); - exit: - mutex_unlock(&port->mutex); - return retval; -} - -/** - * uart_get_lsr_info - get line status register info - * @tty: tty associated with the UART - * @state: UART being queried - * @value: returned modem value - * - * Note: uart_ioctl protects us against hangups. - */ -static int uart_get_lsr_info(struct tty_struct *tty, - struct uart_state *state, unsigned int __user *value) -{ - struct uart_port *uport = state->uart_port; - unsigned int result; - - result = uport->ops->tx_empty(uport); - - /* - * If we're about to load something into the transmit - * register, we'll pretend the transmitter isn't empty to - * avoid a race condition (depending on when the transmit - * interrupt happens). - */ - if (uport->x_char || - ((uart_circ_chars_pending(&state->xmit) > 0) && - !tty->stopped && !tty->hw_stopped)) - result &= ~TIOCSER_TEMT; - - return put_user(result, value); -} - -static int uart_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct uart_state *state = tty->driver_data; - struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; - int result = -EIO; - - mutex_lock(&port->mutex); - if ((!file || !tty_hung_up_p(file)) && - !(tty->flags & (1 << TTY_IO_ERROR))) { - result = uport->mctrl; - - spin_lock_irq(&uport->lock); - result |= uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); - } - mutex_unlock(&port->mutex); - - return result; -} - -static int -uart_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - int ret = -EIO; - - mutex_lock(&port->mutex); - if ((!file || !tty_hung_up_p(file)) && - !(tty->flags & (1 << TTY_IO_ERROR))) { - uart_update_mctrl(uport, set, clear); - ret = 0; - } - mutex_unlock(&port->mutex); - return ret; -} - -static int uart_break_ctl(struct tty_struct *tty, int break_state) -{ - struct uart_state *state = tty->driver_data; - struct tty_port *port = &state->port; - struct uart_port *uport = state->uart_port; - - mutex_lock(&port->mutex); - - if (uport->type != PORT_UNKNOWN) - uport->ops->break_ctl(uport, break_state); - - mutex_unlock(&port->mutex); - return 0; -} - -static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) -{ - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - int flags, ret; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - /* - * Take the per-port semaphore. This prevents count from - * changing, and hence any extra opens of the port while - * we're auto-configuring. - */ - if (mutex_lock_interruptible(&port->mutex)) - return -ERESTARTSYS; - - ret = -EBUSY; - if (tty_port_users(port) == 1) { - uart_shutdown(tty, state); - - /* - * If we already have a port type configured, - * we must release its resources. - */ - if (uport->type != PORT_UNKNOWN) - uport->ops->release_port(uport); - - flags = UART_CONFIG_TYPE; - if (uport->flags & UPF_AUTO_IRQ) - flags |= UART_CONFIG_IRQ; - - /* - * This will claim the ports resources if - * a port is found. - */ - uport->ops->config_port(uport, flags); - - ret = uart_startup(tty, state, 1); - } - mutex_unlock(&port->mutex); - return ret; -} - -/* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - * - * FIXME: This wants extracting into a common all driver implementation - * of TIOCMWAIT using tty_port. - */ -static int -uart_wait_modem_status(struct uart_state *state, unsigned long arg) -{ - struct uart_port *uport = state->uart_port; - struct tty_port *port = &state->port; - DECLARE_WAITQUEUE(wait, current); - struct uart_icount cprev, cnow; - int ret; - - /* - * note the counters on entry - */ - spin_lock_irq(&uport->lock); - memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); - - /* - * Force modem status interrupts on - */ - uport->ops->enable_ms(uport); - spin_unlock_irq(&uport->lock); - - add_wait_queue(&port->delta_msr_wait, &wait); - for (;;) { - spin_lock_irq(&uport->lock); - memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&uport->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(); - - /* see if a signal did it */ - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - - cprev = cnow; - } - - current->state = TASK_RUNNING; - remove_wait_queue(&port->delta_msr_wait, &wait); - - return ret; -} - -/* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ -static int uart_get_icount(struct tty_struct *tty, - struct serial_icounter_struct *icount) -{ - struct uart_state *state = tty->driver_data; - struct uart_icount cnow; - struct uart_port *uport = state->uart_port; - - spin_lock_irq(&uport->lock); - memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&uport->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 0; -} - -/* - * Called via sys_ioctl. We can use spin_lock_irq() here. - */ -static int -uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - struct uart_state *state = tty->driver_data; - struct tty_port *port = &state->port; - void __user *uarg = (void __user *)arg; - int ret = -ENOIOCTLCMD; - - - /* - * These ioctls don't rely on the hardware to be present. - */ - switch (cmd) { - case TIOCGSERIAL: - ret = uart_get_info(state, uarg); - break; - - case TIOCSSERIAL: - ret = uart_set_info(tty, state, uarg); - break; - - case TIOCSERCONFIG: - ret = uart_do_autoconfig(tty, state); - break; - - case TIOCSERGWILD: /* obsolete */ - case TIOCSERSWILD: /* obsolete */ - ret = 0; - break; - } - - if (ret != -ENOIOCTLCMD) - goto out; - - if (tty->flags & (1 << TTY_IO_ERROR)) { - ret = -EIO; - goto out; - } - - /* - * The following should only be used when hardware is present. - */ - switch (cmd) { - case TIOCMIWAIT: - ret = uart_wait_modem_status(state, arg); - break; - } - - if (ret != -ENOIOCTLCMD) - goto out; - - mutex_lock(&port->mutex); - - if (tty_hung_up_p(filp)) { - ret = -EIO; - goto out_up; - } - - /* - * All these rely on hardware being present and need to be - * protected against the tty being hung up. - */ - switch (cmd) { - case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(tty, state, uarg); - break; - - default: { - struct uart_port *uport = state->uart_port; - if (uport->ops->ioctl) - ret = uport->ops->ioctl(uport, cmd, arg); - break; - } - } -out_up: - mutex_unlock(&port->mutex); -out: - return ret; -} - -static void uart_set_ldisc(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *uport = state->uart_port; - - if (uport->ops->set_ldisc) - uport->ops->set_ldisc(uport, tty->termios->c_line); -} - -static void uart_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct uart_state *state = tty->driver_data; - unsigned long flags; - unsigned int cflag = tty->termios->c_cflag; - - - /* - * These are the bits that are used to setup various - * flags in the low level driver. We can ignore the Bfoo - * bits in c_cflag; c_[io]speed will always be set - * appropriately by set_termios() in tty_ioctl.c - */ -#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - if ((cflag ^ old_termios->c_cflag) == 0 && - tty->termios->c_ospeed == old_termios->c_ospeed && - tty->termios->c_ispeed == old_termios->c_ispeed && - RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) { - return; - } - - uart_change_speed(tty, state, old_termios); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR); - /* Handle transition away from B0 status */ - else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { - unsigned int mask = TIOCM_DTR; - if (!(cflag & CRTSCTS) || - !test_bit(TTY_THROTTLED, &tty->flags)) - mask |= TIOCM_RTS; - uart_set_mctrl(state->uart_port, mask); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&state->uart_port->lock, flags); - tty->hw_stopped = 0; - __uart_start(tty); - spin_unlock_irqrestore(&state->uart_port->lock, flags); - } - /* Handle turning on CRTSCTS */ - else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { - spin_lock_irqsave(&state->uart_port->lock, flags); - if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) { - tty->hw_stopped = 1; - state->uart_port->ops->stop_tx(state->uart_port); - } - spin_unlock_irqrestore(&state->uart_port->lock, flags); - } -#if 0 - /* - * No need to wake up processes in open wait, since they - * sample the CLOCAL flag once, and don't recheck it. - * XXX It's not clear whether the current behavior is correct - * or not. Hence, this may change..... - */ - if (!(old_termios->c_cflag & CLOCAL) && - (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&state->uart_port.open_wait); -#endif -} - -/* - * In 2.4.5, calls to this will be serialized via the BKL in - * linux/drivers/char/tty_io.c:tty_release() - * linux/drivers/char/tty_io.c:do_tty_handup() - */ -static void uart_close(struct tty_struct *tty, struct file *filp) -{ - struct uart_state *state = tty->driver_data; - struct tty_port *port; - struct uart_port *uport; - unsigned long flags; - - BUG_ON(!tty_locked()); - - if (!state) - return; - - uport = state->uart_port; - port = &state->port; - - pr_debug("uart_close(%d) called\n", uport->line); - - mutex_lock(&port->mutex); - spin_lock_irqsave(&port->lock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&port->lock, flags); - goto done; - } - - if ((tty->count == 1) && (port->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. port->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, " - "port->count is %d\n", port->count); - port->count = 1; - } - if (--port->count < 0) { - printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n", - tty->name, port->count); - port->count = 0; - } - if (port->count) { - spin_unlock_irqrestore(&port->lock, flags); - goto done; - } - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters by - * setting tty->closing. - */ - tty->closing = 1; - spin_unlock_irqrestore(&port->lock, flags); - - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { - /* - * hack: open-coded tty_wait_until_sent to avoid - * recursive tty_lock - */ - long timeout = msecs_to_jiffies(port->closing_wait); - if (wait_event_interruptible_timeout(tty->write_wait, - !tty_chars_in_buffer(tty), timeout) >= 0) - __uart_wait_until_sent(uport, timeout); - } - - /* - * At this point, we stop accepting input. To do this, we - * disable the receive line status interrupts. - */ - if (port->flags & ASYNC_INITIALIZED) { - unsigned long flags; - spin_lock_irqsave(&uport->lock, flags); - uport->ops->stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - __uart_wait_until_sent(uport, uport->timeout); - } - - uart_shutdown(tty, state); - uart_flush_buffer(tty); - - tty_ldisc_flush(tty); - - tty_port_tty_set(port, NULL); - spin_lock_irqsave(&port->lock, flags); - tty->closing = 0; - - if (port->blocked_open) { - spin_unlock_irqrestore(&port->lock, flags); - if (port->close_delay) - msleep_interruptible(port->close_delay); - spin_lock_irqsave(&port->lock, flags); - } else if (!uart_console(uport)) { - spin_unlock_irqrestore(&port->lock, flags); - uart_change_pm(state, 3); - spin_lock_irqsave(&port->lock, flags); - } - - /* - * Wake up anyone trying to open this port. - */ - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); - spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&port->open_wait); - -done: - mutex_unlock(&port->mutex); -} - -static void __uart_wait_until_sent(struct uart_port *port, int timeout) -{ - unsigned long char_time, expire; - - if (port->type == PORT_UNKNOWN || port->fifosize == 0) - return; - - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - 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 the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than port->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*port->timeout. - */ - if (timeout == 0 || timeout > 2 * port->timeout) - timeout = 2 * port->timeout; - - expire = jiffies + timeout; - - pr_debug("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", - port->line, jiffies, expire); - - /* - * Check whether the transmitter is empty every 'char_time'. - * 'timeout' / 'expire' give us the maximum amount of time - * we wait. - */ - while (!port->ops->tx_empty(port)) { - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (time_after(jiffies, expire)) - break; - } - set_current_state(TASK_RUNNING); /* might not be needed */ -} - -static void uart_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->uart_port; - - tty_lock(); - __uart_wait_until_sent(port, timeout); - tty_unlock(); -} - -/* - * This is called with the BKL held in - * linux/drivers/char/tty_io.c:do_tty_hangup() - * We're called from the eventd thread, so we can sleep for - * a _short_ time only. - */ -static void uart_hangup(struct tty_struct *tty) -{ - struct uart_state *state = tty->driver_data; - struct tty_port *port = &state->port; - unsigned long flags; - - BUG_ON(!tty_locked()); - pr_debug("uart_hangup(%d)\n", state->uart_port->line); - - mutex_lock(&port->mutex); - if (port->flags & ASYNC_NORMAL_ACTIVE) { - uart_flush_buffer(tty); - uart_shutdown(tty, state); - spin_lock_irqsave(&port->lock, flags); - port->count = 0; - clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); - spin_unlock_irqrestore(&port->lock, flags); - tty_port_tty_set(port, NULL); - wake_up_interruptible(&port->open_wait); - wake_up_interruptible(&port->delta_msr_wait); - } - mutex_unlock(&port->mutex); -} - -/** - * uart_update_termios - update the terminal hw settings - * @tty: tty associated with UART - * @state: UART to update - * - * Copy across the serial console cflag setting into the termios settings - * for the initial open of the port. This allows continuity between the - * kernel settings, and the settings init adopts when it opens the port - * for the first time. - */ -static void uart_update_termios(struct tty_struct *tty, - struct uart_state *state) -{ - struct uart_port *port = state->uart_port; - - if (uart_console(port) && port->cons->cflag) { - tty->termios->c_cflag = port->cons->cflag; - port->cons->cflag = 0; - } - - /* - * If the device failed to grab its irq resources, - * or some other error occurred, don't try to talk - * to the port hardware. - */ - if (!(tty->flags & (1 << TTY_IO_ERROR))) { - /* - * Make termios settings take effect. - */ - uart_change_speed(tty, state, NULL); - - /* - * And finally enable the RTS and DTR signals. - */ - if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); - } -} - -static int uart_carrier_raised(struct tty_port *port) -{ - struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; - int mctrl; - spin_lock_irq(&uport->lock); - uport->ops->enable_ms(uport); - mctrl = uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); - if (mctrl & TIOCM_CAR) - return 1; - return 0; -} - -static void uart_dtr_rts(struct tty_port *port, int onoff) -{ - struct uart_state *state = container_of(port, struct uart_state, port); - struct uart_port *uport = state->uart_port; - - if (onoff) { - uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); - - /* - * If this is the first open to succeed, - * adjust things to suit. - */ - if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags)) - uart_update_termios(port->tty, state); - } - else - uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); -} - -static struct uart_state *uart_get(struct uart_driver *drv, int line) -{ - struct uart_state *state; - struct tty_port *port; - int ret = 0; - - state = drv->state + line; - port = &state->port; - if (mutex_lock_interruptible(&port->mutex)) { - ret = -ERESTARTSYS; - goto err; - } - - port->count++; - if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { - ret = -ENXIO; - goto err_unlock; - } - return state; - - err_unlock: - port->count--; - mutex_unlock(&port->mutex); - err: - return ERR_PTR(ret); -} - -/* - * calls to uart_open are serialised by the BKL in - * fs/char_dev.c:chrdev_open() - * Note that if this fails, then uart_close() _will_ be called. - * - * In time, we want to scrap the "opening nonpresent ports" - * behaviour and implement an alternative way for setserial - * to set base addresses/ports/types. This will allow us to - * get rid of a certain amount of extra tests. - */ -static int uart_open(struct tty_struct *tty, struct file *filp) -{ - struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; - struct uart_state *state; - struct tty_port *port; - int retval, line = tty->index; - - BUG_ON(!tty_locked()); - pr_debug("uart_open(%d) called\n", line); - - /* - * tty->driver->num won't change, so we won't fail here with - * tty->driver_data set to something non-NULL (and therefore - * we won't get caught by uart_close()). - */ - retval = -ENODEV; - if (line >= tty->driver->num) - goto fail; - - /* - * We take the semaphore inside uart_get to guarantee that we won't - * be re-entered while allocating the state structure, or while we - * request any IRQs that the driver may need. This also has the nice - * side-effect that it delays the action of uart_hangup, so we can - * guarantee that state->port.tty will always contain something - * reasonable. - */ - state = uart_get(drv, line); - if (IS_ERR(state)) { - retval = PTR_ERR(state); - goto fail; - } - port = &state->port; - - /* - * Once we set tty->driver_data here, we are guaranteed that - * uart_close() will decrement the driver module use count. - * Any failures from here onwards should not touch the count. - */ - tty->driver_data = state; - state->uart_port->state = state; - tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; - tty->alt_speed = 0; - tty_port_tty_set(port, tty); - - /* - * If the port is in the middle of closing, bail out now. - */ - if (tty_hung_up_p(filp)) { - retval = -EAGAIN; - port->count--; - mutex_unlock(&port->mutex); - goto fail; - } - - /* - * Make sure the device is in D0 state. - */ - if (port->count == 1) - uart_change_pm(state, 0); - - /* - * Start up the serial port. - */ - retval = uart_startup(tty, state, 0); - - /* - * If we succeeded, wait until the port is ready. - */ - mutex_unlock(&port->mutex); - if (retval == 0) - retval = tty_port_block_til_ready(port, tty, filp); - -fail: - return retval; -} - -static const char *uart_type(struct uart_port *port) -{ - const char *str = NULL; - - if (port->ops->type) - str = port->ops->type(port); - - if (!str) - str = "unknown"; - - return str; -} - -#ifdef CONFIG_PROC_FS - -static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) -{ - struct uart_state *state = drv->state + i; - struct tty_port *port = &state->port; - int pm_state; - struct uart_port *uport = state->uart_port; - char stat_buf[32]; - unsigned int status; - int mmio; - - if (!uport) - return; - - mmio = uport->iotype >= UPIO_MEM; - seq_printf(m, "%d: uart:%s %s%08llX irq:%d", - uport->line, uart_type(uport), - mmio ? "mmio:0x" : "port:", - mmio ? (unsigned long long)uport->mapbase - : (unsigned long long)uport->iobase, - uport->irq); - - if (uport->type == PORT_UNKNOWN) { - seq_putc(m, '\n'); - return; - } - - if (capable(CAP_SYS_ADMIN)) { - mutex_lock(&port->mutex); - pm_state = state->pm_state; - if (pm_state) - uart_change_pm(state, 0); - spin_lock_irq(&uport->lock); - status = uport->ops->get_mctrl(uport); - spin_unlock_irq(&uport->lock); - if (pm_state) - uart_change_pm(state, pm_state); - mutex_unlock(&port->mutex); - - seq_printf(m, " tx:%d rx:%d", - uport->icount.tx, uport->icount.rx); - if (uport->icount.frame) - seq_printf(m, " fe:%d", - uport->icount.frame); - if (uport->icount.parity) - seq_printf(m, " pe:%d", - uport->icount.parity); - if (uport->icount.brk) - seq_printf(m, " brk:%d", - uport->icount.brk); - if (uport->icount.overrun) - seq_printf(m, " oe:%d", - uport->icount.overrun); - -#define INFOBIT(bit, str) \ - if (uport->mctrl & (bit)) \ - strncat(stat_buf, (str), sizeof(stat_buf) - \ - strlen(stat_buf) - 2) -#define STATBIT(bit, str) \ - if (status & (bit)) \ - strncat(stat_buf, (str), sizeof(stat_buf) - \ - strlen(stat_buf) - 2) - - stat_buf[0] = '\0'; - stat_buf[1] = '\0'; - INFOBIT(TIOCM_RTS, "|RTS"); - STATBIT(TIOCM_CTS, "|CTS"); - INFOBIT(TIOCM_DTR, "|DTR"); - STATBIT(TIOCM_DSR, "|DSR"); - STATBIT(TIOCM_CAR, "|CD"); - STATBIT(TIOCM_RNG, "|RI"); - if (stat_buf[0]) - stat_buf[0] = ' '; - - seq_puts(m, stat_buf); - } - seq_putc(m, '\n'); -#undef STATBIT -#undef INFOBIT -} - -static int uart_proc_show(struct seq_file *m, void *v) -{ - struct tty_driver *ttydrv = m->private; - struct uart_driver *drv = ttydrv->driver_state; - int i; - - seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", - "", "", ""); - for (i = 0; i < drv->nr; i++) - uart_line_info(m, drv, i); - return 0; -} - -static int uart_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, uart_proc_show, PDE(inode)->data); -} - -static const struct file_operations uart_proc_fops = { - .owner = THIS_MODULE, - .open = uart_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - -#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) -/* - * uart_console_write - write a console message to a serial port - * @port: the port to write the message - * @s: array of characters - * @count: number of characters in string to write - * @write: function to write character to port - */ -void uart_console_write(struct uart_port *port, const char *s, - unsigned int count, - void (*putchar)(struct uart_port *, int)) -{ - unsigned int i; - - for (i = 0; i < count; i++, s++) { - if (*s == '\n') - putchar(port, '\r'); - putchar(port, *s); - } -} -EXPORT_SYMBOL_GPL(uart_console_write); - -/* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ -struct uart_port * __init -uart_get_console(struct uart_port *ports, int nr, struct console *co) -{ - int idx = co->index; - - if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && - ports[idx].membase == NULL)) - for (idx = 0; idx < nr; idx++) - if (ports[idx].iobase != 0 || - ports[idx].membase != NULL) - break; - - co->index = idx; - - return ports + idx; -} - -/** - * uart_parse_options - Parse serial port baud/parity/bits/flow contro. - * @options: pointer to option string - * @baud: pointer to an 'int' variable for the baud rate. - * @parity: pointer to an 'int' variable for the parity. - * @bits: pointer to an 'int' variable for the number of data bits. - * @flow: pointer to an 'int' variable for the flow control character. - * - * uart_parse_options decodes a string containing the serial console - * options. The format of the string is , - * eg: 115200n8r - */ -void -uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) -{ - char *s = options; - - *baud = simple_strtoul(s, NULL, 10); - while (*s >= '0' && *s <= '9') - s++; - if (*s) - *parity = *s++; - if (*s) - *bits = *s++ - '0'; - if (*s) - *flow = *s; -} -EXPORT_SYMBOL_GPL(uart_parse_options); - -struct baud_rates { - unsigned int rate; - unsigned int cflag; -}; - -static const struct baud_rates baud_rates[] = { - { 921600, B921600 }, - { 460800, B460800 }, - { 230400, B230400 }, - { 115200, B115200 }, - { 57600, B57600 }, - { 38400, B38400 }, - { 19200, B19200 }, - { 9600, B9600 }, - { 4800, B4800 }, - { 2400, B2400 }, - { 1200, B1200 }, - { 0, B38400 } -}; - -/** - * uart_set_options - setup the serial console parameters - * @port: pointer to the serial ports uart_port structure - * @co: console pointer - * @baud: baud rate - * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) - * @bits: number of data bits - * @flow: flow control character - 'r' (rts) - */ -int -uart_set_options(struct uart_port *port, struct console *co, - int baud, int parity, int bits, int flow) -{ - struct ktermios termios; - static struct ktermios dummy; - int i; - - /* - * Ensure that the serial console lock is initialised - * early. - */ - spin_lock_init(&port->lock); - lockdep_set_class(&port->lock, &port_lock_key); - - memset(&termios, 0, sizeof(struct ktermios)); - - termios.c_cflag = CREAD | HUPCL | CLOCAL; - - /* - * Construct a cflag setting. - */ - for (i = 0; baud_rates[i].rate; i++) - if (baud_rates[i].rate <= baud) - break; - - termios.c_cflag |= baud_rates[i].cflag; - - if (bits == 7) - termios.c_cflag |= CS7; - else - termios.c_cflag |= CS8; - - switch (parity) { - case 'o': case 'O': - termios.c_cflag |= PARODD; - /*fall through*/ - case 'e': case 'E': - termios.c_cflag |= PARENB; - break; - } - - if (flow == 'r') - termios.c_cflag |= CRTSCTS; - - /* - * some uarts on other side don't support no flow control. - * So we set * DTR in host uart to make them happy - */ - port->mctrl |= TIOCM_DTR; - - port->ops->set_termios(port, &termios, &dummy); - /* - * Allow the setting of the UART parameters with a NULL console - * too: - */ - if (co) - co->cflag = termios.c_cflag; - - return 0; -} -EXPORT_SYMBOL_GPL(uart_set_options); -#endif /* CONFIG_SERIAL_CORE_CONSOLE */ - -static void uart_change_pm(struct uart_state *state, int pm_state) -{ - struct uart_port *port = state->uart_port; - - if (state->pm_state != pm_state) { - if (port->ops->pm) - port->ops->pm(port, pm_state, state->pm_state); - state->pm_state = pm_state; - } -} - -struct uart_match { - struct uart_port *port; - struct uart_driver *driver; -}; - -static int serial_match_port(struct device *dev, void *data) -{ - struct uart_match *match = data; - struct tty_driver *tty_drv = match->driver->tty_driver; - dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + - match->port->line; - - return dev->devt == devt; /* Actually, only one tty per port */ -} - -int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) -{ - struct uart_state *state = drv->state + uport->line; - struct tty_port *port = &state->port; - struct device *tty_dev; - struct uart_match match = {uport, drv}; - struct tty_struct *tty; - - mutex_lock(&port->mutex); - - /* Must be inside the mutex lock until we convert to tty_port */ - tty = port->tty; - - tty_dev = device_find_child(uport->dev, &match, serial_match_port); - if (device_may_wakeup(tty_dev)) { - if (!enable_irq_wake(uport->irq)) - uport->irq_wake = 1; - put_device(tty_dev); - mutex_unlock(&port->mutex); - return 0; - } - if (console_suspend_enabled || !uart_console(uport)) - uport->suspended = 1; - - if (port->flags & ASYNC_INITIALIZED) { - const struct uart_ops *ops = uport->ops; - int tries; - - if (console_suspend_enabled || !uart_console(uport)) { - set_bit(ASYNCB_SUSPENDED, &port->flags); - clear_bit(ASYNCB_INITIALIZED, &port->flags); - - spin_lock_irq(&uport->lock); - ops->stop_tx(uport); - ops->set_mctrl(uport, 0); - ops->stop_rx(uport); - spin_unlock_irq(&uport->lock); - } - - /* - * Wait for the transmitter to empty. - */ - for (tries = 3; !ops->tx_empty(uport) && tries; tries--) - msleep(10); - if (!tries) - printk(KERN_ERR "%s%s%s%d: Unable to drain " - "transmitter\n", - uport->dev ? dev_name(uport->dev) : "", - uport->dev ? ": " : "", - drv->dev_name, - drv->tty_driver->name_base + uport->line); - - if (console_suspend_enabled || !uart_console(uport)) - ops->shutdown(uport); - } - - /* - * Disable the console device before suspending. - */ - if (console_suspend_enabled && uart_console(uport)) - console_stop(uport->cons); - - if (console_suspend_enabled || !uart_console(uport)) - uart_change_pm(state, 3); - - mutex_unlock(&port->mutex); - - return 0; -} - -int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) -{ - struct uart_state *state = drv->state + uport->line; - struct tty_port *port = &state->port; - struct device *tty_dev; - struct uart_match match = {uport, drv}; - struct ktermios termios; - - mutex_lock(&port->mutex); - - tty_dev = device_find_child(uport->dev, &match, serial_match_port); - if (!uport->suspended && device_may_wakeup(tty_dev)) { - if (uport->irq_wake) { - disable_irq_wake(uport->irq); - uport->irq_wake = 0; - } - mutex_unlock(&port->mutex); - return 0; - } - uport->suspended = 0; - - /* - * Re-enable the console device after suspending. - */ - if (console_suspend_enabled && uart_console(uport)) { - /* - * First try to use the console cflag setting. - */ - memset(&termios, 0, sizeof(struct ktermios)); - termios.c_cflag = uport->cons->cflag; - - /* - * If that's unset, use the tty termios setting. - */ - if (port->tty && port->tty->termios && termios.c_cflag == 0) - termios = *(port->tty->termios); - - uart_change_pm(state, 0); - uport->ops->set_termios(uport, &termios, NULL); - console_start(uport->cons); - } - - if (port->flags & ASYNC_SUSPENDED) { - const struct uart_ops *ops = uport->ops; - int ret; - - uart_change_pm(state, 0); - spin_lock_irq(&uport->lock); - ops->set_mctrl(uport, 0); - spin_unlock_irq(&uport->lock); - if (console_suspend_enabled || !uart_console(uport)) { - /* Protected by port mutex for now */ - struct tty_struct *tty = port->tty; - ret = ops->startup(uport); - if (ret == 0) { - if (tty) - uart_change_speed(tty, state, NULL); - spin_lock_irq(&uport->lock); - ops->set_mctrl(uport, uport->mctrl); - ops->start_tx(uport); - spin_unlock_irq(&uport->lock); - set_bit(ASYNCB_INITIALIZED, &port->flags); - } else { - /* - * Failed to resume - maybe hardware went away? - * Clear the "initialized" flag so we won't try - * to call the low level drivers shutdown method. - */ - uart_shutdown(tty, state); - } - } - - clear_bit(ASYNCB_SUSPENDED, &port->flags); - } - - mutex_unlock(&port->mutex); - - return 0; -} - -static inline void -uart_report_port(struct uart_driver *drv, struct uart_port *port) -{ - char address[64]; - - switch (port->iotype) { - case UPIO_PORT: - snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase); - break; - case UPIO_HUB6: - snprintf(address, sizeof(address), - "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); - break; - case UPIO_MEM: - case UPIO_MEM32: - case UPIO_AU: - case UPIO_TSI: - case UPIO_DWAPB: - case UPIO_DWAPB32: - snprintf(address, sizeof(address), - "MMIO 0x%llx", (unsigned long long)port->mapbase); - break; - default: - strlcpy(address, "*unknown*", sizeof(address)); - break; - } - - printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", - port->dev ? dev_name(port->dev) : "", - port->dev ? ": " : "", - drv->dev_name, - drv->tty_driver->name_base + port->line, - address, port->irq, uart_type(port)); -} - -static void -uart_configure_port(struct uart_driver *drv, struct uart_state *state, - struct uart_port *port) -{ - unsigned int flags; - - /* - * If there isn't a port here, don't do anything further. - */ - if (!port->iobase && !port->mapbase && !port->membase) - return; - - /* - * Now do the auto configuration stuff. Note that config_port - * is expected to claim the resources and map the port for us. - */ - flags = 0; - if (port->flags & UPF_AUTO_IRQ) - flags |= UART_CONFIG_IRQ; - if (port->flags & UPF_BOOT_AUTOCONF) { - if (!(port->flags & UPF_FIXED_TYPE)) { - port->type = PORT_UNKNOWN; - flags |= UART_CONFIG_TYPE; - } - port->ops->config_port(port, flags); - } - - if (port->type != PORT_UNKNOWN) { - unsigned long flags; - - uart_report_port(drv, port); - - /* Power up port for set_mctrl() */ - uart_change_pm(state, 0); - - /* - * Ensure that the modem control lines are de-activated. - * keep the DTR setting that is set in uart_set_options() - * We probably don't need a spinlock around this, but - */ - spin_lock_irqsave(&port->lock, flags); - port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR); - spin_unlock_irqrestore(&port->lock, flags); - - /* - * If this driver supports console, and it hasn't been - * successfully registered yet, try to re-register it. - * It may be that the port was not available. - */ - if (port->cons && !(port->cons->flags & CON_ENABLED)) - register_console(port->cons); - - /* - * Power down all ports by default, except the - * console if we have one. - */ - if (!uart_console(port)) - uart_change_pm(state, 3); - } -} - -#ifdef CONFIG_CONSOLE_POLL - -static int uart_poll_init(struct tty_driver *driver, int line, char *options) -{ - struct uart_driver *drv = driver->driver_state; - struct uart_state *state = drv->state + line; - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (!state || !state->uart_port) - return -1; - - port = state->uart_port; - if (!(port->ops->poll_get_char && port->ops->poll_put_char)) - return -1; - - if (options) { - uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(port, NULL, baud, parity, bits, flow); - } - - return 0; -} - -static int uart_poll_get_char(struct tty_driver *driver, int line) -{ - struct uart_driver *drv = driver->driver_state; - struct uart_state *state = drv->state + line; - struct uart_port *port; - - if (!state || !state->uart_port) - return -1; - - port = state->uart_port; - return port->ops->poll_get_char(port); -} - -static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) -{ - struct uart_driver *drv = driver->driver_state; - struct uart_state *state = drv->state + line; - struct uart_port *port; - - if (!state || !state->uart_port) - return; - - port = state->uart_port; - port->ops->poll_put_char(port, ch); -} -#endif - -static const struct tty_operations uart_ops = { - .open = uart_open, - .close = uart_close, - .write = uart_write, - .put_char = uart_put_char, - .flush_chars = uart_flush_chars, - .write_room = uart_write_room, - .chars_in_buffer= uart_chars_in_buffer, - .flush_buffer = uart_flush_buffer, - .ioctl = uart_ioctl, - .throttle = uart_throttle, - .unthrottle = uart_unthrottle, - .send_xchar = uart_send_xchar, - .set_termios = uart_set_termios, - .set_ldisc = uart_set_ldisc, - .stop = uart_stop, - .start = uart_start, - .hangup = uart_hangup, - .break_ctl = uart_break_ctl, - .wait_until_sent= uart_wait_until_sent, -#ifdef CONFIG_PROC_FS - .proc_fops = &uart_proc_fops, -#endif - .tiocmget = uart_tiocmget, - .tiocmset = uart_tiocmset, - .get_icount = uart_get_icount, -#ifdef CONFIG_CONSOLE_POLL - .poll_init = uart_poll_init, - .poll_get_char = uart_poll_get_char, - .poll_put_char = uart_poll_put_char, -#endif -}; - -static const struct tty_port_operations uart_port_ops = { - .carrier_raised = uart_carrier_raised, - .dtr_rts = uart_dtr_rts, -}; - -/** - * uart_register_driver - register a driver with the uart core layer - * @drv: low level driver structure - * - * Register a uart driver with the core driver. We in turn register - * with the tty layer, and initialise the core driver per-port state. - * - * We have a proc file in /proc/tty/driver which is named after the - * normal driver. - * - * drv->port should be NULL, and the per-port structures should be - * registered using uart_add_one_port after this call has succeeded. - */ -int uart_register_driver(struct uart_driver *drv) -{ - struct tty_driver *normal; - int i, retval; - - BUG_ON(drv->state); - - /* - * Maybe we should be using a slab cache for this, especially if - * we have a large number of ports to handle. - */ - drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); - if (!drv->state) - goto out; - - normal = alloc_tty_driver(drv->nr); - if (!normal) - goto out_kfree; - - drv->tty_driver = normal; - - normal->owner = drv->owner; - normal->driver_name = drv->driver_name; - normal->name = drv->dev_name; - normal->major = drv->major; - normal->minor_start = drv->minor; - 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->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; - normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - normal->driver_state = drv; - tty_set_operations(normal, &uart_ops); - - /* - * Initialise the UART state(s). - */ - for (i = 0; i < drv->nr; i++) { - struct uart_state *state = drv->state + i; - struct tty_port *port = &state->port; - - tty_port_init(port); - port->ops = &uart_port_ops; - port->close_delay = 500; /* .5 seconds */ - port->closing_wait = 30000; /* 30 seconds */ - tasklet_init(&state->tlet, uart_tasklet_action, - (unsigned long)state); - } - - retval = tty_register_driver(normal); - if (retval >= 0) - return retval; - - put_tty_driver(normal); -out_kfree: - kfree(drv->state); -out: - return -ENOMEM; -} - -/** - * uart_unregister_driver - remove a driver from the uart core layer - * @drv: low level driver structure - * - * Remove all references to a driver from the core driver. The low - * level driver must have removed all its ports via the - * uart_remove_one_port() if it registered them with uart_add_one_port(). - * (ie, drv->port == NULL) - */ -void uart_unregister_driver(struct uart_driver *drv) -{ - struct tty_driver *p = drv->tty_driver; - tty_unregister_driver(p); - put_tty_driver(p); - kfree(drv->state); - drv->tty_driver = NULL; -} - -struct tty_driver *uart_console_device(struct console *co, int *index) -{ - struct uart_driver *p = co->data; - *index = co->index; - return p->tty_driver; -} - -/** - * uart_add_one_port - attach a driver-defined port structure - * @drv: pointer to the uart low level driver structure for this port - * @uport: uart port structure to use for this port. - * - * This allows the driver to register its own uart_port structure - * with the core driver. The main purpose is to allow the low - * level uart drivers to expand uart_port, rather than having yet - * more levels of structures. - */ -int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) -{ - struct uart_state *state; - struct tty_port *port; - int ret = 0; - struct device *tty_dev; - - BUG_ON(in_interrupt()); - - if (uport->line >= drv->nr) - return -EINVAL; - - state = drv->state + uport->line; - port = &state->port; - - mutex_lock(&port_mutex); - mutex_lock(&port->mutex); - if (state->uart_port) { - ret = -EINVAL; - goto out; - } - - state->uart_port = uport; - state->pm_state = -1; - - uport->cons = drv->cons; - uport->state = state; - - /* - * If this port is a console, then the spinlock is already - * initialised. - */ - if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) { - spin_lock_init(&uport->lock); - lockdep_set_class(&uport->lock, &port_lock_key); - } - - uart_configure_port(drv, state, uport); - - /* - * Register the port whether it's detected or not. This allows - * setserial to be used to alter this ports parameters. - */ - tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); - if (likely(!IS_ERR(tty_dev))) { - device_init_wakeup(tty_dev, 1); - device_set_wakeup_enable(tty_dev, 0); - } else - printk(KERN_ERR "Cannot register tty device on line %d\n", - uport->line); - - /* - * Ensure UPF_DEAD is not set. - */ - uport->flags &= ~UPF_DEAD; - - out: - mutex_unlock(&port->mutex); - mutex_unlock(&port_mutex); - - return ret; -} - -/** - * uart_remove_one_port - detach a driver defined port structure - * @drv: pointer to the uart low level driver structure for this port - * @uport: uart port structure for this port - * - * This unhooks (and hangs up) the specified port structure from the - * core driver. No further calls will be made to the low-level code - * for this port. - */ -int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) -{ - struct uart_state *state = drv->state + uport->line; - struct tty_port *port = &state->port; - - BUG_ON(in_interrupt()); - - if (state->uart_port != uport) - printk(KERN_ALERT "Removing wrong port: %p != %p\n", - state->uart_port, uport); - - mutex_lock(&port_mutex); - - /* - * Mark the port "dead" - this prevents any opens from - * succeeding while we shut down the port. - */ - mutex_lock(&port->mutex); - uport->flags |= UPF_DEAD; - mutex_unlock(&port->mutex); - - /* - * Remove the devices from the tty layer - */ - tty_unregister_device(drv->tty_driver, uport->line); - - if (port->tty) - tty_vhangup(port->tty); - - /* - * Free the port IO and memory resources, if any. - */ - if (uport->type != PORT_UNKNOWN) - uport->ops->release_port(uport); - - /* - * Indicate that there isn't a port here anymore. - */ - uport->type = PORT_UNKNOWN; - - /* - * Kill the tasklet, and free resources. - */ - tasklet_kill(&state->tlet); - - state->uart_port = NULL; - mutex_unlock(&port_mutex); - - return 0; -} - -/* - * Are the two ports equivalent? - */ -int uart_match_port(struct uart_port *port1, struct uart_port *port2) -{ - if (port1->iotype != port2->iotype) - return 0; - - switch (port1->iotype) { - case UPIO_PORT: - return (port1->iobase == port2->iobase); - case UPIO_HUB6: - return (port1->iobase == port2->iobase) && - (port1->hub6 == port2->hub6); - case UPIO_MEM: - case UPIO_MEM32: - case UPIO_AU: - case UPIO_TSI: - case UPIO_DWAPB: - case UPIO_DWAPB32: - return (port1->mapbase == port2->mapbase); - } - return 0; -} -EXPORT_SYMBOL(uart_match_port); - -EXPORT_SYMBOL(uart_write_wakeup); -EXPORT_SYMBOL(uart_register_driver); -EXPORT_SYMBOL(uart_unregister_driver); -EXPORT_SYMBOL(uart_suspend_port); -EXPORT_SYMBOL(uart_resume_port); -EXPORT_SYMBOL(uart_add_one_port); -EXPORT_SYMBOL(uart_remove_one_port); - -MODULE_DESCRIPTION("Serial driver core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c deleted file mode 100644 index 93760b2ea172..000000000000 --- a/drivers/serial/serial_cs.c +++ /dev/null @@ -1,869 +0,0 @@ -/*====================================================================== - - A driver for PCMCIA serial devices - - serial_cs.c 1.134 2002/05/04 05:48:53 - - The contents of this file are subject to the Mozilla Public - License Version 1.1 (the "License"); you may not use this file - except in compliance with the License. You may obtain a copy of - the License at http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS - IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or - implied. See the License for the specific language governing - rights and limitations under the License. - - The initial developer of the original code is David A. Hinds - . Portions created by David A. Hinds - are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - - Alternatively, the contents of this file may be used under the - terms of the GNU General Public License version 2 (the "GPL"), in which - case the provisions of the GPL are applicable instead of the - above. If you wish to allow the use of your version of this file - only under the terms of the GPL and not to allow others to use - your version of this file under the MPL, indicate your decision - by deleting the provisions above and replace them with the notice - and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this - file under either the MPL or the GPL. - -======================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "8250.h" - - -/*====================================================================*/ - -/* Parameters that can be set with 'insmod' */ - -/* Enable the speaker? */ -static int do_sound = 1; -/* Skip strict UART tests? */ -static int buggy_uart; - -module_param(do_sound, int, 0444); -module_param(buggy_uart, int, 0444); - -/*====================================================================*/ - -/* Table of multi-port card ID's */ - -struct serial_quirk { - unsigned int manfid; - unsigned int prodid; - int multi; /* 1 = multifunction, > 1 = # ports */ - void (*config)(struct pcmcia_device *); - void (*setup)(struct pcmcia_device *, struct uart_port *); - void (*wakeup)(struct pcmcia_device *); - int (*post)(struct pcmcia_device *); -}; - -struct serial_info { - struct pcmcia_device *p_dev; - int ndev; - int multi; - int slave; - int manfid; - int prodid; - int c950ctrl; - int line[4]; - const struct serial_quirk *quirk; -}; - -struct serial_cfg_mem { - tuple_t tuple; - cisparse_t parse; - u_char buf[256]; -}; - -/* - * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" - * manfid 0x0160, 0x0104 - * This card appears to have a 14.7456MHz clock. - */ -/* Generic Modem: MD55x (GPRS/EDGE) have - * Elan VPU16551 UART with 14.7456MHz oscillator - * manfid 0x015D, 0x4C45 - */ -static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) -{ - port->uartclk = 14745600; -} - -static int quirk_post_ibm(struct pcmcia_device *link) -{ - u8 val; - int ret; - - ret = pcmcia_read_config_byte(link, 0x800, &val); - if (ret) - goto failed; - - ret = pcmcia_write_config_byte(link, 0x800, val | 1); - if (ret) - goto failed; - return 0; - - failed: - return -ENODEV; -} - -/* - * Nokia cards are not really multiport cards. Shouldn't this - * be handled by setting the quirk entry .multi = 0 | 1 ? - */ -static void quirk_config_nokia(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - if (info->multi > 1) - info->multi = 1; -} - -static void quirk_wakeup_oxsemi(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - if (info->c950ctrl) - outb(12, info->c950ctrl + 1); -} - -/* request_region? oxsemi branch does no request_region too... */ -/* - * This sequence is needed to properly initialize MC45 attached to OXCF950. - * I tried decreasing these msleep()s, but it worked properly (survived - * 1000 stop/start operations) with these timeouts (or bigger). - */ -static void quirk_wakeup_possio_gcc(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - unsigned int ctrl = info->c950ctrl; - - outb(0xA, ctrl + 1); - msleep(100); - outb(0xE, ctrl + 1); - msleep(300); - outb(0xC, ctrl + 1); - msleep(100); - outb(0xE, ctrl + 1); - msleep(200); - outb(0xF, ctrl + 1); - msleep(100); - outb(0xE, ctrl + 1); - msleep(100); - outb(0xC, ctrl + 1); -} - -/* - * Socket Dual IO: this enables irq's for second port - */ -static void quirk_config_socket(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - if (info->multi) - link->config_flags |= CONF_ENABLE_ESR; -} - -static const struct serial_quirk quirks[] = { - { - .manfid = 0x0160, - .prodid = 0x0104, - .multi = -1, - .setup = quirk_setup_brainboxes_0104, - }, { - .manfid = 0x015D, - .prodid = 0x4C45, - .multi = -1, - .setup = quirk_setup_brainboxes_0104, - }, { - .manfid = MANFID_IBM, - .prodid = ~0, - .multi = -1, - .post = quirk_post_ibm, - }, { - .manfid = MANFID_INTEL, - .prodid = PRODID_INTEL_DUAL_RS232, - .multi = 2, - }, { - .manfid = MANFID_NATINST, - .prodid = PRODID_NATINST_QUAD_RS232, - .multi = 4, - }, { - .manfid = MANFID_NOKIA, - .prodid = ~0, - .multi = -1, - .config = quirk_config_nokia, - }, { - .manfid = MANFID_OMEGA, - .prodid = PRODID_OMEGA_QSP_100, - .multi = 4, - }, { - .manfid = MANFID_OXSEMI, - .prodid = ~0, - .multi = -1, - .wakeup = quirk_wakeup_oxsemi, - }, { - .manfid = MANFID_POSSIO, - .prodid = PRODID_POSSIO_GCC, - .multi = -1, - .wakeup = quirk_wakeup_possio_gcc, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_DUAL_RS232, - .multi = 2, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_DUAL_RS232_D1, - .multi = 2, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_DUAL_RS232_G, - .multi = 2, - }, { - .manfid = MANFID_QUATECH, - .prodid = PRODID_QUATECH_QUAD_RS232, - .multi = 4, - }, { - .manfid = MANFID_SOCKET, - .prodid = PRODID_SOCKET_DUAL_RS232, - .multi = 2, - .config = quirk_config_socket, - }, { - .manfid = MANFID_SOCKET, - .prodid = ~0, - .multi = -1, - .config = quirk_config_socket, - } -}; - - -static int serial_config(struct pcmcia_device * link); - - -static void serial_remove(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i; - - dev_dbg(&link->dev, "serial_release\n"); - - /* - * Recheck to see if the device is still configured. - */ - for (i = 0; i < info->ndev; i++) - serial8250_unregister_port(info->line[i]); - - if (!info->slave) - pcmcia_disable_device(link); -} - -static int serial_suspend(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i; - - for (i = 0; i < info->ndev; i++) - serial8250_suspend_port(info->line[i]); - - return 0; -} - -static int serial_resume(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i; - - for (i = 0; i < info->ndev; i++) - serial8250_resume_port(info->line[i]); - - if (info->quirk && info->quirk->wakeup) - info->quirk->wakeup(link); - - return 0; -} - -static int serial_probe(struct pcmcia_device *link) -{ - struct serial_info *info; - - dev_dbg(&link->dev, "serial_attach()\n"); - - /* Create new serial device */ - info = kzalloc(sizeof (*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - info->p_dev = link; - link->priv = info; - - link->config_flags |= CONF_ENABLE_IRQ; - if (do_sound) - link->config_flags |= CONF_ENABLE_SPKR; - - return serial_config(link); -} - -static void serial_detach(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - - dev_dbg(&link->dev, "serial_detach\n"); - - /* - * Ensure that the ports have been released. - */ - serial_remove(link); - - /* free bits */ - kfree(info); -} - -/*====================================================================*/ - -static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, - unsigned int iobase, int irq) -{ - struct uart_port port; - int line; - - memset(&port, 0, sizeof (struct uart_port)); - port.iobase = iobase; - port.irq = irq; - port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; - port.uartclk = 1843200; - port.dev = &handle->dev; - if (buggy_uart) - port.flags |= UPF_BUGGY_UART; - - if (info->quirk && info->quirk->setup) - info->quirk->setup(handle, &port); - - line = serial8250_register_port(&port); - if (line < 0) { - printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " - "0x%04lx, irq %d failed\n", (u_long)iobase, irq); - return -EINVAL; - } - - info->line[info->ndev] = line; - info->ndev++; - - return 0; -} - -/*====================================================================*/ - -static int pfc_config(struct pcmcia_device *p_dev) -{ - unsigned int port = 0; - struct serial_info *info = p_dev->priv; - - if ((p_dev->resource[1]->end != 0) && - (resource_size(p_dev->resource[1]) == 8)) { - port = p_dev->resource[1]->start; - info->slave = 1; - } else if ((info->manfid == MANFID_OSITECH) && - (resource_size(p_dev->resource[0]) == 0x40)) { - port = p_dev->resource[0]->start + 0x28; - info->slave = 1; - } - if (info->slave) - return setup_serial(p_dev, info, port, p_dev->irq); - - dev_warn(&p_dev->dev, "no usable port range found, giving up\n"); - return -ENODEV; -} - -static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - static const int size_table[2] = { 8, 16 }; - int *try = priv_data; - - if (p_dev->resource[0]->start == 0) - return -ENODEV; - - if ((*try & 0x1) == 0) - p_dev->io_lines = 16; - - if (p_dev->resource[0]->end != size_table[(*try >> 1)]) - return -ENODEV; - - p_dev->resource[0]->end = 8; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - return pcmcia_request_io(p_dev); -} - -static int simple_config_check_notpicky(struct pcmcia_device *p_dev, - void *priv_data) -{ - static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - int j; - - if (p_dev->io_lines > 3) - return -ENODEV; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 8; - - for (j = 0; j < 5; j++) { - p_dev->resource[0]->start = base[j]; - p_dev->io_lines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; -} - -static int simple_config(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i = -ENODEV, try; - - /* First pass: look for a config entry that looks normal. - * Two tries: without IO aliases, then with aliases */ - link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; - for (try = 0; try < 4; try++) - if (!pcmcia_loop_config(link, simple_config_check, &try)) - goto found_port; - - /* Second pass: try to find an entry that isn't picky about - its base address, then try to grab any standard serial port - address, and finally try to get any free port. */ - if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) - goto found_port; - - dev_warn(&link->dev, "no usable port range found, giving up\n"); - return -1; - -found_port: - if (info->multi && (info->manfid == MANFID_3COM)) - link->config_index &= ~(0x08); - - /* - * Apply any configuration quirks. - */ - if (info->quirk && info->quirk->config) - info->quirk->config(link); - - i = pcmcia_enable_device(link); - if (i != 0) - return -1; - return setup_serial(link, info, link->resource[0]->start, link->irq); -} - -static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - int *multi = priv_data; - - if (p_dev->resource[1]->end) - return -EINVAL; - - /* The quad port cards have bad CIS's, so just look for a - window larger than 8 ports and assume it will be right */ - if (p_dev->resource[0]->end <= 8) - return -EINVAL; - - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = *multi * 8; - - if (pcmcia_request_io(p_dev)) - return -ENODEV; - return 0; -} - -static int multi_config_check_notpicky(struct pcmcia_device *p_dev, - void *priv_data) -{ - int *base2 = priv_data; - - if (!p_dev->resource[0]->end || !p_dev->resource[1]->end) - return -ENODEV; - - p_dev->resource[0]->end = p_dev->resource[1]->end = 8; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - - if (pcmcia_request_io(p_dev)) - return -ENODEV; - - *base2 = p_dev->resource[0]->start + 8; - return 0; -} - -static int multi_config(struct pcmcia_device *link) -{ - struct serial_info *info = link->priv; - int i, base2 = 0; - - link->config_flags |= CONF_AUTO_SET_IO; - /* First, look for a generic full-sized window */ - if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) - base2 = link->resource[0]->start + 8; - else { - /* If that didn't work, look for two windows */ - info->multi = 2; - if (pcmcia_loop_config(link, multi_config_check_notpicky, - &base2)) { - dev_warn(&link->dev, "no usable port range " - "found, giving up\n"); - return -ENODEV; - } - } - - if (!link->irq) - dev_warn(&link->dev, "no usable IRQ found, continuing...\n"); - - /* - * Apply any configuration quirks. - */ - if (info->quirk && info->quirk->config) - info->quirk->config(link); - - i = pcmcia_enable_device(link); - if (i != 0) - return -ENODEV; - - /* The Oxford Semiconductor OXCF950 cards are in fact single-port: - * 8 registers are for the UART, the others are extra registers. - * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. - */ - if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO && - info->prodid == PRODID_POSSIO_GCC)) { - int err; - - if (link->config_index == 1 || - link->config_index == 3) { - err = setup_serial(link, info, base2, - link->irq); - base2 = link->resource[0]->start; - } else { - err = setup_serial(link, info, link->resource[0]->start, - link->irq); - } - info->c950ctrl = base2; - - /* - * FIXME: We really should wake up the port prior to - * handing it over to the serial layer. - */ - if (info->quirk && info->quirk->wakeup) - info->quirk->wakeup(link); - - return 0; - } - - setup_serial(link, info, link->resource[0]->start, link->irq); - for (i = 0; i < info->multi - 1; i++) - setup_serial(link, info, base2 + (8 * i), - link->irq); - return 0; -} - -static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) -{ - struct serial_info *info = p_dev->priv; - - if (!p_dev->resource[0]->end) - return -EINVAL; - - if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) - info->multi = p_dev->resource[0]->end >> 3; - - if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) - && (p_dev->resource[1]->end == 8)) - info->multi = 2; - - return 0; /* break */ -} - - -static int serial_config(struct pcmcia_device * link) -{ - struct serial_info *info = link->priv; - int i; - - dev_dbg(&link->dev, "serial_config\n"); - - /* Is this a compliant multifunction card? */ - info->multi = (link->socket->functions > 1); - - /* Is this a multiport card? */ - info->manfid = link->manf_id; - info->prodid = link->card_id; - - for (i = 0; i < ARRAY_SIZE(quirks); i++) - if ((quirks[i].manfid == ~0 || - quirks[i].manfid == info->manfid) && - (quirks[i].prodid == ~0 || - quirks[i].prodid == info->prodid)) { - info->quirk = &quirks[i]; - break; - } - - /* Another check for dual-serial cards: look for either serial or - multifunction cards that ask for appropriate IO port ranges */ - if ((info->multi == 0) && - (link->has_func_id) && - (link->socket->pcmcia_pfc == 0) && - ((link->func_id == CISTPL_FUNCID_MULTI) || - (link->func_id == CISTPL_FUNCID_SERIAL))) - pcmcia_loop_config(link, serial_check_for_multi, info); - - /* - * Apply any multi-port quirk. - */ - if (info->quirk && info->quirk->multi != -1) - info->multi = info->quirk->multi; - - dev_info(&link->dev, - "trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n", - link->manf_id, link->card_id, - link->socket->pcmcia_pfc, info->multi, info->quirk); - if (link->socket->pcmcia_pfc) - i = pfc_config(link); - else if (info->multi > 1) - i = multi_config(link); - else - i = simple_config(link); - - if (i || info->ndev == 0) - goto failed; - - /* - * Apply any post-init quirk. FIXME: This should really happen - * before we register the port, since it might already be in use. - */ - if (info->quirk && info->quirk->post) - if (info->quirk->post(link)) - goto failed; - - return 0; - -failed: - dev_warn(&link->dev, "failed to initialize\n"); - serial_remove(link); - return -ENODEV; -} - -static struct pcmcia_device_id serial_ids[] = { - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), - PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), - PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), - PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), - PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), - PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070), - PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020), - PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), - PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), - PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), - PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), - PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), - PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), - PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), - PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), - PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */ - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */ - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */ - PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), - PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */ - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), - PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), - PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */ - PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */ - PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */ - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), - PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ - PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ - PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), - PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), - PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), - PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), - PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), - PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e), - PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), - PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), - PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), - PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76), - PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), - PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), - PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), - PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b), - PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), - PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb), - PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447), - PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), - PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), - PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), - PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), - PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), - PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41), - PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), - PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), - PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), - PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38), - PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */ - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ - PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), - PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), - PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), - PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3), - PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676), - PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), - PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), - PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b), - /* too generic */ - /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ - /* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */ - PCMCIA_DEVICE_FUNC_ID(2), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, serial_ids); - -MODULE_FIRMWARE("cis/PCMLM28.cis"); -MODULE_FIRMWARE("cis/DP83903.cis"); -MODULE_FIRMWARE("cis/3CCFEM556.cis"); -MODULE_FIRMWARE("cis/3CXEM556.cis"); -MODULE_FIRMWARE("cis/SW_8xx_SER.cis"); -MODULE_FIRMWARE("cis/SW_7xx_SER.cis"); -MODULE_FIRMWARE("cis/SW_555_SER.cis"); -MODULE_FIRMWARE("cis/MT5634ZLX.cis"); -MODULE_FIRMWARE("cis/COMpad2.cis"); -MODULE_FIRMWARE("cis/COMpad4.cis"); -MODULE_FIRMWARE("cis/RS-COM-2P.cis"); - -static struct pcmcia_driver serial_cs_driver = { - .owner = THIS_MODULE, - .name = "serial_cs", - .probe = serial_probe, - .remove = serial_detach, - .id_table = serial_ids, - .suspend = serial_suspend, - .resume = serial_resume, -}; - -static int __init init_serial_cs(void) -{ - return pcmcia_register_driver(&serial_cs_driver); -} - -static void __exit exit_serial_cs(void) -{ - pcmcia_unregister_driver(&serial_cs_driver); -} - -module_init(init_serial_cs); -module_exit(exit_serial_cs); - -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c deleted file mode 100644 index b1962025b1aa..000000000000 --- a/drivers/serial/serial_ks8695.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * drivers/serial/serial_ks8695.c - * - * Driver for KS8695 serial ports - * - * Based on drivers/serial/serial_amba.c, by Kam Lee. - * - * Copyright 2002-2005 Micrel Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#if defined(CONFIG_SERIAL_KS8695_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - - -#define SERIAL_KS8695_MAJOR 204 -#define SERIAL_KS8695_MINOR 16 -#define SERIAL_KS8695_DEVNAME "ttyAM" - -#define SERIAL_KS8695_NR 1 - -/* - * Access macros for the KS8695 UART - */ -#define UART_GET_CHAR(p) (__raw_readl((p)->membase + KS8695_URRB) & 0xFF) -#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + KS8695_URTH) -#define UART_GET_FCR(p) __raw_readl((p)->membase + KS8695_URFC) -#define UART_PUT_FCR(p, c) __raw_writel((c), (p)->membase + KS8695_URFC) -#define UART_GET_MSR(p) __raw_readl((p)->membase + KS8695_URMS) -#define UART_GET_LSR(p) __raw_readl((p)->membase + KS8695_URLS) -#define UART_GET_LCR(p) __raw_readl((p)->membase + KS8695_URLC) -#define UART_PUT_LCR(p, c) __raw_writel((c), (p)->membase + KS8695_URLC) -#define UART_GET_MCR(p) __raw_readl((p)->membase + KS8695_URMC) -#define UART_PUT_MCR(p, c) __raw_writel((c), (p)->membase + KS8695_URMC) -#define UART_GET_BRDR(p) __raw_readl((p)->membase + KS8695_URBD) -#define UART_PUT_BRDR(p, c) __raw_writel((c), (p)->membase + KS8695_URBD) - -#define KS8695_CLR_TX_INT() __raw_writel(1 << KS8695_IRQ_UART_TX, KS8695_IRQ_VA + KS8695_INTST) - -#define UART_DUMMY_LSR_RX 0x100 -#define UART_PORT_SIZE (KS8695_USR - KS8695_URRB + 4) - -static inline int tx_enabled(struct uart_port *port) -{ - return port->unused[0] & 1; -} - -static inline int rx_enabled(struct uart_port *port) -{ - return port->unused[0] & 2; -} - -static inline int ms_enabled(struct uart_port *port) -{ - return port->unused[0] & 4; -} - -static inline void ms_enable(struct uart_port *port, int enabled) -{ - if(enabled) - port->unused[0] |= 4; - else - port->unused[0] &= ~4; -} - -static inline void rx_enable(struct uart_port *port, int enabled) -{ - if(enabled) - port->unused[0] |= 2; - else - port->unused[0] &= ~2; -} - -static inline void tx_enable(struct uart_port *port, int enabled) -{ - if(enabled) - port->unused[0] |= 1; - else - port->unused[0] &= ~1; -} - - -#ifdef SUPPORT_SYSRQ -static struct console ks8695_console; -#endif - -static void ks8695uart_stop_tx(struct uart_port *port) -{ - if (tx_enabled(port)) { - /* use disable_irq_nosync() and not disable_irq() to avoid self - * imposed deadlock by not waiting for irq handler to end, - * since this ks8695uart_stop_tx() is called from interrupt context. - */ - disable_irq_nosync(KS8695_IRQ_UART_TX); - tx_enable(port, 0); - } -} - -static void ks8695uart_start_tx(struct uart_port *port) -{ - if (!tx_enabled(port)) { - enable_irq(KS8695_IRQ_UART_TX); - tx_enable(port, 1); - } -} - -static void ks8695uart_stop_rx(struct uart_port *port) -{ - if (rx_enabled(port)) { - disable_irq(KS8695_IRQ_UART_RX); - rx_enable(port, 0); - } -} - -static void ks8695uart_enable_ms(struct uart_port *port) -{ - if (!ms_enabled(port)) { - enable_irq(KS8695_IRQ_UART_MODEM_STATUS); - ms_enable(port,1); - } -} - -static void ks8695uart_disable_ms(struct uart_port *port) -{ - if (ms_enabled(port)) { - disable_irq(KS8695_IRQ_UART_MODEM_STATUS); - ms_enable(port,0); - } -} - -static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct tty_struct *tty = port->state->port.tty; - unsigned int status, ch, lsr, flg, max_count = 256; - - status = UART_GET_LSR(port); /* clears pending LSR interrupts */ - while ((status & URLS_URDR) && max_count--) { - ch = UART_GET_CHAR(port); - flg = TTY_NORMAL; - - port->icount.rx++; - - /* - * Note that the error handling code is - * out of the main execution path - */ - lsr = UART_GET_LSR(port) | UART_DUMMY_LSR_RX; - if (unlikely(lsr & (URLS_URBI | URLS_URPE | URLS_URFE | URLS_URROE))) { - if (lsr & URLS_URBI) { - lsr &= ~(URLS_URFE | URLS_URPE); - port->icount.brk++; - if (uart_handle_break(port)) - goto ignore_char; - } - if (lsr & URLS_URPE) - port->icount.parity++; - if (lsr & URLS_URFE) - port->icount.frame++; - if (lsr & URLS_URROE) - port->icount.overrun++; - - lsr &= port->read_status_mask; - - if (lsr & URLS_URBI) - flg = TTY_BREAK; - else if (lsr & URLS_URPE) - flg = TTY_PARITY; - else if (lsr & URLS_URFE) - flg = TTY_FRAME; - } - - if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; - - uart_insert_char(port, lsr, URLS_URROE, ch, flg); - -ignore_char: - status = UART_GET_LSR(port); - } - tty_flip_buffer_push(tty); - - return IRQ_HANDLED; -} - - -static irqreturn_t ks8695uart_tx_chars(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct circ_buf *xmit = &port->state->xmit; - unsigned int count; - - if (port->x_char) { - KS8695_CLR_TX_INT(); - UART_PUT_CHAR(port, port->x_char); - port->icount.tx++; - port->x_char = 0; - return IRQ_HANDLED; - } - - if (uart_tx_stopped(port) || uart_circ_empty(xmit)) { - ks8695uart_stop_tx(port); - return IRQ_HANDLED; - } - - count = 16; /* fifo size */ - while (!uart_circ_empty(xmit) && (count-- > 0)) { - KS8695_CLR_TX_INT(); - UART_PUT_CHAR(port, xmit->buf[xmit->tail]); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - ks8695uart_stop_tx(port); - - return IRQ_HANDLED; -} - -static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - unsigned int status; - - /* - * clear modem interrupt by reading MSR - */ - status = UART_GET_MSR(port); - - if (status & URMS_URDDCD) - uart_handle_dcd_change(port, status & URMS_URDDCD); - - if (status & URMS_URDDST) - port->icount.dsr++; - - if (status & URMS_URDCTS) - uart_handle_cts_change(port, status & URMS_URDCTS); - - if (status & URMS_URTERI) - port->icount.rng++; - - wake_up_interruptible(&port->state->port.delta_msr_wait); - - return IRQ_HANDLED; -} - -static unsigned int ks8695uart_tx_empty(struct uart_port *port) -{ - return (UART_GET_LSR(port) & URLS_URTE) ? TIOCSER_TEMT : 0; -} - -static unsigned int ks8695uart_get_mctrl(struct uart_port *port) -{ - unsigned int result = 0; - unsigned int status; - - status = UART_GET_MSR(port); - if (status & URMS_URDCD) - result |= TIOCM_CAR; - if (status & URMS_URDSR) - result |= TIOCM_DSR; - if (status & URMS_URCTS) - result |= TIOCM_CTS; - if (status & URMS_URRI) - result |= TIOCM_RI; - - return result; -} - -static void ks8695uart_set_mctrl(struct uart_port *port, u_int mctrl) -{ - unsigned int mcr; - - mcr = UART_GET_MCR(port); - if (mctrl & TIOCM_RTS) - mcr |= URMC_URRTS; - else - mcr &= ~URMC_URRTS; - - if (mctrl & TIOCM_DTR) - mcr |= URMC_URDTR; - else - mcr &= ~URMC_URDTR; - - UART_PUT_MCR(port, mcr); -} - -static void ks8695uart_break_ctl(struct uart_port *port, int break_state) -{ - unsigned int lcr; - - lcr = UART_GET_LCR(port); - - if (break_state == -1) - lcr |= URLC_URSBC; - else - lcr &= ~URLC_URSBC; - - UART_PUT_LCR(port, lcr); -} - -static int ks8695uart_startup(struct uart_port *port) -{ - int retval; - - set_irq_flags(KS8695_IRQ_UART_TX, IRQF_VALID | IRQF_NOAUTOEN); - tx_enable(port, 0); - rx_enable(port, 1); - ms_enable(port, 1); - - /* - * Allocate the IRQ - */ - retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, IRQF_DISABLED, "UART TX", port); - if (retval) - goto err_tx; - - retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, IRQF_DISABLED, "UART RX", port); - if (retval) - goto err_rx; - - retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, IRQF_DISABLED, "UART LineStatus", port); - if (retval) - goto err_ls; - - retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, IRQF_DISABLED, "UART ModemStatus", port); - if (retval) - goto err_ms; - - return 0; - -err_ms: - free_irq(KS8695_IRQ_UART_LINE_STATUS, port); -err_ls: - free_irq(KS8695_IRQ_UART_RX, port); -err_rx: - free_irq(KS8695_IRQ_UART_TX, port); -err_tx: - return retval; -} - -static void ks8695uart_shutdown(struct uart_port *port) -{ - /* - * Free the interrupt - */ - free_irq(KS8695_IRQ_UART_RX, port); - free_irq(KS8695_IRQ_UART_TX, port); - free_irq(KS8695_IRQ_UART_MODEM_STATUS, port); - free_irq(KS8695_IRQ_UART_LINE_STATUS, port); - - /* disable break condition and fifos */ - UART_PUT_LCR(port, UART_GET_LCR(port) & ~URLC_URSBC); - UART_PUT_FCR(port, UART_GET_FCR(port) & ~URFC_URFE); -} - -static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) -{ - unsigned int lcr, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - switch (termios->c_cflag & CSIZE) { - case CS5: - lcr = URCL_5; - break; - case CS6: - lcr = URCL_6; - break; - case CS7: - lcr = URCL_7; - break; - default: - lcr = URCL_8; - break; - } - - /* stop bits */ - if (termios->c_cflag & CSTOPB) - lcr |= URLC_URSB; - - /* parity */ - if (termios->c_cflag & PARENB) { - if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */ - if (termios->c_cflag & PARODD) - lcr |= URPE_MARK; - else - lcr |= URPE_SPACE; - } - else if (termios->c_cflag & PARODD) - lcr |= URPE_ODD; - else - lcr |= URPE_EVEN; - } - - if (port->fifosize > 1) - fcr = URFC_URFRT_8 | URFC_URTFR | URFC_URRFR | URFC_URFE; - - spin_lock_irqsave(&port->lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - port->read_status_mask = URLS_URROE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (URLS_URFE | URLS_URPE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= URLS_URBI; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= (URLS_URFE | URLS_URPE); - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= URLS_URBI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= URLS_URROE; - } - - /* - * Ignore all characters if CREAD is not set. - */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= UART_DUMMY_LSR_RX; - - /* first, disable everything */ - if (UART_ENABLE_MS(port, termios->c_cflag)) - ks8695uart_enable_ms(port); - else - ks8695uart_disable_ms(port); - - /* Set baud rate */ - UART_PUT_BRDR(port, quot); - - UART_PUT_LCR(port, lcr); - UART_PUT_FCR(port, fcr); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *ks8695uart_type(struct uart_port *port) -{ - return port->type == PORT_KS8695 ? "KS8695" : NULL; -} - -/* - * Release the memory region(s) being used by 'port' - */ -static void ks8695uart_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, UART_PORT_SIZE); -} - -/* - * Request the memory region(s) being used by 'port' - */ -static int ks8695uart_request_port(struct uart_port *port) -{ - return request_mem_region(port->mapbase, UART_PORT_SIZE, - "serial_ks8695") != NULL ? 0 : -EBUSY; -} - -/* - * Configure/autoconfigure the port. - */ -static void ks8695uart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_KS8695; - ks8695uart_request_port(port); - } -} - -/* - * verify the new serial_struct (for TIOCSSERIAL). - */ -static int ks8695uart_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_KS8695) - ret = -EINVAL; - if (ser->irq != port->irq) - ret = -EINVAL; - if (ser->baud_base < 9600) - ret = -EINVAL; - return ret; -} - -static struct uart_ops ks8695uart_pops = { - .tx_empty = ks8695uart_tx_empty, - .set_mctrl = ks8695uart_set_mctrl, - .get_mctrl = ks8695uart_get_mctrl, - .stop_tx = ks8695uart_stop_tx, - .start_tx = ks8695uart_start_tx, - .stop_rx = ks8695uart_stop_rx, - .enable_ms = ks8695uart_enable_ms, - .break_ctl = ks8695uart_break_ctl, - .startup = ks8695uart_startup, - .shutdown = ks8695uart_shutdown, - .set_termios = ks8695uart_set_termios, - .type = ks8695uart_type, - .release_port = ks8695uart_release_port, - .request_port = ks8695uart_request_port, - .config_port = ks8695uart_config_port, - .verify_port = ks8695uart_verify_port, -}; - -static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = { - { - .membase = (void *) KS8695_UART_VA, - .mapbase = KS8695_UART_VA, - .iotype = SERIAL_IO_MEM, - .irq = KS8695_IRQ_UART_TX, - .uartclk = KS8695_CLOCK_RATE * 16, - .fifosize = 16, - .ops = &ks8695uart_pops, - .flags = ASYNC_BOOT_AUTOCONF, - .line = 0, - } -}; - -#ifdef CONFIG_SERIAL_KS8695_CONSOLE -static void ks8695_console_putchar(struct uart_port *port, int ch) -{ - while (!(UART_GET_LSR(port) & URLS_URTHRE)) - barrier(); - - UART_PUT_CHAR(port, ch); -} - -static void ks8695_console_write(struct console *co, const char *s, u_int count) -{ - struct uart_port *port = ks8695uart_ports + co->index; - - uart_console_write(port, s, count, ks8695_console_putchar); -} - -static void __init ks8695_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) -{ - unsigned int lcr; - - lcr = UART_GET_LCR(port); - - switch (lcr & URLC_PARITY) { - case URPE_ODD: - *parity = 'o'; - break; - case URPE_EVEN: - *parity = 'e'; - break; - default: - *parity = 'n'; - } - - switch (lcr & URLC_URCL) { - case URCL_5: - *bits = 5; - break; - case URCL_6: - *bits = 6; - break; - case URCL_7: - *bits = 7; - break; - default: - *bits = 8; - } - - *baud = port->uartclk / (UART_GET_BRDR(port) & 0x0FFF); - *baud /= 16; - *baud &= 0xFFFFFFF0; -} - -static int __init ks8695_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 115200; - 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(ks8695uart_ports, SERIAL_KS8695_NR, co); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - else - ks8695_console_get_options(port, &baud, &parity, &bits); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver ks8695_reg; - -static struct console ks8695_console = { - .name = SERIAL_KS8695_DEVNAME, - .write = ks8695_console_write, - .device = uart_console_device, - .setup = ks8695_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &ks8695_reg, -}; - -static int __init ks8695_console_init(void) -{ - add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL); - register_console(&ks8695_console); - return 0; -} - -console_initcall(ks8695_console_init); - -#define KS8695_CONSOLE &ks8695_console -#else -#define KS8695_CONSOLE NULL -#endif - -static struct uart_driver ks8695_reg = { - .owner = THIS_MODULE, - .driver_name = "serial_ks8695", - .dev_name = SERIAL_KS8695_DEVNAME, - .major = SERIAL_KS8695_MAJOR, - .minor = SERIAL_KS8695_MINOR, - .nr = SERIAL_KS8695_NR, - .cons = KS8695_CONSOLE, -}; - -static int __init ks8695uart_init(void) -{ - int i, ret; - - printk(KERN_INFO "Serial: Micrel KS8695 UART driver\n"); - - ret = uart_register_driver(&ks8695_reg); - if (ret) - return ret; - - for (i = 0; i < SERIAL_KS8695_NR; i++) - uart_add_one_port(&ks8695_reg, &ks8695uart_ports[0]); - - return 0; -} - -static void __exit ks8695uart_exit(void) -{ - int i; - - for (i = 0; i < SERIAL_KS8695_NR; i++) - uart_remove_one_port(&ks8695_reg, &ks8695uart_ports[0]); - uart_unregister_driver(&ks8695_reg); -} - -module_init(ks8695uart_init); -module_exit(ks8695uart_exit); - -MODULE_DESCRIPTION("KS8695 serial port driver"); -MODULE_AUTHOR("Micrel Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c deleted file mode 100644 index ea744707c4d6..000000000000 --- a/drivers/serial/serial_lh7a40x.c +++ /dev/null @@ -1,682 +0,0 @@ -/* drivers/serial/serial_lh7a40x.c - * - * Copyright (C) 2004 Coastal Environmental Systems - * - * 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. - * - */ - -/* Driver for Sharp LH7A40X embedded serial ports - * - * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. - * Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd. - * - * --- - * - * This driver supports the embedded UARTs of the Sharp LH7A40X series - * CPUs. While similar to the 16550 and other UART chips, there is - * nothing close to register compatibility. Moreover, some of the - * modem control lines are not available, either in the chip or they - * are lacking in the board-level implementation. - * - * - Use of SIRDIS - * For simplicity, we disable the IR functions of any UART whenever - * we enable it. - * - */ - - -#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define DEV_MAJOR 204 -#define DEV_MINOR 16 -#define DEV_NR 3 - -#define ISR_LOOP_LIMIT 256 - -#define UR(p,o) _UR ((p)->membase, o) -#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o)))) -#define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m) -#define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m) - -#define UART_REG_SIZE 32 - -#define UART_R_DATA (0x00) -#define UART_R_FCON (0x04) -#define UART_R_BRCON (0x08) -#define UART_R_CON (0x0c) -#define UART_R_STATUS (0x10) -#define UART_R_RAWISR (0x14) -#define UART_R_INTEN (0x18) -#define UART_R_ISR (0x1c) - -#define UARTEN (0x01) /* UART enable */ -#define SIRDIS (0x02) /* Serial IR disable (UART1 only) */ - -#define RxEmpty (0x10) -#define TxEmpty (0x80) -#define TxFull (0x20) -#define nRxRdy RxEmpty -#define nTxRdy TxFull -#define TxBusy (0x08) - -#define RxBreak (0x0800) -#define RxOverrunError (0x0400) -#define RxParityError (0x0200) -#define RxFramingError (0x0100) -#define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError) - -#define DCD (0x04) -#define DSR (0x02) -#define CTS (0x01) - -#define RxInt (0x01) -#define TxInt (0x02) -#define ModemInt (0x04) -#define RxTimeoutInt (0x08) - -#define MSEOI (0x10) - -#define WLEN_8 (0x60) -#define WLEN_7 (0x40) -#define WLEN_6 (0x20) -#define WLEN_5 (0x00) -#define WLEN (0x60) /* Mask for all word-length bits */ -#define STP2 (0x08) -#define PEN (0x02) /* Parity Enable */ -#define EPS (0x04) /* Even Parity Set */ -#define FEN (0x10) /* FIFO Enable */ -#define BRK (0x01) /* Send Break */ - - -struct uart_port_lh7a40x { - struct uart_port port; - unsigned int statusPrev; /* Most recently read modem status */ -}; - -static void lh7a40xuart_stop_tx (struct uart_port* port) -{ - BIT_CLR (port, UART_R_INTEN, TxInt); -} - -static void lh7a40xuart_start_tx (struct uart_port* port) -{ - BIT_SET (port, UART_R_INTEN, TxInt); - - /* *** FIXME: do I need to check for startup of the - transmitter? The old driver did, but AMBA - doesn't . */ -} - -static void lh7a40xuart_stop_rx (struct uart_port* port) -{ - BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt); -} - -static void lh7a40xuart_enable_ms (struct uart_port* port) -{ - BIT_SET (port, UART_R_INTEN, ModemInt); -} - -static void lh7a40xuart_rx_chars (struct uart_port* port) -{ - struct tty_struct* tty = port->state->port.tty; - int cbRxMax = 256; /* (Gross) limit on receive */ - unsigned int data; /* Received data and status */ - unsigned int flag; - - while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) { - data = UR (port, UART_R_DATA); - flag = TTY_NORMAL; - ++port->icount.rx; - - if (unlikely(data & RxError)) { - if (data & RxBreak) { - data &= ~(RxFramingError | RxParityError); - ++port->icount.brk; - if (uart_handle_break (port)) - continue; - } - else if (data & RxParityError) - ++port->icount.parity; - else if (data & RxFramingError) - ++port->icount.frame; - if (data & RxOverrunError) - ++port->icount.overrun; - - /* Mask by termios, leave Rx'd byte */ - data &= port->read_status_mask | 0xff; - - if (data & RxBreak) - flag = TTY_BREAK; - else if (data & RxParityError) - flag = TTY_PARITY; - else if (data & RxFramingError) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char (port, (unsigned char) data)) - continue; - - uart_insert_char(port, data, RxOverrunError, data, flag); - } - tty_flip_buffer_push (tty); - return; -} - -static void lh7a40xuart_tx_chars (struct uart_port* port) -{ - struct circ_buf* xmit = &port->state->xmit; - int cbTxMax = port->fifosize; - - if (port->x_char) { - UR (port, UART_R_DATA) = port->x_char; - ++port->icount.tx; - port->x_char = 0; - return; - } - if (uart_circ_empty (xmit) || uart_tx_stopped (port)) { - lh7a40xuart_stop_tx (port); - return; - } - - /* Unlike the AMBA UART, the lh7a40x UART does not guarantee - that at least half of the FIFO is empty. Instead, we check - status for every character. Using the AMBA method causes - the transmitter to drop characters. */ - - do { - UR (port, UART_R_DATA) = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - ++port->icount.tx; - if (uart_circ_empty(xmit)) - break; - } while (!(UR (port, UART_R_STATUS) & nTxRdy) - && cbTxMax--); - - if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS) - uart_write_wakeup (port); - - if (uart_circ_empty (xmit)) - lh7a40xuart_stop_tx (port); -} - -static void lh7a40xuart_modem_status (struct uart_port* port) -{ - unsigned int status = UR (port, UART_R_STATUS); - unsigned int delta - = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev; - - BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */ - - if (!delta) /* Only happens if we missed 2 transitions */ - return; - - ((struct uart_port_lh7a40x*) port)->statusPrev = status; - - if (delta & DCD) - uart_handle_dcd_change (port, status & DCD); - - if (delta & DSR) - ++port->icount.dsr; - - if (delta & CTS) - uart_handle_cts_change (port, status & CTS); - - wake_up_interruptible (&port->state->port.delta_msr_wait); -} - -static irqreturn_t lh7a40xuart_int (int irq, void* dev_id) -{ - struct uart_port* port = dev_id; - unsigned int cLoopLimit = ISR_LOOP_LIMIT; - unsigned int isr = UR (port, UART_R_ISR); - - - do { - if (isr & (RxInt | RxTimeoutInt)) - lh7a40xuart_rx_chars(port); - if (isr & ModemInt) - lh7a40xuart_modem_status (port); - if (isr & TxInt) - lh7a40xuart_tx_chars (port); - - if (--cLoopLimit == 0) - break; - - isr = UR (port, UART_R_ISR); - } while (isr & (RxInt | TxInt | RxTimeoutInt)); - - return IRQ_HANDLED; -} - -static unsigned int lh7a40xuart_tx_empty (struct uart_port* port) -{ - return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0; -} - -static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port) -{ - unsigned int result = 0; - unsigned int status = UR (port, UART_R_STATUS); - - if (status & DCD) - result |= TIOCM_CAR; - if (status & DSR) - result |= TIOCM_DSR; - if (status & CTS) - result |= TIOCM_CTS; - - return result; -} - -static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl) -{ - /* None of the ports supports DTR. UART1 supports RTS through GPIO. */ - /* Note, kernel appears to be setting DTR and RTS on console. */ - - /* *** FIXME: this deserves more work. There's some work in - tracing all of the IO pins. */ -#if 0 - if( port->mapbase == UART1_PHYS) { - gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS); - - if (mctrl & TIOCM_RTS) - gpio->pbdr &= ~GPIOB_UART1_RTS; - else - gpio->pbdr |= GPIOB_UART1_RTS; - } -#endif -} - -static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state) -{ - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - if (break_state == -1) - BIT_SET (port, UART_R_FCON, BRK); /* Assert break */ - else - BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */ - spin_unlock_irqrestore(&port->lock, flags); -} - -static int lh7a40xuart_startup (struct uart_port* port) -{ - int retval; - - retval = request_irq (port->irq, lh7a40xuart_int, 0, - "serial_lh7a40x", port); - if (retval) - return retval; - - /* Initial modem control-line settings */ - ((struct uart_port_lh7a40x*) port)->statusPrev - = UR (port, UART_R_STATUS); - - /* There is presently no configuration option to enable IR. - Thus, we always disable it. */ - - BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); - BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt); - - return 0; -} - -static void lh7a40xuart_shutdown (struct uart_port* port) -{ - free_irq (port->irq, port); - BIT_CLR (port, UART_R_FCON, BRK | FEN); - BIT_CLR (port, UART_R_CON, UARTEN); -} - -static void lh7a40xuart_set_termios (struct uart_port* port, - struct ktermios* termios, - struct ktermios* old) -{ - unsigned int con; - unsigned int inten; - unsigned int fcon; - unsigned long flags; - unsigned int baud; - unsigned int quot; - - baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16); - quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */ - - switch (termios->c_cflag & CSIZE) { - case CS5: - fcon = WLEN_5; - break; - case CS6: - fcon = WLEN_6; - break; - case CS7: - fcon = WLEN_7; - break; - case CS8: - default: - fcon = WLEN_8; - break; - } - if (termios->c_cflag & CSTOPB) - fcon |= STP2; - if (termios->c_cflag & PARENB) { - fcon |= PEN; - if (!(termios->c_cflag & PARODD)) - fcon |= EPS; - } - if (port->fifosize > 1) - fcon |= FEN; - - spin_lock_irqsave (&port->lock, flags); - - uart_update_timeout (port, termios->c_cflag, baud); - - port->read_status_mask = RxOverrunError; - if (termios->c_iflag & INPCK) - port->read_status_mask |= RxFramingError | RxParityError; - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= RxBreak; - - /* Figure mask for status we ignore */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= RxFramingError | RxParityError; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= RxBreak; - /* Ignore overrun when ignorning parity */ - /* *** FIXME: is this in the right place? */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= RxOverrunError; - } - - /* Ignore all receive errors when receive disabled */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= RxError; - - con = UR (port, UART_R_CON); - inten = (UR (port, UART_R_INTEN) & ~ModemInt); - - if (UART_ENABLE_MS (port, termios->c_cflag)) - inten |= ModemInt; - - BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */ - UR (port, UART_R_INTEN) = 0; /* Disable interrupts */ - UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */ - UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */ - UR (port, UART_R_INTEN) = inten; /* Enable interrupts */ - UR (port, UART_R_CON) = con; /* Restore UART mode */ - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char* lh7a40xuart_type (struct uart_port* port) -{ - return port->type == PORT_LH7A40X ? "LH7A40X" : NULL; -} - -static void lh7a40xuart_release_port (struct uart_port* port) -{ - release_mem_region (port->mapbase, UART_REG_SIZE); -} - -static int lh7a40xuart_request_port (struct uart_port* port) -{ - return request_mem_region (port->mapbase, UART_REG_SIZE, - "serial_lh7a40x") != NULL - ? 0 : -EBUSY; -} - -static void lh7a40xuart_config_port (struct uart_port* port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_LH7A40X; - lh7a40xuart_request_port (port); - } -} - -static int lh7a40xuart_verify_port (struct uart_port* port, - struct serial_struct* ser) -{ - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X) - ret = -EINVAL; - if (ser->irq < 0 || ser->irq >= nr_irqs) - ret = -EINVAL; - if (ser->baud_base < 9600) /* *** FIXME: is this true? */ - ret = -EINVAL; - return ret; -} - -static struct uart_ops lh7a40x_uart_ops = { - .tx_empty = lh7a40xuart_tx_empty, - .set_mctrl = lh7a40xuart_set_mctrl, - .get_mctrl = lh7a40xuart_get_mctrl, - .stop_tx = lh7a40xuart_stop_tx, - .start_tx = lh7a40xuart_start_tx, - .stop_rx = lh7a40xuart_stop_rx, - .enable_ms = lh7a40xuart_enable_ms, - .break_ctl = lh7a40xuart_break_ctl, - .startup = lh7a40xuart_startup, - .shutdown = lh7a40xuart_shutdown, - .set_termios = lh7a40xuart_set_termios, - .type = lh7a40xuart_type, - .release_port = lh7a40xuart_release_port, - .request_port = lh7a40xuart_request_port, - .config_port = lh7a40xuart_config_port, - .verify_port = lh7a40xuart_verify_port, -}; - -static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = { - { - .port = { - .membase = (void*) io_p2v (UART1_PHYS), - .mapbase = UART1_PHYS, - .iotype = UPIO_MEM, - .irq = IRQ_UART1INTR, - .uartclk = 14745600/2, - .fifosize = 16, - .ops = &lh7a40x_uart_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 0, - }, - }, - { - .port = { - .membase = (void*) io_p2v (UART2_PHYS), - .mapbase = UART2_PHYS, - .iotype = UPIO_MEM, - .irq = IRQ_UART2INTR, - .uartclk = 14745600/2, - .fifosize = 16, - .ops = &lh7a40x_uart_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 1, - }, - }, - { - .port = { - .membase = (void*) io_p2v (UART3_PHYS), - .mapbase = UART3_PHYS, - .iotype = UPIO_MEM, - .irq = IRQ_UART3INTR, - .uartclk = 14745600/2, - .fifosize = 16, - .ops = &lh7a40x_uart_ops, - .flags = UPF_BOOT_AUTOCONF, - .line = 2, - }, - }, -}; - -#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE -# define LH7A40X_CONSOLE NULL -#else -# define LH7A40X_CONSOLE &lh7a40x_console - -static void lh7a40xuart_console_putchar(struct uart_port *port, int ch) -{ - while (UR(port, UART_R_STATUS) & nTxRdy) - ; - UR(port, UART_R_DATA) = ch; -} - -static void lh7a40xuart_console_write (struct console* co, - const char* s, - unsigned int count) -{ - struct uart_port* port = &lh7a40x_ports[co->index].port; - unsigned int con = UR (port, UART_R_CON); - unsigned int inten = UR (port, UART_R_INTEN); - - - UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */ - BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */ - - uart_console_write(port, s, count, lh7a40xuart_console_putchar); - - /* Wait until all characters are sent */ - while (UR (port, UART_R_STATUS) & TxBusy) - ; - - /* Restore control and interrupt mask */ - UR (port, UART_R_CON) = con; - UR (port, UART_R_INTEN) = inten; -} - -static void __init lh7a40xuart_console_get_options (struct uart_port* port, - int* baud, - int* parity, - int* bits) -{ - if (UR (port, UART_R_CON) & UARTEN) { - unsigned int fcon = UR (port, UART_R_FCON); - unsigned int quot = UR (port, UART_R_BRCON) + 1; - - switch (fcon & (PEN | EPS)) { - default: *parity = 'n'; break; - case PEN: *parity = 'o'; break; - case PEN | EPS: *parity = 'e'; break; - } - - switch (fcon & WLEN) { - default: - case WLEN_8: *bits = 8; break; - case WLEN_7: *bits = 7; break; - case WLEN_6: *bits = 6; break; - case WLEN_5: *bits = 5; break; - } - - *baud = port->uartclk/(16*quot); - } -} - -static int __init lh7a40xuart_console_setup (struct console* co, char* options) -{ - struct uart_port* port; - int baud = 38400; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index >= DEV_NR) /* Bounds check on device number */ - co->index = 0; - port = &lh7a40x_ports[co->index].port; - - if (options) - uart_parse_options (options, &baud, &parity, &bits, &flow); - else - lh7a40xuart_console_get_options (port, &baud, &parity, &bits); - - return uart_set_options (port, co, baud, parity, bits, flow); -} - -static struct uart_driver lh7a40x_reg; -static struct console lh7a40x_console = { - .name = "ttyAM", - .write = lh7a40xuart_console_write, - .device = uart_console_device, - .setup = lh7a40xuart_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &lh7a40x_reg, -}; - -static int __init lh7a40xuart_console_init(void) -{ - register_console (&lh7a40x_console); - return 0; -} - -console_initcall (lh7a40xuart_console_init); - -#endif - -static struct uart_driver lh7a40x_reg = { - .owner = THIS_MODULE, - .driver_name = "ttyAM", - .dev_name = "ttyAM", - .major = DEV_MAJOR, - .minor = DEV_MINOR, - .nr = DEV_NR, - .cons = LH7A40X_CONSOLE, -}; - -static int __init lh7a40xuart_init(void) -{ - int ret; - - printk (KERN_INFO "serial: LH7A40X serial driver\n"); - - ret = uart_register_driver (&lh7a40x_reg); - - if (ret == 0) { - int i; - - for (i = 0; i < DEV_NR; i++) { - /* UART3, when used, requires GPIO pin reallocation */ - if (lh7a40x_ports[i].port.mapbase == UART3_PHYS) - GPIO_PINMUX |= 1<<3; - uart_add_one_port (&lh7a40x_reg, - &lh7a40x_ports[i].port); - } - } - return ret; -} - -static void __exit lh7a40xuart_exit(void) -{ - int i; - - for (i = 0; i < DEV_NR; i++) - uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port); - - uart_unregister_driver (&lh7a40x_reg); -} - -module_init (lh7a40xuart_init); -module_exit (lh7a40xuart_exit); - -MODULE_AUTHOR ("Marc Singer"); -MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver"); -MODULE_LICENSE ("GPL"); diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c deleted file mode 100644 index c50e9fbbf743..000000000000 --- a/drivers/serial/serial_txx9.c +++ /dev/null @@ -1,1344 +0,0 @@ -/* - * drivers/serial/serial_txx9.c - * - * Derived from many drivers using generic_serial interface, - * especially serial_tx3912.c by Steven J. Hill and r39xx_serial.c - * (was in Linux/VR tree) by Jim Pick. - * - * Copyright (C) 1999 Harald Koerfgen - * Copyright (C) 2000 Jim Pick - * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) - * Copyright (C) 2000-2002 Toshiba Corporation - * - * 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. - * - * Serial driver for TX3927/TX4927/TX4925/TX4938 internal SIO controller - */ - -#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static char *serial_version = "1.11"; -static char *serial_name = "TX39/49 Serial driver"; - -#define PASS_LIMIT 256 - -#if !defined(CONFIG_SERIAL_TXX9_STDSERIAL) -/* "ttyS" is used for standard serial driver */ -#define TXX9_TTY_NAME "ttyTX" -#define TXX9_TTY_MINOR_START 196 -#define TXX9_TTY_MAJOR 204 -#else -/* acts like standard serial driver */ -#define TXX9_TTY_NAME "ttyS" -#define TXX9_TTY_MINOR_START 64 -#define TXX9_TTY_MAJOR TTY_MAJOR -#endif - -/* flag aliases */ -#define UPF_TXX9_HAVE_CTS_LINE UPF_BUGGY_UART -#define UPF_TXX9_USE_SCLK UPF_MAGIC_MULTIPLIER - -#ifdef CONFIG_PCI -/* support for Toshiba TC86C001 SIO */ -#define ENABLE_SERIAL_TXX9_PCI -#endif - -/* - * Number of serial ports - */ -#define UART_NR CONFIG_SERIAL_TXX9_NR_UARTS - -struct uart_txx9_port { - struct uart_port port; - /* No additional info for now */ -}; - -#define TXX9_REGION_SIZE 0x24 - -/* TXX9 Serial Registers */ -#define TXX9_SILCR 0x00 -#define TXX9_SIDICR 0x04 -#define TXX9_SIDISR 0x08 -#define TXX9_SICISR 0x0c -#define TXX9_SIFCR 0x10 -#define TXX9_SIFLCR 0x14 -#define TXX9_SIBGR 0x18 -#define TXX9_SITFIFO 0x1c -#define TXX9_SIRFIFO 0x20 - -/* SILCR : Line Control */ -#define TXX9_SILCR_SCS_MASK 0x00000060 -#define TXX9_SILCR_SCS_IMCLK 0x00000000 -#define TXX9_SILCR_SCS_IMCLK_BG 0x00000020 -#define TXX9_SILCR_SCS_SCLK 0x00000040 -#define TXX9_SILCR_SCS_SCLK_BG 0x00000060 -#define TXX9_SILCR_UEPS 0x00000010 -#define TXX9_SILCR_UPEN 0x00000008 -#define TXX9_SILCR_USBL_MASK 0x00000004 -#define TXX9_SILCR_USBL_1BIT 0x00000000 -#define TXX9_SILCR_USBL_2BIT 0x00000004 -#define TXX9_SILCR_UMODE_MASK 0x00000003 -#define TXX9_SILCR_UMODE_8BIT 0x00000000 -#define TXX9_SILCR_UMODE_7BIT 0x00000001 - -/* SIDICR : DMA/Int. Control */ -#define TXX9_SIDICR_TDE 0x00008000 -#define TXX9_SIDICR_RDE 0x00004000 -#define TXX9_SIDICR_TIE 0x00002000 -#define TXX9_SIDICR_RIE 0x00001000 -#define TXX9_SIDICR_SPIE 0x00000800 -#define TXX9_SIDICR_CTSAC 0x00000600 -#define TXX9_SIDICR_STIE_MASK 0x0000003f -#define TXX9_SIDICR_STIE_OERS 0x00000020 -#define TXX9_SIDICR_STIE_CTSS 0x00000010 -#define TXX9_SIDICR_STIE_RBRKD 0x00000008 -#define TXX9_SIDICR_STIE_TRDY 0x00000004 -#define TXX9_SIDICR_STIE_TXALS 0x00000002 -#define TXX9_SIDICR_STIE_UBRKD 0x00000001 - -/* SIDISR : DMA/Int. Status */ -#define TXX9_SIDISR_UBRK 0x00008000 -#define TXX9_SIDISR_UVALID 0x00004000 -#define TXX9_SIDISR_UFER 0x00002000 -#define TXX9_SIDISR_UPER 0x00001000 -#define TXX9_SIDISR_UOER 0x00000800 -#define TXX9_SIDISR_ERI 0x00000400 -#define TXX9_SIDISR_TOUT 0x00000200 -#define TXX9_SIDISR_TDIS 0x00000100 -#define TXX9_SIDISR_RDIS 0x00000080 -#define TXX9_SIDISR_STIS 0x00000040 -#define TXX9_SIDISR_RFDN_MASK 0x0000001f - -/* SICISR : Change Int. Status */ -#define TXX9_SICISR_OERS 0x00000020 -#define TXX9_SICISR_CTSS 0x00000010 -#define TXX9_SICISR_RBRKD 0x00000008 -#define TXX9_SICISR_TRDY 0x00000004 -#define TXX9_SICISR_TXALS 0x00000002 -#define TXX9_SICISR_UBRKD 0x00000001 - -/* SIFCR : FIFO Control */ -#define TXX9_SIFCR_SWRST 0x00008000 -#define TXX9_SIFCR_RDIL_MASK 0x00000180 -#define TXX9_SIFCR_RDIL_1 0x00000000 -#define TXX9_SIFCR_RDIL_4 0x00000080 -#define TXX9_SIFCR_RDIL_8 0x00000100 -#define TXX9_SIFCR_RDIL_12 0x00000180 -#define TXX9_SIFCR_RDIL_MAX 0x00000180 -#define TXX9_SIFCR_TDIL_MASK 0x00000018 -#define TXX9_SIFCR_TDIL_MASK 0x00000018 -#define TXX9_SIFCR_TDIL_1 0x00000000 -#define TXX9_SIFCR_TDIL_4 0x00000001 -#define TXX9_SIFCR_TDIL_8 0x00000010 -#define TXX9_SIFCR_TDIL_MAX 0x00000010 -#define TXX9_SIFCR_TFRST 0x00000004 -#define TXX9_SIFCR_RFRST 0x00000002 -#define TXX9_SIFCR_FRSTE 0x00000001 -#define TXX9_SIO_TX_FIFO 8 -#define TXX9_SIO_RX_FIFO 16 - -/* SIFLCR : Flow Control */ -#define TXX9_SIFLCR_RCS 0x00001000 -#define TXX9_SIFLCR_TES 0x00000800 -#define TXX9_SIFLCR_RTSSC 0x00000200 -#define TXX9_SIFLCR_RSDE 0x00000100 -#define TXX9_SIFLCR_TSDE 0x00000080 -#define TXX9_SIFLCR_RTSTL_MASK 0x0000001e -#define TXX9_SIFLCR_RTSTL_MAX 0x0000001e -#define TXX9_SIFLCR_TBRK 0x00000001 - -/* SIBGR : Baudrate Control */ -#define TXX9_SIBGR_BCLK_MASK 0x00000300 -#define TXX9_SIBGR_BCLK_T0 0x00000000 -#define TXX9_SIBGR_BCLK_T2 0x00000100 -#define TXX9_SIBGR_BCLK_T4 0x00000200 -#define TXX9_SIBGR_BCLK_T6 0x00000300 -#define TXX9_SIBGR_BRD_MASK 0x000000ff - -static inline unsigned int sio_in(struct uart_txx9_port *up, int offset) -{ - switch (up->port.iotype) { - default: - return __raw_readl(up->port.membase + offset); - case UPIO_PORT: - return inl(up->port.iobase + offset); - } -} - -static inline void -sio_out(struct uart_txx9_port *up, int offset, int value) -{ - switch (up->port.iotype) { - default: - __raw_writel(value, up->port.membase + offset); - break; - case UPIO_PORT: - outl(value, up->port.iobase + offset); - break; - } -} - -static inline void -sio_mask(struct uart_txx9_port *up, int offset, unsigned int value) -{ - sio_out(up, offset, sio_in(up, offset) & ~value); -} -static inline void -sio_set(struct uart_txx9_port *up, int offset, unsigned int value) -{ - sio_out(up, offset, sio_in(up, offset) | value); -} - -static inline void -sio_quot_set(struct uart_txx9_port *up, int quot) -{ - quot >>= 1; - if (quot < 256) - sio_out(up, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0); - else if (quot < (256 << 2)) - sio_out(up, TXX9_SIBGR, (quot >> 2) | TXX9_SIBGR_BCLK_T2); - else if (quot < (256 << 4)) - sio_out(up, TXX9_SIBGR, (quot >> 4) | TXX9_SIBGR_BCLK_T4); - else if (quot < (256 << 6)) - sio_out(up, TXX9_SIBGR, (quot >> 6) | TXX9_SIBGR_BCLK_T6); - else - sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6); -} - -static struct uart_txx9_port *to_uart_txx9_port(struct uart_port *port) -{ - return container_of(port, struct uart_txx9_port, port); -} - -static void serial_txx9_stop_tx(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); -} - -static void serial_txx9_start_tx(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); -} - -static void serial_txx9_stop_rx(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; -} - -static void serial_txx9_enable_ms(struct uart_port *port) -{ - /* TXX9-SIO can not control DTR... */ -} - -static void serial_txx9_initialize(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned int tmout = 10000; - - sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); - /* TX4925 BUG WORKAROUND. Accessing SIOC register - * immediately after soft reset causes bus error. */ - mmiowb(); - udelay(1); - while ((sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) && --tmout) - udelay(1); - /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ - sio_set(up, TXX9_SIFCR, - TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1); - /* initial settings */ - sio_out(up, TXX9_SILCR, - TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | - ((up->port.flags & UPF_TXX9_USE_SCLK) ? - TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); - sio_quot_set(up, uart_get_divisor(port, 9600)); - sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); - sio_out(up, TXX9_SIDICR, 0); -} - -static inline void -receive_chars(struct uart_txx9_port *up, unsigned int *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch; - unsigned int disr = *status; - int max_count = 256; - char flag; - unsigned int next_ignore_status_mask; - - do { - ch = sio_in(up, TXX9_SIRFIFO); - flag = TTY_NORMAL; - up->port.icount.rx++; - - /* mask out RFDN_MASK bit added by previous overrun */ - next_ignore_status_mask = - up->port.ignore_status_mask & ~TXX9_SIDISR_RFDN_MASK; - if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | - TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) { - /* - * For statistics only - */ - if (disr & TXX9_SIDISR_UBRK) { - disr &= ~(TXX9_SIDISR_UFER | TXX9_SIDISR_UPER); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (disr & TXX9_SIDISR_UPER) - up->port.icount.parity++; - else if (disr & TXX9_SIDISR_UFER) - up->port.icount.frame++; - if (disr & TXX9_SIDISR_UOER) { - up->port.icount.overrun++; - /* - * The receiver read buffer still hold - * a char which caused overrun. - * Ignore next char by adding RFDN_MASK - * to ignore_status_mask temporarily. - */ - next_ignore_status_mask |= - TXX9_SIDISR_RFDN_MASK; - } - - /* - * Mask off conditions which should be ingored. - */ - disr &= up->port.read_status_mask; - - if (disr & TXX9_SIDISR_UBRK) { - flag = TTY_BREAK; - } else if (disr & TXX9_SIDISR_UPER) - flag = TTY_PARITY; - else if (disr & TXX9_SIDISR_UFER) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - - uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag); - - ignore_char: - up->port.ignore_status_mask = next_ignore_status_mask; - disr = sio_in(up, TXX9_SIDISR); - } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); - spin_unlock(&up->port.lock); - tty_flip_buffer_push(tty); - spin_lock(&up->port.lock); - *status = disr; -} - -static inline void transmit_chars(struct uart_txx9_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - sio_out(up, TXX9_SITFIFO, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - serial_txx9_stop_tx(&up->port); - return; - } - - count = TXX9_SIO_TX_FIFO; - do { - sio_out(up, TXX9_SITFIFO, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) - serial_txx9_stop_tx(&up->port); -} - -static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) -{ - int pass_counter = 0; - struct uart_txx9_port *up = dev_id; - unsigned int status; - - while (1) { - spin_lock(&up->port.lock); - status = sio_in(up, TXX9_SIDISR); - if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE)) - status &= ~TXX9_SIDISR_TDIS; - if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | - TXX9_SIDISR_TOUT))) { - spin_unlock(&up->port.lock); - break; - } - - if (status & TXX9_SIDISR_RDIS) - receive_chars(up, &status); - if (status & TXX9_SIDISR_TDIS) - transmit_chars(up); - /* Clear TX/RX Int. Status */ - sio_mask(up, TXX9_SIDISR, - TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | - TXX9_SIDISR_TOUT); - spin_unlock(&up->port.lock); - - if (pass_counter++ > PASS_LIMIT) - break; - } - - return pass_counter ? IRQ_HANDLED : IRQ_NONE; -} - -static unsigned int serial_txx9_tx_empty(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int serial_txx9_get_mctrl(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned int ret; - - /* no modem control lines */ - ret = TIOCM_CAR | TIOCM_DSR; - ret |= (sio_in(up, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC) ? 0 : TIOCM_RTS; - ret |= (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS) ? 0 : TIOCM_CTS; - - return ret; -} - -static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - - if (mctrl & TIOCM_RTS) - sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); - else - sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); -} - -static void serial_txx9_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); - else - sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || (CONFIG_CONSOLE_POLL) -/* - * Wait for transmitter & holding register to empty - */ -static void wait_for_xmitr(struct uart_txx9_port *up) -{ - unsigned int tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - while (--tmout && - !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) - udelay(1); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) - udelay(1); - } -} -#endif - -#ifdef CONFIG_CONSOLE_POLL -/* - * Console polling routines for writing and reading from the uart while - * in an interrupt or debug context. - */ - -static int serial_txx9_get_poll_char(struct uart_port *port) -{ - unsigned int ier; - unsigned char c; - struct uart_txx9_port *up = to_uart_txx9_port(port); - - /* - * First save the IER then disable the interrupts - */ - ier = sio_in(up, TXX9_SIDICR); - sio_out(up, TXX9_SIDICR, 0); - - while (sio_in(up, TXX9_SIDISR) & TXX9_SIDISR_UVALID) - ; - - c = sio_in(up, TXX9_SIRFIFO); - - /* - * Finally, clear RX interrupt status - * and restore the IER - */ - sio_mask(up, TXX9_SIDISR, TXX9_SIDISR_RDIS); - sio_out(up, TXX9_SIDICR, ier); - return c; -} - - -static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) -{ - unsigned int ier; - struct uart_txx9_port *up = to_uart_txx9_port(port); - - /* - * First save the IER then disable the interrupts - */ - ier = sio_in(up, TXX9_SIDICR); - sio_out(up, TXX9_SIDICR, 0); - - wait_for_xmitr(up); - /* - * Send the character out. - * If a LF, also do CR... - */ - sio_out(up, TXX9_SITFIFO, c); - if (c == 10) { - wait_for_xmitr(up); - sio_out(up, TXX9_SITFIFO, 13); - } - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - sio_out(up, TXX9_SIDICR, ier); -} - -#endif /* CONFIG_CONSOLE_POLL */ - -static int serial_txx9_startup(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned long flags; - int retval; - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - sio_set(up, TXX9_SIFCR, - TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); - /* clear reset */ - sio_mask(up, TXX9_SIFCR, - TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); - sio_out(up, TXX9_SIDICR, 0); - - /* - * Clear the interrupt registers. - */ - sio_out(up, TXX9_SIDISR, 0); - - retval = request_irq(up->port.irq, serial_txx9_interrupt, - IRQF_SHARED, "serial_txx9", up); - if (retval) - return retval; - - /* - * Now, initialize the UART - */ - spin_lock_irqsave(&up->port.lock, flags); - serial_txx9_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* Enable RX/TX */ - sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); - - /* - * Finally, enable interrupts. - */ - sio_set(up, TXX9_SIDICR, TXX9_SIDICR_RIE); - - return 0; -} - -static void serial_txx9_shutdown(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned long flags; - - /* - * Disable interrupts from this port - */ - sio_out(up, TXX9_SIDICR, 0); /* disable all intrs */ - - spin_lock_irqsave(&up->port.lock, flags); - serial_txx9_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition - */ - sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); - -#ifdef CONFIG_SERIAL_TXX9_CONSOLE - if (up->port.cons && up->port.line == up->port.cons->index) { - free_irq(up->port.irq, up); - return; - } -#endif - /* reset FIFOs */ - sio_set(up, TXX9_SIFCR, - TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); - /* clear reset */ - sio_mask(up, TXX9_SIFCR, - TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); - - /* Disable RX/TX */ - sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); - - free_irq(up->port.irq, up); -} - -static void -serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - unsigned int cval, fcr = 0; - unsigned long flags; - unsigned int baud, quot; - - /* - * We don't support modem control lines. - */ - termios->c_cflag &= ~(HUPCL | CMSPAR); - termios->c_cflag |= CLOCAL; - - cval = sio_in(up, TXX9_SILCR); - /* byte size and parity */ - cval &= ~TXX9_SILCR_UMODE_MASK; - switch (termios->c_cflag & CSIZE) { - case CS7: - cval |= TXX9_SILCR_UMODE_7BIT; - break; - default: - case CS5: /* not supported */ - case CS6: /* not supported */ - case CS8: - cval |= TXX9_SILCR_UMODE_8BIT; - break; - } - - cval &= ~TXX9_SILCR_USBL_MASK; - if (termios->c_cflag & CSTOPB) - cval |= TXX9_SILCR_USBL_2BIT; - else - cval |= TXX9_SILCR_USBL_1BIT; - cval &= ~(TXX9_SILCR_UPEN | TXX9_SILCR_UEPS); - if (termios->c_cflag & PARENB) - cval |= TXX9_SILCR_UPEN; - if (!(termios->c_cflag & PARODD)) - cval |= TXX9_SILCR_UEPS; - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16/2); - quot = uart_get_divisor(port, baud); - - /* Set up FIFOs */ - /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ - fcr = TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1; - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, termios->c_cflag, baud); - - up->port.read_status_mask = TXX9_SIDISR_UOER | - TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS; - if (termios->c_iflag & INPCK) - up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER; - if (termios->c_iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= TXX9_SIDISR_UBRK; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= TXX9_SIDISR_UPER | TXX9_SIDISR_UFER; - if (termios->c_iflag & IGNBRK) { - up->port.ignore_status_mask |= TXX9_SIDISR_UBRK; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - up->port.ignore_status_mask |= TXX9_SIDISR_UOER; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - up->port.ignore_status_mask |= TXX9_SIDISR_RDIS; - - /* CTS flow control flag */ - if ((termios->c_cflag & CRTSCTS) && - (up->port.flags & UPF_TXX9_HAVE_CTS_LINE)) { - sio_set(up, TXX9_SIFLCR, - TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES); - } else { - sio_mask(up, TXX9_SIFLCR, - TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES); - } - - sio_out(up, TXX9_SILCR, cval); - sio_quot_set(up, quot); - sio_out(up, TXX9_SIFCR, fcr); - - serial_txx9_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void -serial_txx9_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) -{ - /* - * If oldstate was -1 this is called from - * uart_configure_port(). In this case do not initialize the - * port now, because the port was already initialized (for - * non-console port) or should not be initialized here (for - * console port). If we initialized the port here we lose - * serial console settings. - */ - if (state == 0 && oldstate != -1) - serial_txx9_initialize(port); -} - -static int serial_txx9_request_resource(struct uart_txx9_port *up) -{ - unsigned int size = TXX9_REGION_SIZE; - int ret = 0; - - switch (up->port.iotype) { - default: - if (!up->port.mapbase) - break; - - if (!request_mem_region(up->port.mapbase, size, "serial_txx9")) { - ret = -EBUSY; - break; - } - - if (up->port.flags & UPF_IOREMAP) { - up->port.membase = ioremap(up->port.mapbase, size); - if (!up->port.membase) { - release_mem_region(up->port.mapbase, size); - ret = -ENOMEM; - } - } - break; - - case UPIO_PORT: - if (!request_region(up->port.iobase, size, "serial_txx9")) - ret = -EBUSY; - break; - } - return ret; -} - -static void serial_txx9_release_resource(struct uart_txx9_port *up) -{ - unsigned int size = TXX9_REGION_SIZE; - - switch (up->port.iotype) { - default: - if (!up->port.mapbase) - break; - - if (up->port.flags & UPF_IOREMAP) { - iounmap(up->port.membase); - up->port.membase = NULL; - } - - release_mem_region(up->port.mapbase, size); - break; - - case UPIO_PORT: - release_region(up->port.iobase, size); - break; - } -} - -static void serial_txx9_release_port(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - serial_txx9_release_resource(up); -} - -static int serial_txx9_request_port(struct uart_port *port) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - return serial_txx9_request_resource(up); -} - -static void serial_txx9_config_port(struct uart_port *port, int uflags) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - int ret; - - /* - * Find the region that we can probe for. This in turn - * tells us whether we can probe for the type of port. - */ - ret = serial_txx9_request_resource(up); - if (ret < 0) - return; - port->type = PORT_TXX9; - up->port.fifosize = TXX9_SIO_TX_FIFO; - -#ifdef CONFIG_SERIAL_TXX9_CONSOLE - if (up->port.line == up->port.cons->index) - return; -#endif - serial_txx9_initialize(port); -} - -static const char * -serial_txx9_type(struct uart_port *port) -{ - return "txx9"; -} - -static struct uart_ops serial_txx9_pops = { - .tx_empty = serial_txx9_tx_empty, - .set_mctrl = serial_txx9_set_mctrl, - .get_mctrl = serial_txx9_get_mctrl, - .stop_tx = serial_txx9_stop_tx, - .start_tx = serial_txx9_start_tx, - .stop_rx = serial_txx9_stop_rx, - .enable_ms = serial_txx9_enable_ms, - .break_ctl = serial_txx9_break_ctl, - .startup = serial_txx9_startup, - .shutdown = serial_txx9_shutdown, - .set_termios = serial_txx9_set_termios, - .pm = serial_txx9_pm, - .type = serial_txx9_type, - .release_port = serial_txx9_release_port, - .request_port = serial_txx9_request_port, - .config_port = serial_txx9_config_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = serial_txx9_get_poll_char, - .poll_put_char = serial_txx9_put_poll_char, -#endif -}; - -static struct uart_txx9_port serial_txx9_ports[UART_NR]; - -static void __init serial_txx9_register_ports(struct uart_driver *drv, - struct device *dev) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_txx9_port *up = &serial_txx9_ports[i]; - - up->port.line = i; - up->port.ops = &serial_txx9_pops; - up->port.dev = dev; - if (up->port.iobase || up->port.mapbase) - uart_add_one_port(drv, &up->port); - } -} - -#ifdef CONFIG_SERIAL_TXX9_CONSOLE - -static void serial_txx9_console_putchar(struct uart_port *port, int ch) -{ - struct uart_txx9_port *up = to_uart_txx9_port(port); - - wait_for_xmitr(up); - sio_out(up, TXX9_SITFIFO, ch); -} - -/* - * 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. - */ -static void -serial_txx9_console_write(struct console *co, const char *s, unsigned int count) -{ - struct uart_txx9_port *up = &serial_txx9_ports[co->index]; - unsigned int ier, flcr; - - /* - * First save the UER then disable the interrupts - */ - ier = sio_in(up, TXX9_SIDICR); - sio_out(up, TXX9_SIDICR, 0); - /* - * Disable flow-control if enabled (and unnecessary) - */ - flcr = sio_in(up, TXX9_SIFLCR); - if (!(up->port.flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES)) - sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES); - - uart_console_write(&up->port, s, count, serial_txx9_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - sio_out(up, TXX9_SIFLCR, flcr); - sio_out(up, TXX9_SIDICR, ier); -} - -static int __init serial_txx9_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - struct uart_txx9_port *up; - int baud = 9600; - 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. - */ - if (co->index >= UART_NR) - co->index = 0; - up = &serial_txx9_ports[co->index]; - port = &up->port; - if (!port->ops) - return -ENODEV; - - serial_txx9_initialize(&up->port); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver serial_txx9_reg; -static struct console serial_txx9_console = { - .name = TXX9_TTY_NAME, - .write = serial_txx9_console_write, - .device = uart_console_device, - .setup = serial_txx9_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &serial_txx9_reg, -}; - -static int __init serial_txx9_console_init(void) -{ - register_console(&serial_txx9_console); - return 0; -} -console_initcall(serial_txx9_console_init); - -#define SERIAL_TXX9_CONSOLE &serial_txx9_console -#else -#define SERIAL_TXX9_CONSOLE NULL -#endif - -static struct uart_driver serial_txx9_reg = { - .owner = THIS_MODULE, - .driver_name = "serial_txx9", - .dev_name = TXX9_TTY_NAME, - .major = TXX9_TTY_MAJOR, - .minor = TXX9_TTY_MINOR_START, - .nr = UART_NR, - .cons = SERIAL_TXX9_CONSOLE, -}; - -int __init early_serial_txx9_setup(struct uart_port *port) -{ - if (port->line >= ARRAY_SIZE(serial_txx9_ports)) - return -ENODEV; - - serial_txx9_ports[port->line].port = *port; - serial_txx9_ports[port->line].port.ops = &serial_txx9_pops; - serial_txx9_ports[port->line].port.flags |= - UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; - return 0; -} - -static DEFINE_MUTEX(serial_txx9_mutex); - -/** - * serial_txx9_register_port - register a serial port - * @port: serial port template - * - * Configure the serial port specified by the request. - * - * The port is then probed and if necessary the IRQ is autodetected - * If this fails an error is returned. - * - * 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) -{ - int i; - struct uart_txx9_port *uart; - int ret = -ENOSPC; - - mutex_lock(&serial_txx9_mutex); - for (i = 0; i < UART_NR; i++) { - uart = &serial_txx9_ports[i]; - if (uart_match_port(&uart->port, port)) { - uart_remove_one_port(&serial_txx9_reg, &uart->port); - break; - } - } - if (i == UART_NR) { - /* Find unused port */ - for (i = 0; i < UART_NR; i++) { - uart = &serial_txx9_ports[i]; - if (!(uart->port.iobase || uart->port.mapbase)) - break; - } - } - if (i < UART_NR) { - uart->port.iobase = port->iobase; - uart->port.membase = port->membase; - uart->port.irq = port->irq; - uart->port.uartclk = port->uartclk; - uart->port.iotype = port->iotype; - uart->port.flags = port->flags - | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; - uart->port.mapbase = port->mapbase; - if (port->dev) - uart->port.dev = port->dev; - ret = uart_add_one_port(&serial_txx9_reg, &uart->port); - if (ret == 0) - ret = uart->port.line; - } - mutex_unlock(&serial_txx9_mutex); - return ret; -} - -/** - * serial_txx9_unregister_port - remove a txx9 serial port at runtime - * @line: serial line number - * - * 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) -{ - struct uart_txx9_port *uart = &serial_txx9_ports[line]; - - mutex_lock(&serial_txx9_mutex); - uart_remove_one_port(&serial_txx9_reg, &uart->port); - uart->port.flags = 0; - uart->port.type = PORT_UNKNOWN; - uart->port.iobase = 0; - uart->port.mapbase = 0; - uart->port.membase = NULL; - uart->port.dev = NULL; - mutex_unlock(&serial_txx9_mutex); -} - -/* - * Register a set of serial devices attached to a platform device. - */ -static int __devinit serial_txx9_probe(struct platform_device *dev) -{ - struct uart_port *p = dev->dev.platform_data; - struct uart_port port; - int ret, i; - - memset(&port, 0, sizeof(struct uart_port)); - for (i = 0; p && p->uartclk != 0; p++, i++) { - port.iobase = p->iobase; - port.membase = p->membase; - port.irq = p->irq; - port.uartclk = p->uartclk; - port.iotype = p->iotype; - port.flags = p->flags; - port.mapbase = p->mapbase; - port.dev = &dev->dev; - ret = serial_txx9_register_port(&port); - if (ret < 0) { - dev_err(&dev->dev, "unable to register port at index %d " - "(IO%lx MEM%llx IRQ%d): %d\n", i, - p->iobase, (unsigned long long)p->mapbase, - p->irq, ret); - } - } - return 0; -} - -/* - * Remove serial ports registered against a platform device. - */ -static int __devexit serial_txx9_remove(struct platform_device *dev) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_txx9_port *up = &serial_txx9_ports[i]; - - if (up->port.dev == &dev->dev) - serial_txx9_unregister_port(i); - } - return 0; -} - -#ifdef CONFIG_PM -static int serial_txx9_suspend(struct platform_device *dev, pm_message_t state) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_txx9_port *up = &serial_txx9_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - uart_suspend_port(&serial_txx9_reg, &up->port); - } - - return 0; -} - -static int serial_txx9_resume(struct platform_device *dev) -{ - int i; - - for (i = 0; i < UART_NR; i++) { - struct uart_txx9_port *up = &serial_txx9_ports[i]; - - if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) - uart_resume_port(&serial_txx9_reg, &up->port); - } - - return 0; -} -#endif - -static struct platform_driver serial_txx9_plat_driver = { - .probe = serial_txx9_probe, - .remove = __devexit_p(serial_txx9_remove), -#ifdef CONFIG_PM - .suspend = serial_txx9_suspend, - .resume = serial_txx9_resume, -#endif - .driver = { - .name = "serial_txx9", - .owner = THIS_MODULE, - }, -}; - -#ifdef ENABLE_SERIAL_TXX9_PCI -/* - * Probe one serial board. Unfortunately, there is no rhyme nor reason - * to the arrangement of serial ports on a PCI card. - */ -static int __devinit -pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) -{ - struct uart_port port; - int line; - int rc; - - rc = pci_enable_device(dev); - if (rc) - return rc; - - memset(&port, 0, sizeof(port)); - port.ops = &serial_txx9_pops; - port.flags |= UPF_TXX9_HAVE_CTS_LINE; - port.uartclk = 66670000; - port.irq = dev->irq; - port.iotype = UPIO_PORT; - port.iobase = pci_resource_start(dev, 1); - port.dev = &dev->dev; - line = serial_txx9_register_port(&port); - if (line < 0) { - printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line); - pci_disable_device(dev); - return line; - } - pci_set_drvdata(dev, &serial_txx9_ports[line]); - - return 0; -} - -static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) -{ - struct uart_txx9_port *up = pci_get_drvdata(dev); - - pci_set_drvdata(dev, NULL); - - if (up) { - serial_txx9_unregister_port(up->port.line); - pci_disable_device(dev); - } -} - -#ifdef CONFIG_PM -static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state) -{ - struct uart_txx9_port *up = pci_get_drvdata(dev); - - if (up) - uart_suspend_port(&serial_txx9_reg, &up->port); - pci_save_state(dev); - pci_set_power_state(dev, pci_choose_state(dev, state)); - return 0; -} - -static int pciserial_txx9_resume_one(struct pci_dev *dev) -{ - struct uart_txx9_port *up = pci_get_drvdata(dev); - - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - if (up) - uart_resume_port(&serial_txx9_reg, &up->port); - return 0; -} -#endif - -static const struct pci_device_id serial_txx9_pci_tbl[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC) }, - { 0, } -}; - -static struct pci_driver serial_txx9_pci_driver = { - .name = "serial_txx9", - .probe = pciserial_txx9_init_one, - .remove = __devexit_p(pciserial_txx9_remove_one), -#ifdef CONFIG_PM - .suspend = pciserial_txx9_suspend_one, - .resume = pciserial_txx9_resume_one, -#endif - .id_table = serial_txx9_pci_tbl, -}; - -MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); -#endif /* ENABLE_SERIAL_TXX9_PCI */ - -static struct platform_device *serial_txx9_plat_devs; - -static int __init serial_txx9_init(void) -{ - int ret; - - printk(KERN_INFO "%s version %s\n", serial_name, serial_version); - - ret = uart_register_driver(&serial_txx9_reg); - if (ret) - goto out; - - serial_txx9_plat_devs = platform_device_alloc("serial_txx9", -1); - if (!serial_txx9_plat_devs) { - ret = -ENOMEM; - goto unreg_uart_drv; - } - - ret = platform_device_add(serial_txx9_plat_devs); - if (ret) - goto put_dev; - - serial_txx9_register_ports(&serial_txx9_reg, - &serial_txx9_plat_devs->dev); - - ret = platform_driver_register(&serial_txx9_plat_driver); - if (ret) - goto del_dev; - -#ifdef ENABLE_SERIAL_TXX9_PCI - ret = pci_register_driver(&serial_txx9_pci_driver); -#endif - if (ret == 0) - goto out; - - del_dev: - platform_device_del(serial_txx9_plat_devs); - put_dev: - platform_device_put(serial_txx9_plat_devs); - unreg_uart_drv: - uart_unregister_driver(&serial_txx9_reg); - out: - return ret; -} - -static void __exit serial_txx9_exit(void) -{ - int i; - -#ifdef ENABLE_SERIAL_TXX9_PCI - pci_unregister_driver(&serial_txx9_pci_driver); -#endif - platform_driver_unregister(&serial_txx9_plat_driver); - platform_device_unregister(serial_txx9_plat_devs); - for (i = 0; i < UART_NR; i++) { - struct uart_txx9_port *up = &serial_txx9_ports[i]; - if (up->port.iobase || up->port.mapbase) - uart_remove_one_port(&serial_txx9_reg, &up->port); - } - - uart_unregister_driver(&serial_txx9_reg); -} - -module_init(serial_txx9_init); -module_exit(serial_txx9_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("TX39/49 serial driver"); - -MODULE_ALIAS_CHARDEV_MAJOR(TXX9_TTY_MAJOR); diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c deleted file mode 100644 index c291b3add1d2..000000000000 --- a/drivers/serial/sh-sci.c +++ /dev/null @@ -1,2027 +0,0 @@ -/* - * drivers/serial/sh-sci.c - * - * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) - * - * Copyright (C) 2002 - 2008 Paul Mundt - * Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). - * - * based off of the old drivers/char/sh-sci.c by: - * - * Copyright (C) 1999, 2000 Niibe Yutaka - * Copyright (C) 2000 Sugioka Toshinobu - * Modified to support multiple serial ports. Stuart Menefy (May 2000). - * Modified to support SecureEdge. David McCullough (2002) - * Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003). - * Removed SH7300 support (Jul 2007). - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#undef DEBUG - -#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 -#include - -#ifdef CONFIG_SUPERH -#include -#endif - -#ifdef CONFIG_H8300 -#include -#endif - -#include "sh-sci.h" - -struct sci_port { - struct uart_port port; - - /* Port type */ - unsigned int type; - - /* Port IRQs: ERI, RXI, TXI, BRI (optional) */ - unsigned int irqs[SCIx_NR_IRQS]; - - /* Port enable callback */ - void (*enable)(struct uart_port *port); - - /* Port disable callback */ - void (*disable)(struct uart_port *port); - - /* Break timer */ - struct timer_list break_timer; - int break_flag; - - /* Interface clock */ - struct clk *iclk; - /* Function clock */ - struct clk *fclk; - - struct list_head node; - struct dma_chan *chan_tx; - struct dma_chan *chan_rx; -#ifdef CONFIG_SERIAL_SH_SCI_DMA - struct device *dma_dev; - unsigned int slave_tx; - unsigned int slave_rx; - struct dma_async_tx_descriptor *desc_tx; - struct dma_async_tx_descriptor *desc_rx[2]; - dma_cookie_t cookie_tx; - dma_cookie_t cookie_rx[2]; - dma_cookie_t active_rx; - struct scatterlist sg_tx; - unsigned int sg_len_tx; - struct scatterlist sg_rx[2]; - size_t buf_len_rx; - struct sh_dmae_slave param_tx; - struct sh_dmae_slave param_rx; - struct work_struct work_tx; - struct work_struct work_rx; - struct timer_list rx_timer; - unsigned int rx_timeout; -#endif -}; - -struct sh_sci_priv { - spinlock_t lock; - struct list_head ports; - struct notifier_block clk_nb; -}; - -/* Function prototypes */ -static void sci_stop_tx(struct uart_port *port); - -#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS - -static struct sci_port sci_ports[SCI_NPORTS]; -static struct uart_driver sci_uart_driver; - -static inline struct sci_port * -to_sci_port(struct uart_port *uart) -{ - return container_of(uart, struct sci_port, port); -} - -#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) - -#ifdef CONFIG_CONSOLE_POLL -static inline void handle_error(struct uart_port *port) -{ - /* Clear error flags */ - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); -} - -static int sci_poll_get_char(struct uart_port *port) -{ - unsigned short status; - int c; - - do { - status = sci_in(port, SCxSR); - if (status & SCxSR_ERRORS(port)) { - handle_error(port); - continue; - } - break; - } while (1); - - if (!(status & SCxSR_RDxF(port))) - return NO_POLL_CHAR; - - c = sci_in(port, SCxRDR); - - /* Dummy read */ - sci_in(port, SCxSR); - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - - return c; -} -#endif - -static void sci_poll_put_char(struct uart_port *port, unsigned char c) -{ - unsigned short status; - - do { - status = sci_in(port, SCxSR); - } while (!(status & SCxSR_TDxE(port))); - - sci_out(port, SCxTDR, c); - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); -} -#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ - -#if defined(__H8300H__) || defined(__H8300S__) -static void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - int ch = (port->mapbase - SMR0) >> 3; - - /* set DDR regs */ - H8300_GPIO_DDR(h8300_sci_pins[ch].port, - h8300_sci_pins[ch].rx, - H8300_GPIO_INPUT); - H8300_GPIO_DDR(h8300_sci_pins[ch].port, - h8300_sci_pins[ch].tx, - H8300_GPIO_OUTPUT); - - /* tx mark output*/ - H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (port->mapbase == 0xA4400000) { - __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); - __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); - } else if (port->mapbase == 0xA4410000) - __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - - if (cflag & CRTSCTS) { - /* enable RTS/CTS */ - if (port->mapbase == 0xa4430000) { /* SCIF0 */ - /* Clear PTCR bit 9-2; enable all scif pins but sck */ - data = __raw_readw(PORT_PTCR); - __raw_writew((data & 0xfc03), PORT_PTCR); - } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ - /* Clear PVCR bit 9-2 */ - data = __raw_readw(PORT_PVCR); - __raw_writew((data & 0xfc03), PORT_PVCR); - } - } else { - if (port->mapbase == 0xa4430000) { /* SCIF0 */ - /* Clear PTCR bit 5-2; enable only tx and rx */ - data = __raw_readw(PORT_PTCR); - __raw_writew((data & 0xffc3), PORT_PTCR); - } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ - /* Clear PVCR bit 5-2 */ - data = __raw_readw(PORT_PVCR); - __raw_writew((data & 0xffc3), PORT_PVCR); - } - } -} -#elif defined(CONFIG_CPU_SH3) -/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - - /* We need to set SCPCR to enable RTS/CTS */ - data = __raw_readw(SCPCR); - /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ - __raw_writew(data & 0x0fcf, SCPCR); - - if (!(cflag & CRTSCTS)) { - /* We need to set SCPCR to enable RTS/CTS */ - data = __raw_readw(SCPCR); - /* Clear out SCP7MD1,0, SCP4MD1,0, - Set SCP6MD1,0 = {01} (output) */ - __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); - - data = __raw_readb(SCPDR); - /* Set /RTS2 (bit6) = 0 */ - __raw_writeb(data & 0xbf, SCPDR); - } -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - unsigned short data; - - if (port->mapbase == 0xffe00000) { - data = __raw_readw(PSCR); - data &= ~0x03cf; - if (!(cflag & CRTSCTS)) - data |= 0x0340; - - __raw_writew(data, PSCR); - } -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) || \ - defined(CONFIG_CPU_SUBTYPE_SHX3) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (!(cflag & CRTSCTS)) - __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */ -} -#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A) -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - if (!(cflag & CRTSCTS)) - __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */ -} -#else -static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) -{ - /* Nothing to do */ -} -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -static int scif_txfill(struct uart_port *port) -{ - return sci_in(port, SCTFDR) & 0xff; -} - -static int scif_txroom(struct uart_port *port) -{ - return SCIF_TXROOM_MAX - scif_txfill(port); -} - -static int scif_rxfill(struct uart_port *port) -{ - return sci_in(port, SCRFDR) & 0xff; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -static int scif_txfill(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000 || - port->mapbase == 0xffe08000) - /* SCIF0/1*/ - return sci_in(port, SCTFDR) & 0xff; - else - /* SCIF2 */ - return sci_in(port, SCFDR) >> 8; -} - -static int scif_txroom(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000 || - port->mapbase == 0xffe08000) - /* SCIF0/1*/ - return SCIF_TXROOM_MAX - scif_txfill(port); - else - /* SCIF2 */ - return SCIF2_TXROOM_MAX - scif_txfill(port); -} - -static int scif_rxfill(struct uart_port *port) -{ - if ((port->mapbase == 0xffe00000) || - (port->mapbase == 0xffe08000)) { - /* SCIF0/1*/ - return sci_in(port, SCRFDR) & 0xff; - } else { - /* SCIF2 */ - return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; - } -} -#elif defined(CONFIG_ARCH_SH7372) -static int scif_txfill(struct uart_port *port) -{ - if (port->type == PORT_SCIFA) - return sci_in(port, SCFDR) >> 8; - else - return sci_in(port, SCTFDR); -} - -static int scif_txroom(struct uart_port *port) -{ - return port->fifosize - scif_txfill(port); -} - -static int scif_rxfill(struct uart_port *port) -{ - if (port->type == PORT_SCIFA) - return sci_in(port, SCFDR) & SCIF_RFDC_MASK; - else - return sci_in(port, SCRFDR); -} -#else -static int scif_txfill(struct uart_port *port) -{ - return sci_in(port, SCFDR) >> 8; -} - -static int scif_txroom(struct uart_port *port) -{ - return SCIF_TXROOM_MAX - scif_txfill(port); -} - -static int scif_rxfill(struct uart_port *port) -{ - return sci_in(port, SCFDR) & SCIF_RFDC_MASK; -} -#endif - -static int sci_txfill(struct uart_port *port) -{ - return !(sci_in(port, SCxSR) & SCI_TDRE); -} - -static int sci_txroom(struct uart_port *port) -{ - return !sci_txfill(port); -} - -static int sci_rxfill(struct uart_port *port) -{ - return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; -} - -/* ********************************************************************** * - * the interrupt related routines * - * ********************************************************************** */ - -static void sci_transmit_chars(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - unsigned int stopped = uart_tx_stopped(port); - unsigned short status; - unsigned short ctrl; - int count; - - status = sci_in(port, SCxSR); - if (!(status & SCxSR_TDxE(port))) { - ctrl = sci_in(port, SCSCR); - if (uart_circ_empty(xmit)) - ctrl &= ~SCI_CTRL_FLAGS_TIE; - else - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); - return; - } - - if (port->type == PORT_SCI) - count = sci_txroom(port); - else - count = scif_txroom(port); - - do { - unsigned char c; - - if (port->x_char) { - c = port->x_char; - port->x_char = 0; - } else if (!uart_circ_empty(xmit) && !stopped) { - c = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - } else { - break; - } - - sci_out(port, SCxTDR, c); - - port->icount.tx++; - } while (--count > 0); - - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - if (uart_circ_empty(xmit)) { - sci_stop_tx(port); - } else { - ctrl = sci_in(port, SCSCR); - - if (port->type != PORT_SCI) { - sci_in(port, SCxSR); /* Dummy read */ - sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); - } - - ctrl |= SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); - } -} - -/* On SH3, SCIF may read end-of-break as a space->mark char */ -#define STEPFN(c) ({int __c = (c); (((__c-1)|(__c)) == -1); }) - -static inline void sci_receive_chars(struct uart_port *port) -{ - struct sci_port *sci_port = to_sci_port(port); - struct tty_struct *tty = port->state->port.tty; - int i, count, copied = 0; - unsigned short status; - unsigned char flag; - - status = sci_in(port, SCxSR); - if (!(status & SCxSR_RDxF(port))) - return; - - while (1) { - if (port->type == PORT_SCI) - count = sci_rxfill(port); - else - count = scif_rxfill(port); - - /* Don't copy more bytes than there is room for in the buffer */ - count = tty_buffer_request_room(tty, count); - - /* If for any reason we can't copy more data, we're done! */ - if (count == 0) - break; - - if (port->type == PORT_SCI) { - char c = sci_in(port, SCxRDR); - if (uart_handle_sysrq_char(port, c) || - sci_port->break_flag) - count = 0; - else - tty_insert_flip_char(tty, c, TTY_NORMAL); - } else { - for (i = 0; i < count; i++) { - char c = sci_in(port, SCxRDR); - status = sci_in(port, SCxSR); -#if defined(CONFIG_CPU_SH3) - /* Skip "chars" during break */ - if (sci_port->break_flag) { - if ((c == 0) && - (status & SCxSR_FER(port))) { - count--; i--; - continue; - } - - /* Nonzero => end-of-break */ - dev_dbg(port->dev, "debounce<%02x>\n", c); - sci_port->break_flag = 0; - - if (STEPFN(c)) { - count--; i--; - continue; - } - } -#endif /* CONFIG_CPU_SH3 */ - if (uart_handle_sysrq_char(port, c)) { - count--; i--; - continue; - } - - /* Store data and status */ - if (status & SCxSR_FER(port)) { - flag = TTY_FRAME; - dev_notice(port->dev, "frame error\n"); - } else if (status & SCxSR_PER(port)) { - flag = TTY_PARITY; - dev_notice(port->dev, "parity error\n"); - } else - flag = TTY_NORMAL; - - tty_insert_flip_char(tty, c, flag); - } - } - - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - - copied += count; - port->icount.rx += count; - } - - if (copied) { - /* Tell the rest of the system the news. New characters! */ - tty_flip_buffer_push(tty); - } else { - sci_in(port, SCxSR); /* dummy read */ - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - } -} - -#define SCI_BREAK_JIFFIES (HZ/20) -/* The sci generates interrupts during the break, - * 1 per millisecond or so during the break period, for 9600 baud. - * So dont bother disabling interrupts. - * But dont want more than 1 break event. - * Use a kernel timer to periodically poll the rx line until - * the break is finished. - */ -static void sci_schedule_break_timer(struct sci_port *port) -{ - port->break_timer.expires = jiffies + SCI_BREAK_JIFFIES; - add_timer(&port->break_timer); -} -/* Ensure that two consecutive samples find the break over. */ -static void sci_break_timer(unsigned long data) -{ - struct sci_port *port = (struct sci_port *)data; - - if (sci_rxd_in(&port->port) == 0) { - port->break_flag = 1; - sci_schedule_break_timer(port); - } else if (port->break_flag == 1) { - /* break is over. */ - port->break_flag = 2; - sci_schedule_break_timer(port); - } else - port->break_flag = 0; -} - -static inline int sci_handle_errors(struct uart_port *port) -{ - int copied = 0; - unsigned short status = sci_in(port, SCxSR); - struct tty_struct *tty = port->state->port.tty; - - if (status & SCxSR_ORER(port)) { - /* overrun error */ - if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) - copied++; - - dev_notice(port->dev, "overrun error"); - } - - if (status & SCxSR_FER(port)) { - if (sci_rxd_in(port) == 0) { - /* Notify of BREAK */ - struct sci_port *sci_port = to_sci_port(port); - - if (!sci_port->break_flag) { - sci_port->break_flag = 1; - sci_schedule_break_timer(sci_port); - - /* Do sysrq handling. */ - if (uart_handle_break(port)) - return 0; - - dev_dbg(port->dev, "BREAK detected\n"); - - if (tty_insert_flip_char(tty, 0, TTY_BREAK)) - copied++; - } - - } else { - /* frame error */ - if (tty_insert_flip_char(tty, 0, TTY_FRAME)) - copied++; - - dev_notice(port->dev, "frame error\n"); - } - } - - if (status & SCxSR_PER(port)) { - /* parity error */ - if (tty_insert_flip_char(tty, 0, TTY_PARITY)) - copied++; - - dev_notice(port->dev, "parity error"); - } - - if (copied) - tty_flip_buffer_push(tty); - - return copied; -} - -static inline int sci_handle_fifo_overrun(struct uart_port *port) -{ - struct tty_struct *tty = port->state->port.tty; - int copied = 0; - - if (port->type != PORT_SCIF) - return 0; - - if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { - sci_out(port, SCLSR, 0); - - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - tty_flip_buffer_push(tty); - - dev_notice(port->dev, "overrun error\n"); - copied++; - } - - return copied; -} - -static inline int sci_handle_breaks(struct uart_port *port) -{ - int copied = 0; - unsigned short status = sci_in(port, SCxSR); - struct tty_struct *tty = port->state->port.tty; - struct sci_port *s = to_sci_port(port); - - if (uart_handle_break(port)) - return 0; - - if (!s->break_flag && status & SCxSR_BRK(port)) { -#if defined(CONFIG_CPU_SH3) - /* Debounce break */ - s->break_flag = 1; -#endif - /* Notify of BREAK */ - if (tty_insert_flip_char(tty, 0, TTY_BREAK)) - copied++; - - dev_dbg(port->dev, "BREAK detected\n"); - } - - if (copied) - tty_flip_buffer_push(tty); - - copied += sci_handle_fifo_overrun(port); - - return copied; -} - -static irqreturn_t sci_rx_interrupt(int irq, void *ptr) -{ -#ifdef CONFIG_SERIAL_SH_SCI_DMA - struct uart_port *port = ptr; - struct sci_port *s = to_sci_port(port); - - if (s->chan_rx) { - u16 scr = sci_in(port, SCSCR); - u16 ssr = sci_in(port, SCxSR); - - /* Disable future Rx interrupts */ - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - disable_irq_nosync(irq); - scr |= 0x4000; - } else { - scr &= ~SCI_CTRL_FLAGS_RIE; - } - sci_out(port, SCSCR, scr); - /* Clear current interrupt */ - sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); - dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", - jiffies, s->rx_timeout); - mod_timer(&s->rx_timer, jiffies + s->rx_timeout); - - return IRQ_HANDLED; - } -#endif - - /* I think sci_receive_chars has to be called irrespective - * of whether the I_IXOFF is set, otherwise, how is the interrupt - * to be disabled? - */ - sci_receive_chars(ptr); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_tx_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - sci_transmit_chars(port); - spin_unlock_irqrestore(&port->lock, flags); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_er_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - - /* Handle errors */ - if (port->type == PORT_SCI) { - if (sci_handle_errors(port)) { - /* discard character in rx buffer */ - sci_in(port, SCxSR); - sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); - } - } else { - sci_handle_fifo_overrun(port); - sci_rx_interrupt(irq, ptr); - } - - sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); - - /* Kick the transmission */ - sci_tx_interrupt(irq, ptr); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_br_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - - /* Handle BREAKs */ - sci_handle_breaks(port); - sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) -{ - unsigned short ssr_status, scr_status, err_enabled; - struct uart_port *port = ptr; - struct sci_port *s = to_sci_port(port); - irqreturn_t ret = IRQ_NONE; - - ssr_status = sci_in(port, SCxSR); - scr_status = sci_in(port, SCSCR); - err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE); - - /* Tx Interrupt */ - if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) && - !s->chan_tx) - ret = sci_tx_interrupt(irq, ptr); - /* - * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / - * DR flags - */ - if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && - (scr_status & SCI_CTRL_FLAGS_RIE)) - ret = sci_rx_interrupt(irq, ptr); - /* Error Interrupt */ - if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) - ret = sci_er_interrupt(irq, ptr); - /* Break Interrupt */ - if ((ssr_status & SCxSR_BRK(port)) && err_enabled) - ret = sci_br_interrupt(irq, ptr); - - return ret; -} - -/* - * Here we define a transistion notifier so that we can update all of our - * ports' baud rate when the peripheral clock changes. - */ -static int sci_notifier(struct notifier_block *self, - unsigned long phase, void *p) -{ - struct sh_sci_priv *priv = container_of(self, - struct sh_sci_priv, clk_nb); - struct sci_port *sci_port; - unsigned long flags; - - if ((phase == CPUFREQ_POSTCHANGE) || - (phase == CPUFREQ_RESUMECHANGE)) { - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(sci_port, &priv->ports, node) - sci_port->port.uartclk = clk_get_rate(sci_port->iclk); - spin_unlock_irqrestore(&priv->lock, flags); - } - - return NOTIFY_OK; -} - -static void sci_clk_enable(struct uart_port *port) -{ - struct sci_port *sci_port = to_sci_port(port); - - clk_enable(sci_port->iclk); - sci_port->port.uartclk = clk_get_rate(sci_port->iclk); - clk_enable(sci_port->fclk); -} - -static void sci_clk_disable(struct uart_port *port) -{ - struct sci_port *sci_port = to_sci_port(port); - - clk_disable(sci_port->fclk); - clk_disable(sci_port->iclk); -} - -static int sci_request_irq(struct sci_port *port) -{ - int i; - irqreturn_t (*handlers[4])(int irq, void *ptr) = { - sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, - sci_br_interrupt, - }; - const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", - "SCI Transmit Data Empty", "SCI Break" }; - - if (port->irqs[0] == port->irqs[1]) { - if (unlikely(!port->irqs[0])) - return -ENODEV; - - if (request_irq(port->irqs[0], sci_mpxed_interrupt, - IRQF_DISABLED, "sci", port)) { - dev_err(port->port.dev, "Can't allocate IRQ\n"); - return -ENODEV; - } - } else { - for (i = 0; i < ARRAY_SIZE(handlers); i++) { - if (unlikely(!port->irqs[i])) - continue; - - if (request_irq(port->irqs[i], handlers[i], - IRQF_DISABLED, desc[i], port)) { - dev_err(port->port.dev, "Can't allocate IRQ\n"); - return -ENODEV; - } - } - } - - return 0; -} - -static void sci_free_irq(struct sci_port *port) -{ - int i; - - if (port->irqs[0] == port->irqs[1]) - free_irq(port->irqs[0], port); - else { - for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { - if (!port->irqs[i]) - continue; - - free_irq(port->irqs[i], port); - } - } -} - -static unsigned int sci_tx_empty(struct uart_port *port) -{ - unsigned short status = sci_in(port, SCxSR); - unsigned short in_tx_fifo = scif_txfill(port); - - return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; -} - -static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ - /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ - /* If you have signals for DTR and DCD, please implement here. */ -} - -static unsigned int sci_get_mctrl(struct uart_port *port) -{ - /* This routine is used for getting signals of: DTR, DCD, DSR, RI, - and CTS/RTS */ - - return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; -} - -#ifdef CONFIG_SERIAL_SH_SCI_DMA -static void sci_dma_tx_complete(void *arg) -{ - struct sci_port *s = arg; - struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; - unsigned long flags; - - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - - spin_lock_irqsave(&port->lock, flags); - - xmit->tail += sg_dma_len(&s->sg_tx); - xmit->tail &= UART_XMIT_SIZE - 1; - - port->icount.tx += sg_dma_len(&s->sg_tx); - - async_tx_ack(s->desc_tx); - s->cookie_tx = -EINVAL; - s->desc_tx = NULL; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (!uart_circ_empty(xmit)) { - schedule_work(&s->work_tx); - } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 ctrl = sci_in(port, SCSCR); - sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* Locking: called with port lock held */ -static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, - size_t count) -{ - struct uart_port *port = &s->port; - int i, active, room; - - room = tty_buffer_request_room(tty, count); - - if (s->active_rx == s->cookie_rx[0]) { - active = 0; - } else if (s->active_rx == s->cookie_rx[1]) { - active = 1; - } else { - dev_err(port->dev, "cookie %d not found!\n", s->active_rx); - return 0; - } - - if (room < count) - dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", - count - room); - if (!room) - return room; - - for (i = 0; i < room; i++) - tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], - TTY_NORMAL); - - port->icount.rx += room; - - return room; -} - -static void sci_dma_rx_complete(void *arg) -{ - struct sci_port *s = arg; - struct uart_port *port = &s->port; - struct tty_struct *tty = port->state->port.tty; - unsigned long flags; - int count; - - dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); - - spin_lock_irqsave(&port->lock, flags); - - count = sci_dma_rx_push(s, tty, s->buf_len_rx); - - mod_timer(&s->rx_timer, jiffies + s->rx_timeout); - - spin_unlock_irqrestore(&port->lock, flags); - - if (count) - tty_flip_buffer_push(tty); - - schedule_work(&s->work_rx); -} - -static void sci_start_rx(struct uart_port *port); -static void sci_start_tx(struct uart_port *port); - -static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) -{ - struct dma_chan *chan = s->chan_rx; - struct uart_port *port = &s->port; - - s->chan_rx = NULL; - s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; - dma_release_channel(chan); - if (sg_dma_address(&s->sg_rx[0])) - dma_free_coherent(port->dev, s->buf_len_rx * 2, - sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); - if (enable_pio) - sci_start_rx(port); -} - -static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) -{ - struct dma_chan *chan = s->chan_tx; - struct uart_port *port = &s->port; - - s->chan_tx = NULL; - s->cookie_tx = -EINVAL; - dma_release_channel(chan); - if (enable_pio) - sci_start_tx(port); -} - -static void sci_submit_rx(struct sci_port *s) -{ - struct dma_chan *chan = s->chan_rx; - int i; - - for (i = 0; i < 2; i++) { - struct scatterlist *sg = &s->sg_rx[i]; - struct dma_async_tx_descriptor *desc; - - desc = chan->device->device_prep_slave_sg(chan, - sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); - - if (desc) { - s->desc_rx[i] = desc; - desc->callback = sci_dma_rx_complete; - desc->callback_param = s; - s->cookie_rx[i] = desc->tx_submit(desc); - } - - if (!desc || s->cookie_rx[i] < 0) { - if (i) { - async_tx_ack(s->desc_rx[0]); - s->cookie_rx[0] = -EINVAL; - } - if (desc) { - async_tx_ack(desc); - s->cookie_rx[i] = -EINVAL; - } - dev_warn(s->port.dev, - "failed to re-start DMA, using PIO\n"); - sci_rx_dma_release(s, true); - return; - } - dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, - s->cookie_rx[i], i); - } - - s->active_rx = s->cookie_rx[0]; - - dma_async_issue_pending(chan); -} - -static void work_fn_rx(struct work_struct *work) -{ - struct sci_port *s = container_of(work, struct sci_port, work_rx); - struct uart_port *port = &s->port; - struct dma_async_tx_descriptor *desc; - int new; - - if (s->active_rx == s->cookie_rx[0]) { - new = 0; - } else if (s->active_rx == s->cookie_rx[1]) { - new = 1; - } else { - dev_err(port->dev, "cookie %d not found!\n", s->active_rx); - return; - } - desc = s->desc_rx[new]; - - if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != - DMA_SUCCESS) { - /* Handle incomplete DMA receive */ - struct tty_struct *tty = port->state->port.tty; - struct dma_chan *chan = s->chan_rx; - struct sh_desc *sh_desc = container_of(desc, struct sh_desc, - async_tx); - unsigned long flags; - int count; - - chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); - dev_dbg(port->dev, "Read %u bytes with cookie %d\n", - sh_desc->partial, sh_desc->cookie); - - spin_lock_irqsave(&port->lock, flags); - count = sci_dma_rx_push(s, tty, sh_desc->partial); - spin_unlock_irqrestore(&port->lock, flags); - - if (count) - tty_flip_buffer_push(tty); - - sci_submit_rx(s); - - return; - } - - s->cookie_rx[new] = desc->tx_submit(desc); - if (s->cookie_rx[new] < 0) { - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); - sci_rx_dma_release(s, true); - return; - } - - s->active_rx = s->cookie_rx[!new]; - - dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, - s->cookie_rx[new], new, s->active_rx); -} - -static void work_fn_tx(struct work_struct *work) -{ - struct sci_port *s = container_of(work, struct sci_port, work_tx); - struct dma_async_tx_descriptor *desc; - struct dma_chan *chan = s->chan_tx; - struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; - struct scatterlist *sg = &s->sg_tx; - - /* - * DMA is idle now. - * Port xmit buffer is already mapped, and it is one page... Just adjust - * offsets and lengths. Since it is a circular buffer, we have to - * transmit till the end, and then the rest. Take the port lock to get a - * consistent xmit buffer state. - */ - spin_lock_irq(&port->lock); - sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + - sg->offset; - sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - spin_unlock_irq(&port->lock); - - BUG_ON(!sg_dma_len(sg)); - - desc = chan->device->device_prep_slave_sg(chan, - sg, s->sg_len_tx, DMA_TO_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - /* switch to PIO */ - sci_tx_dma_release(s, true); - return; - } - - dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); - - spin_lock_irq(&port->lock); - s->desc_tx = desc; - desc->callback = sci_dma_tx_complete; - desc->callback_param = s; - spin_unlock_irq(&port->lock); - s->cookie_tx = desc->tx_submit(desc); - if (s->cookie_tx < 0) { - dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); - /* switch to PIO */ - sci_tx_dma_release(s, true); - return; - } - - dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, - xmit->buf, xmit->tail, xmit->head, s->cookie_tx); - - dma_async_issue_pending(chan); -} -#endif - -static void sci_start_tx(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - unsigned short ctrl; - -#ifdef CONFIG_SERIAL_SH_SCI_DMA - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 new, scr = sci_in(port, SCSCR); - if (s->chan_tx) - new = scr | 0x8000; - else - new = scr & ~0x8000; - if (new != scr) - sci_out(port, SCSCR, new); - } - if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && - s->cookie_tx < 0) - schedule_work(&s->work_tx); -#endif - if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); - } -} - -static void sci_stop_tx(struct uart_port *port) -{ - unsigned short ctrl; - - /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~0x8000; - ctrl &= ~SCI_CTRL_FLAGS_TIE; - sci_out(port, SCSCR, ctrl); -} - -static void sci_start_rx(struct uart_port *port) -{ - unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; - - /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ - ctrl |= sci_in(port, SCSCR); - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~0x4000; - sci_out(port, SCSCR, ctrl); -} - -static void sci_stop_rx(struct uart_port *port) -{ - unsigned short ctrl; - - /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~0x4000; - ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); - sci_out(port, SCSCR, ctrl); -} - -static void sci_enable_ms(struct uart_port *port) -{ - /* Nothing here yet .. */ -} - -static void sci_break_ctl(struct uart_port *port, int break_state) -{ - /* Nothing here yet .. */ -} - -#ifdef CONFIG_SERIAL_SH_SCI_DMA -static bool filter(struct dma_chan *chan, void *slave) -{ - struct sh_dmae_slave *param = slave; - - dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, - param->slave_id); - - if (param->dma_dev == chan->device->dev) { - chan->private = param; - return true; - } else { - return false; - } -} - -static void rx_timer_fn(unsigned long arg) -{ - struct sci_port *s = (struct sci_port *)arg; - struct uart_port *port = &s->port; - u16 scr = sci_in(port, SCSCR); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - scr &= ~0x4000; - enable_irq(s->irqs[1]); - } - sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); - dev_dbg(port->dev, "DMA Rx timed out\n"); - schedule_work(&s->work_rx); -} - -static void sci_request_dma(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - struct sh_dmae_slave *param; - struct dma_chan *chan; - dma_cap_mask_t mask; - int nent; - - dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, - port->line, s->dma_dev); - - if (!s->dma_dev) - return; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - param = &s->param_tx; - - /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ - param->slave_id = s->slave_tx; - param->dma_dev = s->dma_dev; - - s->cookie_tx = -EINVAL; - chan = dma_request_channel(mask, filter, param); - dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); - if (chan) { - s->chan_tx = chan; - sg_init_table(&s->sg_tx, 1); - /* UART circular tx buffer is an aligned page. */ - BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); - sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), - UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); - nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); - if (!nent) - sci_tx_dma_release(s, false); - else - dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, - sg_dma_len(&s->sg_tx), - port->state->xmit.buf, sg_dma_address(&s->sg_tx)); - - s->sg_len_tx = nent; - - INIT_WORK(&s->work_tx, work_fn_tx); - } - - param = &s->param_rx; - - /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ - param->slave_id = s->slave_rx; - param->dma_dev = s->dma_dev; - - chan = dma_request_channel(mask, filter, param); - dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); - if (chan) { - dma_addr_t dma[2]; - void *buf[2]; - int i; - - s->chan_rx = chan; - - s->buf_len_rx = 2 * max(16, (int)port->fifosize); - buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, - &dma[0], GFP_KERNEL); - - if (!buf[0]) { - dev_warn(port->dev, - "failed to allocate dma buffer, using PIO\n"); - sci_rx_dma_release(s, true); - return; - } - - buf[1] = buf[0] + s->buf_len_rx; - dma[1] = dma[0] + s->buf_len_rx; - - for (i = 0; i < 2; i++) { - struct scatterlist *sg = &s->sg_rx[i]; - - sg_init_table(sg, 1); - sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, - (int)buf[i] & ~PAGE_MASK); - sg_dma_address(sg) = dma[i]; - } - - INIT_WORK(&s->work_rx, work_fn_rx); - setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); - - sci_submit_rx(s); - } -} - -static void sci_free_dma(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - - if (!s->dma_dev) - return; - - if (s->chan_tx) - sci_tx_dma_release(s, false); - if (s->chan_rx) - sci_rx_dma_release(s, false); -} -#endif - -static int sci_startup(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - - if (s->enable) - s->enable(port); - - sci_request_irq(s); -#ifdef CONFIG_SERIAL_SH_SCI_DMA - sci_request_dma(port); -#endif - sci_start_tx(port); - sci_start_rx(port); - - return 0; -} - -static void sci_shutdown(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - - sci_stop_rx(port); - sci_stop_tx(port); -#ifdef CONFIG_SERIAL_SH_SCI_DMA - sci_free_dma(port); -#endif - sci_free_irq(s); - - if (s->disable) - s->disable(port); -} - -static void sci_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ -#ifdef CONFIG_SERIAL_SH_SCI_DMA - struct sci_port *s = to_sci_port(port); -#endif - unsigned int status, baud, smr_val, max_baud; - int t = -1; - u16 scfcr = 0; - - /* - * earlyprintk comes here early on with port->uartclk set to zero. - * the clock framework is not up and running at this point so here - * we assume that 115200 is the maximum baud rate. please note that - * the baud rate is not programmed during earlyprintk - it is assumed - * that the previous boot loader has enabled required clocks and - * setup the baud rate generator hardware for us already. - */ - max_baud = port->uartclk ? port->uartclk / 16 : 115200; - - baud = uart_get_baud_rate(port, termios, old, 0, max_baud); - if (likely(baud && port->uartclk)) - t = SCBRR_VALUE(baud, port->uartclk); - - do { - status = sci_in(port, SCxSR); - } while (!(status & SCxSR_TEND(port))); - - sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ - - if (port->type != PORT_SCI) - sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); - - smr_val = sci_in(port, SCSMR) & 3; - if ((termios->c_cflag & CSIZE) == CS7) - smr_val |= 0x40; - if (termios->c_cflag & PARENB) - smr_val |= 0x20; - if (termios->c_cflag & PARODD) - smr_val |= 0x30; - if (termios->c_cflag & CSTOPB) - smr_val |= 0x08; - - uart_update_timeout(port, termios->c_cflag, baud); - - sci_out(port, SCSMR, smr_val); - - dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, - SCSCR_INIT(port)); - - if (t > 0) { - if (t >= 256) { - sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); - t >>= 2; - } else - sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); - - sci_out(port, SCBRR, t); - udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ - } - - sci_init_pins(port, termios->c_cflag); - sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); - - sci_out(port, SCSCR, SCSCR_INIT(port)); - -#ifdef CONFIG_SERIAL_SH_SCI_DMA - /* - * Calculate delay for 1.5 DMA buffers: see - * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits - * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function - * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." - * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO - * sizes), but it has been found out experimentally, that this is not - * enough: the driver too often needlessly runs on a DMA timeout. 20ms - * as a minimum seem to work perfectly. - */ - if (s->chan_rx) { - s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / - port->fifosize / 2; - dev_dbg(port->dev, - "DMA Rx t-out %ums, tty t-out %u jiffies\n", - s->rx_timeout * 1000 / HZ, port->timeout); - if (s->rx_timeout < msecs_to_jiffies(20)) - s->rx_timeout = msecs_to_jiffies(20); - } -#endif - - if ((termios->c_cflag & CREAD) != 0) - sci_start_rx(port); -} - -static const char *sci_type(struct uart_port *port) -{ - switch (port->type) { - case PORT_IRDA: - return "irda"; - case PORT_SCI: - return "sci"; - case PORT_SCIF: - return "scif"; - case PORT_SCIFA: - return "scifa"; - case PORT_SCIFB: - return "scifb"; - } - - return NULL; -} - -static void sci_release_port(struct uart_port *port) -{ - /* Nothing here yet .. */ -} - -static int sci_request_port(struct uart_port *port) -{ - /* Nothing here yet .. */ - return 0; -} - -static void sci_config_port(struct uart_port *port, int flags) -{ - struct sci_port *s = to_sci_port(port); - - port->type = s->type; - - if (port->membase) - return; - - if (port->flags & UPF_IOREMAP) { - port->membase = ioremap_nocache(port->mapbase, 0x40); - - if (IS_ERR(port->membase)) - dev_err(port->dev, "can't remap port#%d\n", port->line); - } else { - /* - * For the simple (and majority of) cases where we don't - * need to do any remapping, just cast the cookie - * directly. - */ - port->membase = (void __iomem *)port->mapbase; - } -} - -static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - struct sci_port *s = to_sci_port(port); - - if (ser->irq != s->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs) - return -EINVAL; - if (ser->baud_base < 2400) - /* No paper tape reader for Mitch.. */ - return -EINVAL; - - return 0; -} - -static struct uart_ops sci_uart_ops = { - .tx_empty = sci_tx_empty, - .set_mctrl = sci_set_mctrl, - .get_mctrl = sci_get_mctrl, - .start_tx = sci_start_tx, - .stop_tx = sci_stop_tx, - .stop_rx = sci_stop_rx, - .enable_ms = sci_enable_ms, - .break_ctl = sci_break_ctl, - .startup = sci_startup, - .shutdown = sci_shutdown, - .set_termios = sci_set_termios, - .type = sci_type, - .release_port = sci_release_port, - .request_port = sci_request_port, - .config_port = sci_config_port, - .verify_port = sci_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = sci_poll_get_char, - .poll_put_char = sci_poll_put_char, -#endif -}; - -static int __devinit sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) -{ - struct uart_port *port = &sci_port->port; - - port->ops = &sci_uart_ops; - port->iotype = UPIO_MEM; - port->line = index; - - switch (p->type) { - case PORT_SCIFB: - port->fifosize = 256; - break; - case PORT_SCIFA: - port->fifosize = 64; - break; - case PORT_SCIF: - port->fifosize = 16; - break; - default: - port->fifosize = 1; - break; - } - - if (dev) { - sci_port->iclk = clk_get(&dev->dev, "sci_ick"); - if (IS_ERR(sci_port->iclk)) { - sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); - if (IS_ERR(sci_port->iclk)) { - dev_err(&dev->dev, "can't get iclk\n"); - return PTR_ERR(sci_port->iclk); - } - } - - /* - * The function clock is optional, ignore it if we can't - * find it. - */ - sci_port->fclk = clk_get(&dev->dev, "sci_fck"); - if (IS_ERR(sci_port->fclk)) - sci_port->fclk = NULL; - - sci_port->enable = sci_clk_enable; - sci_port->disable = sci_clk_disable; - port->dev = &dev->dev; - } - - sci_port->break_timer.data = (unsigned long)sci_port; - sci_port->break_timer.function = sci_break_timer; - init_timer(&sci_port->break_timer); - - port->mapbase = p->mapbase; - port->membase = p->membase; - - port->irq = p->irqs[SCIx_TXI_IRQ]; - port->flags = p->flags; - sci_port->type = port->type = p->type; - -#ifdef CONFIG_SERIAL_SH_SCI_DMA - sci_port->dma_dev = p->dma_dev; - sci_port->slave_tx = p->dma_slave_tx; - sci_port->slave_rx = p->dma_slave_rx; - - dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__, - p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); -#endif - - memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); - return 0; -} - -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE -static struct tty_driver *serial_console_device(struct console *co, int *index) -{ - struct uart_driver *p = &sci_uart_driver; - *index = co->index; - return p->tty_driver; -} - -static void serial_console_putchar(struct uart_port *port, int ch) -{ - sci_poll_put_char(port, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - */ -static void serial_console_write(struct console *co, const char *s, - unsigned count) -{ - struct uart_port *port = co->data; - struct sci_port *sci_port = to_sci_port(port); - unsigned short bits; - - if (sci_port->enable) - sci_port->enable(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 ((sci_in(port, SCxSR) & bits) != bits) - cpu_relax(); - - if (sci_port->disable) - sci_port->disable(port); -} - -static int __devinit serial_console_setup(struct console *co, char *options) -{ - struct sci_port *sci_port; - struct uart_port *port; - int baud = 115200; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= SCI_NPORTS) - co->index = 0; - - if (co->data) { - port = co->data; - sci_port = to_sci_port(port); - } else { - sci_port = &sci_ports[co->index]; - port = &sci_port->port; - co->data = port; - } - - /* - * Also need to check port->type, we don't actually have any - * UPIO_PORT ports, but uart_report_port() handily misreports - * it anyways if we don't have a port available by the time this is - * called. - */ - if (!port->type) - return -ENODEV; - - sci_config_port(port, 0); - - if (sci_port->enable) - sci_port->enable(port); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - ret = uart_set_options(port, co, baud, parity, bits, flow); -#if defined(__H8300H__) || defined(__H8300S__) - /* disable rx interrupt */ - if (ret == 0) - sci_stop_rx(port); -#endif - /* TODO: disable clock */ - return ret; -} - -static struct console serial_console = { - .name = "ttySC", - .device = serial_console_device, - .write = serial_console_write, - .setup = serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -static int __init sci_console_init(void) -{ - register_console(&serial_console); - return 0; -} -console_initcall(sci_console_init); - -static struct sci_port early_serial_port; -static struct console early_serial_console = { - .name = "early_ttySC", - .write = serial_console_write, - .flags = CON_PRINTBUFFER, -}; -static char early_serial_buf[32]; - -#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ - -#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) -#define SCI_CONSOLE (&serial_console) -#else -#define SCI_CONSOLE 0 -#endif - -static char banner[] __initdata = - KERN_INFO "SuperH SCI(F) driver initialized\n"; - -static struct uart_driver sci_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "sci", - .dev_name = "ttySC", - .major = SCI_MAJOR, - .minor = SCI_MINOR_START, - .nr = SCI_NPORTS, - .cons = SCI_CONSOLE, -}; - - -static int sci_remove(struct platform_device *dev) -{ - struct sh_sci_priv *priv = platform_get_drvdata(dev); - struct sci_port *p; - unsigned long flags; - - cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); - - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) { - uart_remove_one_port(&sci_uart_driver, &p->port); - clk_put(p->iclk); - clk_put(p->fclk); - } - spin_unlock_irqrestore(&priv->lock, flags); - - kfree(priv); - return 0; -} - -static int __devinit sci_probe_single(struct platform_device *dev, - unsigned int index, - struct plat_sci_port *p, - struct sci_port *sciport) -{ - struct sh_sci_priv *priv = platform_get_drvdata(dev); - unsigned long flags; - int ret; - - /* Sanity check */ - if (unlikely(index >= SCI_NPORTS)) { - dev_notice(&dev->dev, "Attempting to register port " - "%d when only %d are available.\n", - index+1, SCI_NPORTS); - dev_notice(&dev->dev, "Consider bumping " - "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); - return 0; - } - - ret = sci_init_single(dev, sciport, index, p); - if (ret) - return ret; - - ret = uart_add_one_port(&sci_uart_driver, &sciport->port); - if (ret) - return ret; - - INIT_LIST_HEAD(&sciport->node); - - spin_lock_irqsave(&priv->lock, flags); - list_add(&sciport->node, &priv->ports); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -/* - * Register a set of serial devices attached to a platform device. The - * list is terminated with a zero flags entry, which means we expect - * all entries to have at least UPF_BOOT_AUTOCONF set. Platforms that need - * remapping (such as sh64) should also set UPF_IOREMAP. - */ -static int __devinit sci_probe(struct platform_device *dev) -{ - struct plat_sci_port *p = dev->dev.platform_data; - struct sh_sci_priv *priv; - int i, ret = -EINVAL; - -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE - if (is_early_platform_device(dev)) { - if (dev->id == -1) - return -ENOTSUPP; - early_serial_console.index = dev->id; - early_serial_console.data = &early_serial_port.port; - sci_init_single(NULL, &early_serial_port, dev->id, p); - serial_console_setup(&early_serial_console, early_serial_buf); - if (!strstr(early_serial_buf, "keep")) - early_serial_console.flags |= CON_BOOT; - register_console(&early_serial_console); - return 0; - } -#endif - - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - INIT_LIST_HEAD(&priv->ports); - spin_lock_init(&priv->lock); - platform_set_drvdata(dev, priv); - - priv->clk_nb.notifier_call = sci_notifier; - cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); - - if (dev->id != -1) { - ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); - if (ret) - goto err_unreg; - } else { - for (i = 0; p && p->flags != 0; p++, i++) { - ret = sci_probe_single(dev, i, p, &sci_ports[i]); - if (ret) - goto err_unreg; - } - } - -#ifdef CONFIG_SH_STANDARD_BIOS - sh_bios_gdb_detach(); -#endif - - return 0; - -err_unreg: - sci_remove(dev); - return ret; -} - -static int sci_suspend(struct device *dev) -{ - struct sh_sci_priv *priv = dev_get_drvdata(dev); - struct sci_port *p; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) - uart_suspend_port(&sci_uart_driver, &p->port); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static int sci_resume(struct device *dev) -{ - struct sh_sci_priv *priv = dev_get_drvdata(dev); - struct sci_port *p; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) - uart_resume_port(&sci_uart_driver, &p->port); - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - -static const struct dev_pm_ops sci_dev_pm_ops = { - .suspend = sci_suspend, - .resume = sci_resume, -}; - -static struct platform_driver sci_driver = { - .probe = sci_probe, - .remove = sci_remove, - .driver = { - .name = "sh-sci", - .owner = THIS_MODULE, - .pm = &sci_dev_pm_ops, - }, -}; - -static int __init sci_init(void) -{ - int ret; - - printk(banner); - - ret = uart_register_driver(&sci_uart_driver); - if (likely(ret == 0)) { - ret = platform_driver_register(&sci_driver); - if (unlikely(ret)) - uart_unregister_driver(&sci_uart_driver); - } - - return ret; -} - -static void __exit sci_exit(void) -{ - platform_driver_unregister(&sci_driver); - uart_unregister_driver(&sci_uart_driver); -} - -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE -early_platform_init_buffer("earlyprintk", &sci_driver, - early_serial_buf, ARRAY_SIZE(early_serial_buf)); -#endif -module_init(sci_init); -module_exit(sci_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:sh-sci"); diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h deleted file mode 100644 index 4bc614e4221c..000000000000 --- a/drivers/serial/sh-sci.h +++ /dev/null @@ -1,660 +0,0 @@ -#include -#include -#include - -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) -#include -#endif -#if defined(CONFIG_H8S2678) -#include -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7708) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ -# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) -# define SCIF0 0xA4400000 -# define SCIF2 0xA4410000 -# define SCSMR_Ir 0xA44A0000 -# define IRDA_SCIF SCIF0 -# define SCPCR 0xA4000116 -# define SCPDR 0xA4000136 - -/* Set the clock source, - * SCIF2 (0xA4410000) -> External clock, SCK pin used as clock input - * SCIF0 (0xA4400000) -> Internal clock, SCK pin as serial clock output - */ -# define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0 -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define PORT_PTCR 0xA405011EUL -# define PORT_PVCR 0xA4050122UL -# define SCIF_ORER 0x0200 /* overrun error bit */ -#elif defined(CONFIG_SH_RTS7751R2D) -# define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) -# define SCSPTR1 0xffe0001c /* 8 bit SCI */ -# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ - 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ - 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) -#elif defined(CONFIG_CPU_SUBTYPE_SH7760) -# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ -# define SCSPTR1 0xfe610024 /* 16 bit SCIF */ -# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -# define SCSPTR0 0xA4400000 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define PACR 0xa4050100 -# define PBCR 0xa4050102 -# define SCSCR_INIT(port) 0x3B -#elif defined(CONFIG_CPU_SUBTYPE_SH7343) -# define SCSPTR0 0xffe00010 /* 16 bit SCIF */ -# define SCSPTR1 0xffe10010 /* 16 bit SCIF */ -# define SCSPTR2 0xffe20010 /* 16 bit SCIF */ -# define SCSPTR3 0xffe30010 /* 16 bit SCIF */ -# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -# define PADR 0xA4050120 -# define PSDR 0xA405013e -# define PWDR 0xA4050166 -# define PSCR 0xA405011E -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7366) -# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ -# define SCSPTR0 SCPDR0 -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) -# define SCSPTR0 0xa4050160 -# define SCSPTR1 0xa405013e -# define SCSPTR2 0xa4050160 -# define SCSPTR3 0xa405013e -# define SCSPTR4 0xa4050128 -# define SCSPTR5 0xa4050128 -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) ((port)->type == PORT_SCIFA ? \ - 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ - 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) -#elif defined(CONFIG_CPU_SUBTYPE_SH4_202) -# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103) -# define SCIF_BASE_ADDR 0x01030000 -# define SCIF_ADDR_SH5 PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR -# define SCIF_PTR2_OFFS 0x0000020 -# define SCIF_LSR2_OFFS 0x0000024 -# define SCSPTR2 ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */ -# define SCLSR2 ((port->mapbase)+SCIF_LSR2_OFFS) /* 16 bit SCIF */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0, TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) -#elif defined(CONFIG_H8S2678) -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ -# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) -#elif defined(CONFIG_CPU_SUBTYPE_SH7757) -# define SCSPTR0 0xfe4b0020 -# define SCSPTR1 0xfe4b0020 -# define SCSPTR2 0xfe4b0020 -# define SCIF_ORER 0x0001 -# define SCSCR_INIT(port) 0x38 -# define SCIF_ONLY -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -# define SCSPTR1 0xffe08024 /* 16 bit SCIF */ -# define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7770) -# define SCSPTR0 0xff923020 /* 16 bit SCIF */ -# define SCSPTR1 0xff924020 /* 16 bit SCIF */ -# define SCSPTR2 0xff925020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7780) -# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ -# define SCSPTR1 0xffe10024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ - -#if defined(CONFIG_SH_SH2007) -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ -# define SCSCR_INIT(port) 0x38 -#else -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ -# define SCSCR_INIT(port) 0x3a -#endif - -#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -# define SCSPTR0 0xffea0024 /* 16 bit SCIF */ -# define SCSPTR1 0xffeb0024 /* 16 bit SCIF */ -# define SCSPTR2 0xffec0024 /* 16 bit SCIF */ -# define SCSPTR3 0xffed0024 /* 16 bit SCIF */ -# define SCSPTR4 0xffee0024 /* 16 bit SCIF */ -# define SCSPTR5 0xffef0024 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ - defined(CONFIG_CPU_SUBTYPE_SH7203) || \ - defined(CONFIG_CPU_SUBTYPE_SH7206) || \ - defined(CONFIG_CPU_SUBTYPE_SH7263) -# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ -# define SCSPTR1 0xfffe8820 /* 16 bit SCIF */ -# define SCSPTR2 0xfffe9020 /* 16 bit SCIF */ -# define SCSPTR3 0xfffe9820 /* 16 bit SCIF */ -# if defined(CONFIG_CPU_SUBTYPE_SH7201) -# define SCSPTR4 0xfffeA020 /* 16 bit SCIF */ -# define SCSPTR5 0xfffeA820 /* 16 bit SCIF */ -# define SCSPTR6 0xfffeB020 /* 16 bit SCIF */ -# define SCSPTR7 0xfffeB820 /* 16 bit SCIF */ -# endif -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7619) -# define SCSPTR0 0xf8400020 /* 16 bit SCIF */ -# define SCSPTR1 0xf8410020 /* 16 bit SCIF */ -# define SCSPTR2 0xf8420020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#elif defined(CONFIG_CPU_SUBTYPE_SHX3) -# define SCSPTR0 0xffc30020 /* 16 bit SCIF */ -# define SCSPTR1 0xffc40020 /* 16 bit SCIF */ -# define SCSPTR2 0xffc50020 /* 16 bit SCIF */ -# define SCSPTR3 0xffc60020 /* 16 bit SCIF */ -# define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ -#else -# error CPU subtype not defined -#endif - -/* SCSCR */ -#define SCI_CTRL_FLAGS_TIE 0x80 /* all */ -#define SCI_CTRL_FLAGS_RIE 0x40 /* all */ -#define SCI_CTRL_FLAGS_TE 0x20 /* all */ -#define SCI_CTRL_FLAGS_RE 0x10 /* all */ -#if defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7722) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) || \ - defined(CONFIG_CPU_SUBTYPE_SHX3) -#define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -#define SCI_CTRL_FLAGS_REIE ((port)->type == PORT_SCIFA ? 0 : 8) -#else -#define SCI_CTRL_FLAGS_REIE 0 -#endif -/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_CTRL_FLAGS_CKE1 0x02 * all */ -/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */ - -/* SCxSR SCI */ -#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ - -#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) - -/* SCxSR SCIF */ -#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ -#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ - -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -# define SCIF_ORER 0x0200 -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) -# define SCIF_RFDC_MASK 0x007f -# define SCIF_TXROOM_MAX 64 -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK ) -# define SCIF_RFDC_MASK 0x007f -# define SCIF_TXROOM_MAX 64 -/* SH7763 SCIF2 support */ -# define SCIF2_RFDC_MASK 0x001f -# define SCIF2_TXROOM_MAX 16 -#else -# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) -# define SCIF_RFDC_MASK 0x001f -# define SCIF_TXROOM_MAX 16 -#endif - -#ifndef SCIF_ORER -#define SCIF_ORER 0x0000 -#endif - -#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) -#define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) -#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) -#define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) -#define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) -#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) -#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) -#define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) - -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -# define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) -# define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) -# define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) -# define SCxSR_BREAK_CLEAR(port) (sci_in(port, SCxSR) & 0xffe3) -#else -# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) -# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) -# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) -# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) -#endif - -/* SCFCR */ -#define SCFCR_RFRST 0x0002 -#define SCFCR_TFRST 0x0004 -#define SCFCR_TCRST 0x4000 -#define SCFCR_MCE 0x0008 - -#define SCI_MAJOR 204 -#define SCI_MINOR_START 8 - -/* Generic serial flags */ -#define SCI_RX_THROTTLE 0x0000001 - -#define SCI_MAGIC 0xbabeface - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define SCI_EVENT_WRITE_WAKEUP 0 - -#define SCI_IN(size, offset) \ - if ((size) == 8) { \ - return ioread8(port->membase + (offset)); \ - } else { \ - return ioread16(port->membase + (offset)); \ - } -#define SCI_OUT(size, offset, value) \ - if ((size) == 8) { \ - iowrite8(value, port->membase + (offset)); \ - } else if ((size) == 16) { \ - iowrite16(value, port->membase + (offset)); \ - } - -#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - SCI_IN(scif_size, scif_offset) \ - } else { /* PORT_SCI or PORT_SCIFA */ \ - SCI_IN(sci_size, sci_offset); \ - } \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ - SCI_OUT(scif_size, scif_offset, value) \ - } else { /* PORT_SCI or PORT_SCIFA */ \ - SCI_OUT(sci_size, sci_offset, value); \ - } \ - } - -#ifdef CONFIG_H8300 -/* h8300 don't have SCIF */ -#define CPU_SCIF_FNS(name) \ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - return 0; \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - } -#else -#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ - static inline unsigned int sci_##name##_in(struct uart_port *port) \ - { \ - SCI_IN(scif_size, scif_offset); \ - } \ - static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ - { \ - SCI_OUT(scif_size, scif_offset, value); \ - } -#endif - -#define CPU_SCI_FNS(name, sci_offset, sci_size) \ - static inline unsigned int sci_##name##_in(struct uart_port* port) \ - { \ - SCI_IN(sci_size, sci_offset); \ - } \ - static inline void sci_##name##_out(struct uart_port* port, unsigned int value) \ - { \ - SCI_OUT(sci_size, sci_offset, value); \ - } - -#if defined(CONFIG_CPU_SH3) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) -#define SCIF_FNS(name, scif_offset, scif_size) \ - CPU_SCIF_FNS(name, scif_offset, scif_size) -#elif defined(CONFIG_ARCH_SH7372) -#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ - CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) -#define SCIF_FNS(name, scif_offset, scif_size) \ - CPU_SCIF_FNS(name, scif_offset, scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) -#endif -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name) -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) - #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) - #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#else -#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ - sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ - h8_sci_offset, h8_sci_size) \ - CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) -#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ - CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) - -SCIF_FNS(SCSMR, 0x00, 16) -SCIF_FNS(SCBRR, 0x04, 8) -SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCTDSR, 0x0c, 8) -SCIF_FNS(SCFER, 0x10, 16) -SCIF_FNS(SCxSR, 0x14, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCxTDR, 0x20, 8) -SCIF_FNS(SCxRDR, 0x24, 8) -SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_ARCH_SH7372) -SCIF_FNS(SCSMR, 0x00, 16) -SCIF_FNS(SCBRR, 0x04, 8) -SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCTDSR, 0x0c, 16) -SCIF_FNS(SCFER, 0x10, 16) -SCIF_FNS(SCxSR, 0x14, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCTFDR, 0x38, 16) -SCIF_FNS(SCRFDR, 0x3c, 16) -SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) -SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) -SCIF_FNS(SCLSR, 0x00, 0) -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) -SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) -SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) -SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) -SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) -SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) -SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) -SCIx_FNS(SCSPTR, 0, 0, 0, 0) -SCIF_FNS(SCTDSR, 0x0c, 8) -SCIF_FNS(SCFER, 0x10, 16) -SCIF_FNS(SCFCR, 0x18, 16) -SCIF_FNS(SCFDR, 0x1c, 16) -SCIF_FNS(SCLSR, 0x24, 16) -#else -/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ -/* name off sz off sz off sz off sz off sz*/ -SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) -SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) -SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) -SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) -SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) -SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) -SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) -#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) -SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) -SCIF_FNS(SCLSR, 0, 0, 0x28, 16) -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) -SCIF_FNS(SCSPTR2, 0, 0, 0x20, 16) -SCIF_FNS(SCLSR2, 0, 0, 0x24, 16) -SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) -SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) -SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) -SCIF_FNS(SCLSR, 0, 0, 0x28, 16) -#else -SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) -#if defined(CONFIG_CPU_SUBTYPE_SH7722) -SCIF_FNS(SCSPTR, 0, 0, 0, 0) -#else -SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) -#endif -SCIF_FNS(SCLSR, 0, 0, 0x24, 16) -#endif -#endif -#define sci_in(port, reg) sci_##reg##_in(port) -#define sci_out(port, reg, value) sci_##reg##_out(port, value) - -/* H8/300 series SCI pins assignment */ -#if defined(__H8300H__) || defined(__H8300S__) -static const struct __attribute__((packed)) { - int port; /* GPIO port no */ - unsigned short rx,tx; /* GPIO bit no */ -} h8300_sci_pins[] = { -#if defined(CONFIG_H83007) || defined(CONFIG_H83068) - { /* SCI0 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P9, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_PB, - .rx = H8300_GPIO_B7, - .tx = H8300_GPIO_B6, - } -#elif defined(CONFIG_H8S2678) - { /* SCI0 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B2, - .tx = H8300_GPIO_B0, - }, - { /* SCI1 */ - .port = H8300_GPIO_P3, - .rx = H8300_GPIO_B3, - .tx = H8300_GPIO_B1, - }, - { /* SCI2 */ - .port = H8300_GPIO_P5, - .rx = H8300_GPIO_B1, - .tx = H8300_GPIO_B0, - } -#endif -}; -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7708) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xfffffe80) - return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ - return 1; -} -#elif defined(__H8300H__) || defined(__H8300S__) -static inline int sci_rxd_in(struct uart_port *port) -{ - int ch = (port->mapbase - SMR0) >> 3; - return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; -} -#else /* default case for non-SCI processors */ -static inline int sci_rxd_in(struct uart_port *port) -{ - return 1; -} -#endif - -/* - * Values for the BitRate Register (SCBRR) - * - * The values are actually divisors for a frequency which can - * be internal to the SH3 (14.7456MHz) or derived from an external - * clock source. This driver assumes the internal clock is used; - * to support using an external clock source, config options or - * possibly command-line options would need to be added. - * - * Also, to support speeds below 2400 (why?) the lower 2 bits of - * the SCSMR register would also need to be set to non-zero values. - * - * -- Greg Banks 27Feb2000 - * - * Answer: The SCBRR register is only eight bits, and the value in - * it gets larger with lower baud rates. At around 2400 (depending on - * the peripherial module clock) you run out of bits. However the - * lower two bits of SCSMR allow the module clock to be divided down, - * scaling the value which is needed in SCBRR. - * - * -- Stuart Menefy - 23 May 2000 - * - * I meant, why would anyone bother with bitrates below 2400. - * - * -- Greg Banks - 7Jul2000 - * - * You "speedist"! How will I use my 110bps ASR-33 teletype with paper - * tape reader as a console! - * - * -- Mitch Davis - 15 Jul 2000 - */ - -#if (defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786)) && \ - !defined(CONFIG_SH_SH2007) -#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) -static inline int scbrr_calc(struct uart_port *port, int bps, int clk) -{ - if (port->type == PORT_SCIF) - return (clk+16*bps)/(32*bps)-1; - else - return ((clk*2)+16*bps)/(16*bps)-1; -} -#define SCBRR_VALUE(bps, clk) scbrr_calc(port, bps, clk) -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) -#else /* Generic SH */ -#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) -#endif diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c deleted file mode 100644 index cff9a306660f..000000000000 --- a/drivers/serial/sn_console.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * C-Brick Serial Port (and console) driver for SGI Altix machines. - * - * This driver is NOT suitable for talking to the l1-controller for - * anything other than 'console activities' --- please use the l1 - * driver for that. - * - * - * Copyright (c) 2004-2006 Silicon Graphics, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * Further, this software is distributed without any warranty that it is - * free of the rightful claim of any third person regarding infringement - * or the like. Any license provided herein, whether implied or - * otherwise, applies only to this software file. Patent licenses, if - * any, provided herein do not apply to combinations of this program with - * other software, or any other product whatsoever. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - * - * Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, - * Mountain View, CA 94043, or: - * - * http://www.sgi.com - * - * For further information regarding this notice, see: - * - * http://oss.sgi.com/projects/GenInfo/NoticeExplan - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for mdelay */ -#include -#include - -#include -#include -#include - -/* number of characters we can transmit to the SAL console at a time */ -#define SN_SAL_MAX_CHARS 120 - -/* 64K, when we're asynch, it must be at least printk's LOG_BUF_LEN to - * avoid losing chars, (always has to be a power of 2) */ -#define SN_SAL_BUFFER_SIZE (64 * (1 << 10)) - -#define SN_SAL_UART_FIFO_DEPTH 16 -#define SN_SAL_UART_FIFO_SPEED_CPS (9600/10) - -/* sn_transmit_chars() calling args */ -#define TRANSMIT_BUFFERED 0 -#define TRANSMIT_RAW 1 - -/* To use dynamic numbers only and not use the assigned major and minor, - * define the following.. */ - /* #define USE_DYNAMIC_MINOR 1 *//* use dynamic minor number */ -#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */ - -/* Device name we're using */ -#define DEVICE_NAME "ttySG" -#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */ -/* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */ -#define DEVICE_MAJOR 204 -#define DEVICE_MINOR 40 - -#ifdef CONFIG_MAGIC_SYSRQ -static char sysrq_serial_str[] = "\eSYS"; -static char *sysrq_serial_ptr = sysrq_serial_str; -static unsigned long sysrq_requested; -#endif /* CONFIG_MAGIC_SYSRQ */ - -/* - * Port definition - this kinda drives it all - */ -struct sn_cons_port { - struct timer_list sc_timer; - struct uart_port sc_port; - struct sn_sal_ops { - int (*sal_puts_raw) (const char *s, int len); - int (*sal_puts) (const char *s, int len); - int (*sal_getc) (void); - int (*sal_input_pending) (void); - void (*sal_wakeup_transmit) (struct sn_cons_port *, int); - } *sc_ops; - unsigned long sc_interrupt_timeout; - int sc_is_asynch; -}; - -static struct sn_cons_port sal_console_port; -static int sn_process_input; - -/* Only used if USE_DYNAMIC_MINOR is set to 1 */ -static struct miscdevice misc; /* used with misc_register for dynamic */ - -extern void early_sn_setup(void); - -#undef DEBUG -#ifdef DEBUG -static int sn_debug_printf(const char *fmt, ...); -#define DPRINTF(x...) sn_debug_printf(x) -#else -#define DPRINTF(x...) do { } while (0) -#endif - -/* Prototypes */ -static int snt_hw_puts_raw(const char *, int); -static int snt_hw_puts_buffered(const char *, int); -static int snt_poll_getc(void); -static int snt_poll_input_pending(void); -static int snt_intr_getc(void); -static int snt_intr_input_pending(void); -static void sn_transmit_chars(struct sn_cons_port *, int); - -/* A table for polling: - */ -static struct sn_sal_ops poll_ops = { - .sal_puts_raw = snt_hw_puts_raw, - .sal_puts = snt_hw_puts_raw, - .sal_getc = snt_poll_getc, - .sal_input_pending = snt_poll_input_pending -}; - -/* A table for interrupts enabled */ -static struct sn_sal_ops intr_ops = { - .sal_puts_raw = snt_hw_puts_raw, - .sal_puts = snt_hw_puts_buffered, - .sal_getc = snt_intr_getc, - .sal_input_pending = snt_intr_input_pending, - .sal_wakeup_transmit = sn_transmit_chars -}; - -/* the console does output in two distinctly different ways: - * synchronous (raw) and asynchronous (buffered). initally, early_printk - * does synchronous output. any data written goes directly to the SAL - * to be output (incidentally, it is internally buffered by the SAL) - * after interrupts and timers are initialized and available for use, - * the console init code switches to asynchronous output. this is - * also the earliest opportunity to begin polling for console input. - * after console initialization, console output and tty (serial port) - * output is buffered and sent to the SAL asynchronously (either by - * timer callback or by UART interrupt) */ - -/* routines for running the console in polling mode */ - -/** - * snt_poll_getc - Get a character from the console in polling mode - * - */ -static int snt_poll_getc(void) -{ - int ch; - - ia64_sn_console_getc(&ch); - return ch; -} - -/** - * snt_poll_input_pending - Check if any input is waiting - polling mode. - * - */ -static int snt_poll_input_pending(void) -{ - int status, input; - - status = ia64_sn_console_check(&input); - return !status && input; -} - -/* routines for an interrupt driven console (normal) */ - -/** - * snt_intr_getc - Get a character from the console, interrupt mode - * - */ -static int snt_intr_getc(void) -{ - return ia64_sn_console_readc(); -} - -/** - * snt_intr_input_pending - Check if input is pending, interrupt mode - * - */ -static int snt_intr_input_pending(void) -{ - return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV; -} - -/* these functions are polled and interrupt */ - -/** - * snt_hw_puts_raw - Send raw string to the console, polled or interrupt mode - * @s: String - * @len: Length - * - */ -static int snt_hw_puts_raw(const char *s, int len) -{ - /* this will call the PROM and not return until this is done */ - return ia64_sn_console_putb(s, len); -} - -/** - * snt_hw_puts_buffered - Send string to console, polled or interrupt mode - * @s: String - * @len: Length - * - */ -static int snt_hw_puts_buffered(const char *s, int len) -{ - /* queue data to the PROM */ - return ia64_sn_console_xmit_chars((char *)s, len); -} - -/* uart interface structs - * These functions are associated with the uart_port that the serial core - * infrastructure calls. - * - * Note: Due to how the console works, many routines are no-ops. - */ - -/** - * snp_type - What type of console are we? - * @port: Port to operate with (we ignore since we only have one port) - * - */ -static const char *snp_type(struct uart_port *port) -{ - return ("SGI SN L1"); -} - -/** - * snp_tx_empty - Is the transmitter empty? We pretend we're always empty - * @port: Port to operate on (we ignore since we only have one port) - * - */ -static unsigned int snp_tx_empty(struct uart_port *port) -{ - return 1; -} - -/** - * snp_stop_tx - stop the transmitter - no-op for us - * @port: Port to operat eon - we ignore - no-op function - * - */ -static void snp_stop_tx(struct uart_port *port) -{ -} - -/** - * snp_release_port - Free i/o and resources for port - no-op for us - * @port: Port to operate on - we ignore - no-op function - * - */ -static void snp_release_port(struct uart_port *port) -{ -} - -/** - * snp_enable_ms - Force modem status interrupts on - no-op for us - * @port: Port to operate on - we ignore - no-op function - * - */ -static void snp_enable_ms(struct uart_port *port) -{ -} - -/** - * snp_shutdown - shut down the port - free irq and disable - no-op for us - * @port: Port to shut down - we ignore - * - */ -static void snp_shutdown(struct uart_port *port) -{ -} - -/** - * snp_set_mctrl - set control lines (dtr, rts, etc) - no-op for our console - * @port: Port to operate on - we ignore - * @mctrl: Lines to set/unset - we ignore - * - */ -static void snp_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -/** - * snp_get_mctrl - get contorl line info, we just return a static value - * @port: port to operate on - we only have one port so we ignore this - * - */ -static unsigned int snp_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS; -} - -/** - * snp_stop_rx - Stop the receiver - we ignor ethis - * @port: Port to operate on - we ignore - * - */ -static void snp_stop_rx(struct uart_port *port) -{ -} - -/** - * snp_start_tx - Start transmitter - * @port: Port to operate on - * - */ -static void snp_start_tx(struct uart_port *port) -{ - if (sal_console_port.sc_ops->sal_wakeup_transmit) - sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port, - TRANSMIT_BUFFERED); - -} - -/** - * snp_break_ctl - handle breaks - ignored by us - * @port: Port to operate on - * @break_state: Break state - * - */ -static void snp_break_ctl(struct uart_port *port, int break_state) -{ -} - -/** - * snp_startup - Start up the serial port - always return 0 (We're always on) - * @port: Port to operate on - * - */ -static int snp_startup(struct uart_port *port) -{ - return 0; -} - -/** - * snp_set_termios - set termios stuff - we ignore these - * @port: port to operate on - * @termios: New settings - * @termios: Old - * - */ -static void -snp_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ -} - -/** - * snp_request_port - allocate resources for port - ignored by us - * @port: port to operate on - * - */ -static int snp_request_port(struct uart_port *port) -{ - return 0; -} - -/** - * snp_config_port - allocate resources, set up - we ignore, we're always on - * @port: Port to operate on - * @flags: flags used for port setup - * - */ -static void snp_config_port(struct uart_port *port, int flags) -{ -} - -/* Associate the uart functions above - given to serial core */ - -static struct uart_ops sn_console_ops = { - .tx_empty = snp_tx_empty, - .set_mctrl = snp_set_mctrl, - .get_mctrl = snp_get_mctrl, - .stop_tx = snp_stop_tx, - .start_tx = snp_start_tx, - .stop_rx = snp_stop_rx, - .enable_ms = snp_enable_ms, - .break_ctl = snp_break_ctl, - .startup = snp_startup, - .shutdown = snp_shutdown, - .set_termios = snp_set_termios, - .pm = NULL, - .type = snp_type, - .release_port = snp_release_port, - .request_port = snp_request_port, - .config_port = snp_config_port, - .verify_port = NULL, -}; - -/* End of uart struct functions and defines */ - -#ifdef DEBUG - -/** - * sn_debug_printf - close to hardware debugging printf - * @fmt: printf format - * - * This is as "close to the metal" as we can get, used when the driver - * itself may be broken. - * - */ -static int sn_debug_printf(const char *fmt, ...) -{ - static char printk_buf[1024]; - int printed_len; - va_list args; - - va_start(args, fmt); - printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args); - - if (!sal_console_port.sc_ops) { - sal_console_port.sc_ops = &poll_ops; - early_sn_setup(); - } - sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len); - - va_end(args); - return printed_len; -} -#endif /* DEBUG */ - -/* - * Interrupt handling routines. - */ - -/** - * sn_receive_chars - Grab characters, pass them to tty layer - * @port: Port to operate on - * @flags: irq flags - * - * Note: If we're not registered with the serial core infrastructure yet, - * we don't try to send characters to it... - * - */ -static void -sn_receive_chars(struct sn_cons_port *port, unsigned long flags) -{ - int ch; - struct tty_struct *tty; - - if (!port) { - printk(KERN_ERR "sn_receive_chars - port NULL so can't receieve\n"); - return; - } - - if (!port->sc_ops) { - printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receieve\n"); - return; - } - - if (port->sc_port.state) { - /* The serial_core stuffs are initialized, use them */ - tty = port->sc_port.state->port.tty; - } - else { - /* Not registered yet - can't pass to tty layer. */ - tty = NULL; - } - - while (port->sc_ops->sal_input_pending()) { - ch = port->sc_ops->sal_getc(); - if (ch < 0) { - printk(KERN_ERR "sn_console: An error occured while " - "obtaining data from the console (0x%0x)\n", ch); - break; - } -#ifdef CONFIG_MAGIC_SYSRQ - if (sysrq_requested) { - unsigned long sysrq_timeout = sysrq_requested + HZ*5; - - sysrq_requested = 0; - if (ch && time_before(jiffies, sysrq_timeout)) { - spin_unlock_irqrestore(&port->sc_port.lock, flags); - handle_sysrq(ch); - spin_lock_irqsave(&port->sc_port.lock, flags); - /* ignore actual sysrq command char */ - continue; - } - } - if (ch == *sysrq_serial_ptr) { - if (!(*++sysrq_serial_ptr)) { - sysrq_requested = jiffies; - sysrq_serial_ptr = sysrq_serial_str; - } - /* - * ignore the whole sysrq string except for the - * leading escape - */ - if (ch != '\e') - continue; - } - else - sysrq_serial_ptr = sysrq_serial_str; -#endif /* CONFIG_MAGIC_SYSRQ */ - - /* record the character to pass up to the tty layer */ - if (tty) { - if(tty_insert_flip_char(tty, ch, TTY_NORMAL) == 0) - break; - } - port->sc_port.icount.rx++; - } - - if (tty) - tty_flip_buffer_push(tty); -} - -/** - * sn_transmit_chars - grab characters from serial core, send off - * @port: Port to operate on - * @raw: Transmit raw or buffered - * - * Note: If we're early, before we're registered with serial core, the - * writes are going through sn_sal_console_write because that's how - * register_console has been set up. We currently could have asynch - * polls calling this function due to sn_sal_switch_to_asynch but we can - * ignore them until we register with the serial core stuffs. - * - */ -static void sn_transmit_chars(struct sn_cons_port *port, int raw) -{ - int xmit_count, tail, head, loops, ii; - int result; - char *start; - struct circ_buf *xmit; - - if (!port) - return; - - BUG_ON(!port->sc_is_asynch); - - if (port->sc_port.state) { - /* We're initialized, using serial core infrastructure */ - xmit = &port->sc_port.state->xmit; - } else { - /* Probably sn_sal_switch_to_asynch has been run but serial core isn't - * initialized yet. Just return. Writes are going through - * sn_sal_console_write (due to register_console) at this time. - */ - return; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) { - /* Nothing to do. */ - ia64_sn_console_intr_disable(SAL_CONSOLE_INTR_XMIT); - return; - } - - head = xmit->head; - tail = xmit->tail; - start = &xmit->buf[tail]; - - /* twice around gets the tail to the end of the buffer and - * then to the head, if needed */ - loops = (head < tail) ? 2 : 1; - - for (ii = 0; ii < loops; ii++) { - xmit_count = (head < tail) ? - (UART_XMIT_SIZE - tail) : (head - tail); - - if (xmit_count > 0) { - if (raw == TRANSMIT_RAW) - result = - port->sc_ops->sal_puts_raw(start, - xmit_count); - else - result = - port->sc_ops->sal_puts(start, xmit_count); -#ifdef DEBUG - if (!result) - DPRINTF("`"); -#endif - if (result > 0) { - xmit_count -= result; - port->sc_port.icount.tx += result; - tail += result; - tail &= UART_XMIT_SIZE - 1; - xmit->tail = tail; - start = &xmit->buf[tail]; - } - } - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&port->sc_port); - - if (uart_circ_empty(xmit)) - snp_stop_tx(&port->sc_port); /* no-op for us */ -} - -/** - * sn_sal_interrupt - Handle console interrupts - * @irq: irq #, useful for debug statements - * @dev_id: our pointer to our port (sn_cons_port which contains the uart port) - * - */ -static irqreturn_t sn_sal_interrupt(int irq, void *dev_id) -{ - struct sn_cons_port *port = (struct sn_cons_port *)dev_id; - unsigned long flags; - int status = ia64_sn_console_intr_status(); - - if (!port) - return IRQ_NONE; - - spin_lock_irqsave(&port->sc_port.lock, flags); - if (status & SAL_CONSOLE_INTR_RECV) { - sn_receive_chars(port, flags); - } - if (status & SAL_CONSOLE_INTR_XMIT) { - sn_transmit_chars(port, TRANSMIT_BUFFERED); - } - spin_unlock_irqrestore(&port->sc_port.lock, flags); - return IRQ_HANDLED; -} - -/** - * sn_sal_timer_poll - this function handles polled console mode - * @data: A pointer to our sn_cons_port (which contains the uart port) - * - * data is the pointer that init_timer will store for us. This function is - * associated with init_timer to see if there is any console traffic. - * Obviously not used in interrupt mode - * - */ -static void sn_sal_timer_poll(unsigned long data) -{ - struct sn_cons_port *port = (struct sn_cons_port *)data; - unsigned long flags; - - if (!port) - return; - - if (!port->sc_port.irq) { - spin_lock_irqsave(&port->sc_port.lock, flags); - if (sn_process_input) - sn_receive_chars(port, flags); - sn_transmit_chars(port, TRANSMIT_RAW); - spin_unlock_irqrestore(&port->sc_port.lock, flags); - mod_timer(&port->sc_timer, - jiffies + port->sc_interrupt_timeout); - } -} - -/* - * Boot-time initialization code - */ - -/** - * sn_sal_switch_to_asynch - Switch to async mode (as opposed to synch) - * @port: Our sn_cons_port (which contains the uart port) - * - * So this is used by sn_sal_serial_console_init (early on, before we're - * registered with serial core). It's also used by sn_sal_module_init - * right after we've registered with serial core. The later only happens - * if we didn't already come through here via sn_sal_serial_console_init. - * - */ -static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port) -{ - unsigned long flags; - - if (!port) - return; - - DPRINTF("sn_console: about to switch to asynchronous console\n"); - - /* without early_printk, we may be invoked late enough to race - * with other cpus doing console IO at this point, however - * console interrupts will never be enabled */ - spin_lock_irqsave(&port->sc_port.lock, flags); - - /* early_printk invocation may have done this for us */ - if (!port->sc_ops) - port->sc_ops = &poll_ops; - - /* we can't turn on the console interrupt (as request_irq - * calls kmalloc, which isn't set up yet), so we rely on a - * timer to poll for input and push data from the console - * buffer. - */ - init_timer(&port->sc_timer); - port->sc_timer.function = sn_sal_timer_poll; - port->sc_timer.data = (unsigned long)port; - - if (IS_RUNNING_ON_SIMULATOR()) - port->sc_interrupt_timeout = 6; - else { - /* 960cps / 16 char FIFO = 60HZ - * HZ / (SN_SAL_FIFO_SPEED_CPS / SN_SAL_FIFO_DEPTH) */ - port->sc_interrupt_timeout = - HZ * SN_SAL_UART_FIFO_DEPTH / SN_SAL_UART_FIFO_SPEED_CPS; - } - mod_timer(&port->sc_timer, jiffies + port->sc_interrupt_timeout); - - port->sc_is_asynch = 1; - spin_unlock_irqrestore(&port->sc_port.lock, flags); -} - -/** - * sn_sal_switch_to_interrupts - Switch to interrupt driven mode - * @port: Our sn_cons_port (which contains the uart port) - * - * In sn_sal_module_init, after we're registered with serial core and - * the port is added, this function is called to switch us to interrupt - * mode. We were previously in asynch/polling mode (using init_timer). - * - * We attempt to switch to interrupt mode here by calling - * request_irq. If that works out, we enable receive interrupts. - */ -static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) -{ - unsigned long flags; - - if (port) { - DPRINTF("sn_console: switching to interrupt driven console\n"); - - if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, - IRQF_DISABLED | IRQF_SHARED, - "SAL console driver", port) >= 0) { - spin_lock_irqsave(&port->sc_port.lock, flags); - port->sc_port.irq = SGI_UART_VECTOR; - port->sc_ops = &intr_ops; - - /* turn on receive interrupts */ - ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV); - spin_unlock_irqrestore(&port->sc_port.lock, flags); - } - else { - printk(KERN_INFO - "sn_console: console proceeding in polled mode\n"); - } - } -} - -/* - * Kernel console definitions - */ - -static void sn_sal_console_write(struct console *, const char *, unsigned); -static int sn_sal_console_setup(struct console *, char *); -static struct uart_driver sal_console_uart; -extern struct tty_driver *uart_console_device(struct console *, int *); - -static struct console sal_console = { - .name = DEVICE_NAME, - .write = sn_sal_console_write, - .device = uart_console_device, - .setup = sn_sal_console_setup, - .index = -1, /* unspecified */ - .data = &sal_console_uart, -}; - -#define SAL_CONSOLE &sal_console - -static struct uart_driver sal_console_uart = { - .owner = THIS_MODULE, - .driver_name = "sn_console", - .dev_name = DEVICE_NAME, - .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */ - .minor = 0, - .nr = 1, /* one port */ - .cons = SAL_CONSOLE, -}; - -/** - * sn_sal_module_init - When the kernel loads us, get us rolling w/ serial core - * - * Before this is called, we've been printing kernel messages in a special - * early mode not making use of the serial core infrastructure. When our - * driver is loaded for real, we register the driver and port with serial - * core and try to enable interrupt driven mode. - * - */ -static int __init sn_sal_module_init(void) -{ - int retval; - - if (!ia64_platform_is("sn2")) - return 0; - - printk(KERN_INFO "sn_console: Console driver init\n"); - - if (USE_DYNAMIC_MINOR == 1) { - misc.minor = MISC_DYNAMIC_MINOR; - misc.name = DEVICE_NAME_DYNAMIC; - retval = misc_register(&misc); - if (retval != 0) { - printk(KERN_WARNING "Failed to register console " - "device using misc_register.\n"); - return -ENODEV; - } - sal_console_uart.major = MISC_MAJOR; - sal_console_uart.minor = misc.minor; - } else { - sal_console_uart.major = DEVICE_MAJOR; - sal_console_uart.minor = DEVICE_MINOR; - } - - /* We register the driver and the port before switching to interrupts - * or async above so the proper uart structures are populated */ - - if (uart_register_driver(&sal_console_uart) < 0) { - printk - ("ERROR sn_sal_module_init failed uart_register_driver, line %d\n", - __LINE__); - return -ENODEV; - } - - spin_lock_init(&sal_console_port.sc_port.lock); - - /* Setup the port struct with the minimum needed */ - sal_console_port.sc_port.membase = (char *)1; /* just needs to be non-zero */ - sal_console_port.sc_port.type = PORT_16550A; - sal_console_port.sc_port.fifosize = SN_SAL_MAX_CHARS; - sal_console_port.sc_port.ops = &sn_console_ops; - sal_console_port.sc_port.line = 0; - - if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) { - /* error - not sure what I'd do - so I'll do nothing */ - printk(KERN_ERR "%s: unable to add port\n", __func__); - } - - /* when this driver is compiled in, the console initialization - * will have already switched us into asynchronous operation - * before we get here through the module initcalls */ - if (!sal_console_port.sc_is_asynch) { - sn_sal_switch_to_asynch(&sal_console_port); - } - - /* at this point (module_init) we can try to turn on interrupts */ - if (!IS_RUNNING_ON_SIMULATOR()) { - sn_sal_switch_to_interrupts(&sal_console_port); - } - sn_process_input = 1; - return 0; -} - -/** - * sn_sal_module_exit - When we're unloaded, remove the driver/port - * - */ -static void __exit sn_sal_module_exit(void) -{ - del_timer_sync(&sal_console_port.sc_timer); - uart_remove_one_port(&sal_console_uart, &sal_console_port.sc_port); - uart_unregister_driver(&sal_console_uart); - misc_deregister(&misc); -} - -module_init(sn_sal_module_init); -module_exit(sn_sal_module_exit); - -/** - * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required - * @puts_raw : puts function to do the writing - * @s: input string - * @count: length - * - * We need a \r ahead of every \n for direct writes through - * ia64_sn_console_putb (what sal_puts_raw below actually does). - * - */ - -static void puts_raw_fixed(int (*puts_raw) (const char *s, int len), - const char *s, int count) -{ - const char *s1; - - /* Output '\r' before each '\n' */ - while ((s1 = memchr(s, '\n', count)) != NULL) { - puts_raw(s, s1 - s); - puts_raw("\r\n", 2); - count -= s1 + 1 - s; - s = s1 + 1; - } - puts_raw(s, count); -} - -/** - * sn_sal_console_write - Print statements before serial core available - * @console: Console to operate on - we ignore since we have just one - * @s: String to send - * @count: length - * - * This is referenced in the console struct. It is used for early - * console printing before we register with serial core and for things - * such as kdb. The console_lock must be held when we get here. - * - * This function has some code for trying to print output even if the lock - * is held. We try to cover the case where a lock holder could have died. - * We don't use this special case code if we're not registered with serial - * core yet. After we're registered with serial core, the only time this - * function would be used is for high level kernel output like magic sys req, - * kdb, and printk's. - */ -static void -sn_sal_console_write(struct console *co, const char *s, unsigned count) -{ - unsigned long flags = 0; - struct sn_cons_port *port = &sal_console_port; - static int stole_lock = 0; - - BUG_ON(!port->sc_is_asynch); - - /* We can't look at the xmit buffer if we're not registered with serial core - * yet. So only do the fancy recovery after registering - */ - if (!port->sc_port.state) { - /* Not yet registered with serial core - simple case */ - puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - return; - } - - /* somebody really wants this output, might be an - * oops, kdb, panic, etc. make sure they get it. */ - if (spin_is_locked(&port->sc_port.lock)) { - int lhead = port->sc_port.state->xmit.head; - int ltail = port->sc_port.state->xmit.tail; - int counter, got_lock = 0; - - /* - * We attempt to determine if someone has died with the - * lock. We wait ~20 secs after the head and tail ptrs - * stop moving and assume the lock holder is not functional - * and plow ahead. If the lock is freed within the time out - * period we re-get the lock and go ahead normally. We also - * remember if we have plowed ahead so that we don't have - * to wait out the time out period again - the asumption - * is that we will time out again. - */ - - for (counter = 0; counter < 150; mdelay(125), counter++) { - if (!spin_is_locked(&port->sc_port.lock) - || stole_lock) { - if (!stole_lock) { - spin_lock_irqsave(&port->sc_port.lock, - flags); - got_lock = 1; - } - break; - } else { - /* still locked */ - if ((lhead != port->sc_port.state->xmit.head) - || (ltail != - port->sc_port.state->xmit.tail)) { - lhead = - port->sc_port.state->xmit.head; - ltail = - port->sc_port.state->xmit.tail; - counter = 0; - } - } - } - /* flush anything in the serial core xmit buffer, raw */ - sn_transmit_chars(port, 1); - if (got_lock) { - spin_unlock_irqrestore(&port->sc_port.lock, flags); - stole_lock = 0; - } else { - /* fell thru */ - stole_lock = 1; - } - puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - } else { - stole_lock = 0; - spin_lock_irqsave(&port->sc_port.lock, flags); - sn_transmit_chars(port, 1); - spin_unlock_irqrestore(&port->sc_port.lock, flags); - - puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); - } -} - - -/** - * sn_sal_console_setup - Set up console for early printing - * @co: Console to work with - * @options: Options to set - * - * Altix console doesn't do anything with baud rates, etc, anyway. - * - * This isn't required since not providing the setup function in the - * console struct is ok. However, other patches like KDB plop something - * here so providing it is easier. - * - */ -static int sn_sal_console_setup(struct console *co, char *options) -{ - return 0; -} - -/** - * sn_sal_console_write_early - simple early output routine - * @co - console struct - * @s - string to print - * @count - count - * - * Simple function to provide early output, before even - * sn_sal_serial_console_init is called. Referenced in the - * console struct registerd in sn_serial_console_early_setup. - * - */ -static void __init -sn_sal_console_write_early(struct console *co, const char *s, unsigned count) -{ - puts_raw_fixed(sal_console_port.sc_ops->sal_puts_raw, s, count); -} - -/* Used for very early console printing - again, before - * sn_sal_serial_console_init is run */ -static struct console sal_console_early __initdata = { - .name = "sn_sal", - .write = sn_sal_console_write_early, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -/** - * sn_serial_console_early_setup - Sets up early console output support - * - * Register a console early on... This is for output before even - * sn_sal_serial_cosnole_init is called. This function is called from - * setup.c. This allows us to do really early polled writes. When - * sn_sal_serial_console_init is called, this console is unregistered - * and a new one registered. - */ -int __init sn_serial_console_early_setup(void) -{ - if (!ia64_platform_is("sn2")) - return -1; - - sal_console_port.sc_ops = &poll_ops; - spin_lock_init(&sal_console_port.sc_port.lock); - early_sn_setup(); /* Find SAL entry points */ - register_console(&sal_console_early); - - return 0; -} - -/** - * sn_sal_serial_console_init - Early console output - set up for register - * - * This function is called when regular console init happens. Because we - * support even earlier console output with sn_serial_console_early_setup - * (called from setup.c directly), this function unregisters the really - * early console. - * - * Note: Even if setup.c doesn't register sal_console_early, unregistering - * it here doesn't hurt anything. - * - */ -static int __init sn_sal_serial_console_init(void) -{ - if (ia64_platform_is("sn2")) { - sn_sal_switch_to_asynch(&sal_console_port); - DPRINTF("sn_sal_serial_console_init : register console\n"); - register_console(&sal_console); - unregister_console(&sal_console_early); - } - return 0; -} - -console_initcall(sn_sal_serial_console_init); diff --git a/drivers/serial/suncore.c b/drivers/serial/suncore.c deleted file mode 100644 index 6381a0282ee7..000000000000 --- a/drivers/serial/suncore.c +++ /dev/null @@ -1,247 +0,0 @@ -/* suncore.c - * - * Common SUN serial routines. Based entirely - * upon drivers/sbus/char/sunserial.c which is: - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Adaptation to new UART layer is: - * - * Copyright (C) 2002 David S. Miller (davem@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "suncore.h" - -static int sunserial_current_minor = 64; - -int sunserial_register_minors(struct uart_driver *drv, int count) -{ - int err = 0; - - drv->minor = sunserial_current_minor; - drv->nr += count; - /* Register the driver on the first call */ - if (drv->nr == count) - err = uart_register_driver(drv); - if (err == 0) { - sunserial_current_minor += count; - drv->tty_driver->name_base = drv->minor - 64; - } - return err; -} -EXPORT_SYMBOL(sunserial_register_minors); - -void sunserial_unregister_minors(struct uart_driver *drv, int count) -{ - drv->nr -= count; - sunserial_current_minor -= count; - - if (drv->nr == 0) - uart_unregister_driver(drv); -} -EXPORT_SYMBOL(sunserial_unregister_minors); - -int sunserial_console_match(struct console *con, struct device_node *dp, - struct uart_driver *drv, int line, bool ignore_line) -{ - if (!con) - return 0; - - drv->cons = con; - - if (of_console_device != dp) - return 0; - - if (!ignore_line) { - int off = 0; - - if (of_console_options && - *of_console_options == 'b') - off = 1; - - if ((line & 1) != off) - return 0; - } - - if (!console_set_on_cmdline) { - con->index = line; - add_preferred_console(con->name, line, NULL); - } - return 1; -} -EXPORT_SYMBOL(sunserial_console_match); - -void sunserial_console_termios(struct console *con, struct device_node *uart_dp) -{ - const char *mode, *s; - char mode_prop[] = "ttyX-mode"; - int baud, bits, stop, cflag; - char parity; - - if (!strcmp(uart_dp->name, "rsc") || - !strcmp(uart_dp->name, "rsc-console") || - !strcmp(uart_dp->name, "rsc-control")) { - mode = of_get_property(uart_dp, - "ssp-console-modes", NULL); - if (!mode) - mode = "115200,8,n,1,-"; - } else if (!strcmp(uart_dp->name, "lom-console")) { - mode = "9600,8,n,1,-"; - } else { - struct device_node *dp; - char c; - - c = 'a'; - if (of_console_options) - c = *of_console_options; - - mode_prop[3] = c; - - dp = of_find_node_by_path("/options"); - mode = of_get_property(dp, mode_prop, NULL); - if (!mode) - mode = "9600,8,n,1,-"; - } - - cflag = CREAD | HUPCL | CLOCAL; - - s = mode; - baud = simple_strtoul(s, NULL, 0); - s = strchr(s, ','); - bits = simple_strtoul(++s, NULL, 0); - s = strchr(s, ','); - parity = *(++s); - s = strchr(s, ','); - stop = simple_strtoul(++s, NULL, 0); - s = strchr(s, ','); - /* XXX handshake is not handled here. */ - - switch (baud) { - case 150: cflag |= B150; break; - case 300: cflag |= B300; break; - case 600: cflag |= B600; break; - case 1200: cflag |= B1200; break; - case 2400: cflag |= B2400; break; - case 4800: cflag |= B4800; break; - case 9600: cflag |= B9600; break; - case 19200: cflag |= B19200; break; - case 38400: cflag |= B38400; break; - case 57600: cflag |= B57600; break; - case 115200: cflag |= B115200; break; - case 230400: cflag |= B230400; break; - case 460800: cflag |= B460800; break; - default: baud = 9600; cflag |= B9600; break; - } - - switch (bits) { - case 5: cflag |= CS5; break; - case 6: cflag |= CS6; break; - case 7: cflag |= CS7; break; - case 8: cflag |= CS8; break; - default: cflag |= CS8; break; - } - - switch (parity) { - case 'o': cflag |= (PARENB | PARODD); break; - case 'e': cflag |= PARENB; break; - case 'n': default: break; - } - - switch (stop) { - case 2: cflag |= CSTOPB; break; - case 1: default: break; - } - - con->cflag = cflag; -} - -/* Sun serial MOUSE auto baud rate detection. */ -static struct mouse_baud_cflag { - int baud; - unsigned int cflag; -} mouse_baud_table[] = { - { 1200, B1200 }, - { 2400, B2400 }, - { 4800, B4800 }, - { 9600, B9600 }, - { -1, ~0 }, - { -1, ~0 }, -}; - -unsigned int suncore_mouse_baud_cflag_next(unsigned int cflag, int *new_baud) -{ - int i; - - for (i = 0; mouse_baud_table[i].baud != -1; i++) - if (mouse_baud_table[i].cflag == (cflag & CBAUD)) - break; - - i += 1; - if (mouse_baud_table[i].baud == -1) - i = 0; - - *new_baud = mouse_baud_table[i].baud; - return mouse_baud_table[i].cflag; -} - -EXPORT_SYMBOL(suncore_mouse_baud_cflag_next); - -/* Basically, when the baud rate is wrong the mouse spits out - * breaks to us. - */ -int suncore_mouse_baud_detection(unsigned char ch, int is_break) -{ - static int mouse_got_break = 0; - static int ctr = 0; - - if (is_break) { - /* Let a few normal bytes go by before we jump the gun - * and say we need to try another baud rate. - */ - if (mouse_got_break && ctr < 8) - return 1; - - /* Ok, we need to try another baud. */ - ctr = 0; - mouse_got_break = 1; - return 2; - } - if (mouse_got_break) { - ctr++; - if (ch == 0x87) { - /* Correct baud rate determined. */ - mouse_got_break = 0; - } - return 1; - } - return 0; -} - -EXPORT_SYMBOL(suncore_mouse_baud_detection); - -static int __init suncore_init(void) -{ - return 0; -} - -static void __exit suncore_exit(void) -{ -} - -module_init(suncore_init); -module_exit(suncore_exit); - -MODULE_AUTHOR("Eddie C. Dost, David S. Miller"); -MODULE_DESCRIPTION("Sun serial common layer"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/suncore.h b/drivers/serial/suncore.h deleted file mode 100644 index db2057936c31..000000000000 --- a/drivers/serial/suncore.h +++ /dev/null @@ -1,33 +0,0 @@ -/* suncore.h - * - * Generic SUN serial/kbd/ms layer. Based entirely - * upon drivers/sbus/char/sunserial.h which is: - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Port to new UART layer is: - * - * Copyright (C) 2002 David S. Miller (davem@redhat.com) - */ - -#ifndef _SERIAL_SUN_H -#define _SERIAL_SUN_H - -/* Serial keyboard defines for L1-A processing... */ -#define SUNKBD_RESET 0xff -#define SUNKBD_L1 0x01 -#define SUNKBD_UP 0x80 -#define SUNKBD_A 0x4d - -extern unsigned int suncore_mouse_baud_cflag_next(unsigned int, int *); -extern int suncore_mouse_baud_detection(unsigned char, int); - -extern int sunserial_register_minors(struct uart_driver *, int); -extern void sunserial_unregister_minors(struct uart_driver *, int); - -extern int sunserial_console_match(struct console *, struct device_node *, - struct uart_driver *, int, bool); -extern void sunserial_console_termios(struct console *, - struct device_node *); - -#endif /* !(_SERIAL_SUN_H) */ diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c deleted file mode 100644 index c9014868297d..000000000000 --- a/drivers/serial/sunhv.c +++ /dev/null @@ -1,661 +0,0 @@ -/* sunhv.c: Serial driver for SUN4V hypervisor console. - * - * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#if defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#include "suncore.h" - -#define CON_BREAK ((long)-1) -#define CON_HUP ((long)-2) - -#define IGNORE_BREAK 0x1 -#define IGNORE_ALL 0x2 - -static char *con_write_page; -static char *con_read_page; - -static int hung_up = 0; - -static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) -{ - while (!uart_circ_empty(xmit)) { - long status = sun4v_con_putchar(xmit->buf[xmit->tail]); - - if (status != HV_EOK) - break; - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } -} - -static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) -{ - while (!uart_circ_empty(xmit)) { - unsigned long ra = __pa(xmit->buf + xmit->tail); - unsigned long len, status, sent; - - len = CIRC_CNT_TO_END(xmit->head, xmit->tail, - UART_XMIT_SIZE); - status = sun4v_con_write(ra, len, &sent); - if (status != HV_EOK) - break; - xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1); - port->icount.tx += sent; - } -} - -static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) -{ - int saw_console_brk = 0; - int limit = 10000; - - while (limit-- > 0) { - long status; - long c = sun4v_con_getchar(&status); - - if (status == HV_EWOULDBLOCK) - break; - - if (c == CON_BREAK) { - if (uart_handle_break(port)) - continue; - saw_console_brk = 1; - c = 0; - } - - if (c == CON_HUP) { - hung_up = 1; - uart_handle_dcd_change(port, 0); - } else if (hung_up) { - hung_up = 0; - uart_handle_dcd_change(port, 1); - } - - if (tty == NULL) { - uart_handle_sysrq_char(port, c); - continue; - } - - port->icount.rx++; - - if (uart_handle_sysrq_char(port, c)) - continue; - - tty_insert_flip_char(tty, c, TTY_NORMAL); - } - - return saw_console_brk; -} - -static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) -{ - int saw_console_brk = 0; - int limit = 10000; - - while (limit-- > 0) { - unsigned long ra = __pa(con_read_page); - unsigned long bytes_read, i; - long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read); - - if (stat != HV_EOK) { - bytes_read = 0; - - if (stat == CON_BREAK) { - if (uart_handle_break(port)) - continue; - saw_console_brk = 1; - *con_read_page = 0; - bytes_read = 1; - } else if (stat == CON_HUP) { - hung_up = 1; - uart_handle_dcd_change(port, 0); - continue; - } else { - /* HV_EWOULDBLOCK, etc. */ - break; - } - } - - if (hung_up) { - hung_up = 0; - uart_handle_dcd_change(port, 1); - } - - for (i = 0; i < bytes_read; i++) - uart_handle_sysrq_char(port, con_read_page[i]); - - if (tty == NULL) - continue; - - port->icount.rx += bytes_read; - - tty_insert_flip_string(tty, con_read_page, bytes_read); - } - - return saw_console_brk; -} - -struct sunhv_ops { - void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); - int (*receive_chars)(struct uart_port *port, struct tty_struct *tty); -}; - -static struct sunhv_ops bychar_ops = { - .transmit_chars = transmit_chars_putchar, - .receive_chars = receive_chars_getchar, -}; - -static struct sunhv_ops bywrite_ops = { - .transmit_chars = transmit_chars_write, - .receive_chars = receive_chars_read, -}; - -static struct sunhv_ops *sunhv_ops = &bychar_ops; - -static struct tty_struct *receive_chars(struct uart_port *port) -{ - struct tty_struct *tty = NULL; - - if (port->state != NULL) /* Unopened serial console */ - tty = port->state->port.tty; - - if (sunhv_ops->receive_chars(port, tty)) - sun_do_break(); - - return tty; -} - -static void transmit_chars(struct uart_port *port) -{ - struct circ_buf *xmit; - - if (!port->state) - return; - - xmit = &port->state->xmit; - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - return; - - sunhv_ops->transmit_chars(port, xmit); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); -} - -static irqreturn_t sunhv_interrupt(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - struct tty_struct *tty; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - tty = receive_chars(port); - transmit_chars(port); - spin_unlock_irqrestore(&port->lock, flags); - - if (tty) - tty_flip_buffer_push(tty); - - return IRQ_HANDLED; -} - -/* port->lock is not held. */ -static unsigned int sunhv_tx_empty(struct uart_port *port) -{ - /* Transmitter is always empty for us. If the circ buffer - * is non-empty or there is an x_char pending, our caller - * will do the right thing and ignore what we return here. - */ - return TIOCSER_TEMT; -} - -/* port->lock held by caller. */ -static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - return; -} - -/* port->lock is held by caller and interrupts are disabled. */ -static unsigned int sunhv_get_mctrl(struct uart_port *port) -{ - return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; -} - -/* port->lock held by caller. */ -static void sunhv_stop_tx(struct uart_port *port) -{ - return; -} - -/* port->lock held by caller. */ -static void sunhv_start_tx(struct uart_port *port) -{ - transmit_chars(port); -} - -/* port->lock is not held. */ -static void sunhv_send_xchar(struct uart_port *port, char ch) -{ - unsigned long flags; - int limit = 10000; - - spin_lock_irqsave(&port->lock, flags); - - while (limit-- > 0) { - long status = sun4v_con_putchar(ch); - if (status == HV_EOK) - break; - udelay(1); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* port->lock held by caller. */ -static void sunhv_stop_rx(struct uart_port *port) -{ -} - -/* port->lock held by caller. */ -static void sunhv_enable_ms(struct uart_port *port) -{ -} - -/* port->lock is not held. */ -static void sunhv_break_ctl(struct uart_port *port, int break_state) -{ - if (break_state) { - unsigned long flags; - int limit = 10000; - - spin_lock_irqsave(&port->lock, flags); - - while (limit-- > 0) { - long status = sun4v_con_putchar(CON_BREAK); - if (status == HV_EOK) - break; - udelay(1); - } - - spin_unlock_irqrestore(&port->lock, flags); - } -} - -/* port->lock is not held. */ -static int sunhv_startup(struct uart_port *port) -{ - return 0; -} - -/* port->lock is not held. */ -static void sunhv_shutdown(struct uart_port *port) -{ -} - -/* port->lock is not held. */ -static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - unsigned int quot = uart_get_divisor(port, baud); - unsigned int iflag, cflag; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - - iflag = termios->c_iflag; - cflag = termios->c_cflag; - - port->ignore_status_mask = 0; - if (iflag & IGNBRK) - port->ignore_status_mask |= IGNORE_BREAK; - if ((cflag & CREAD) == 0) - port->ignore_status_mask |= IGNORE_ALL; - - /* XXX */ - uart_update_timeout(port, cflag, - (port->uartclk / (16 * quot))); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *sunhv_type(struct uart_port *port) -{ - return "SUN4V HCONS"; -} - -static void sunhv_release_port(struct uart_port *port) -{ -} - -static int sunhv_request_port(struct uart_port *port) -{ - return 0; -} - -static void sunhv_config_port(struct uart_port *port, int flags) -{ -} - -static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static struct uart_ops sunhv_pops = { - .tx_empty = sunhv_tx_empty, - .set_mctrl = sunhv_set_mctrl, - .get_mctrl = sunhv_get_mctrl, - .stop_tx = sunhv_stop_tx, - .start_tx = sunhv_start_tx, - .send_xchar = sunhv_send_xchar, - .stop_rx = sunhv_stop_rx, - .enable_ms = sunhv_enable_ms, - .break_ctl = sunhv_break_ctl, - .startup = sunhv_startup, - .shutdown = sunhv_shutdown, - .set_termios = sunhv_set_termios, - .type = sunhv_type, - .release_port = sunhv_release_port, - .request_port = sunhv_request_port, - .config_port = sunhv_config_port, - .verify_port = sunhv_verify_port, -}; - -static struct uart_driver sunhv_reg = { - .owner = THIS_MODULE, - .driver_name = "sunhv", - .dev_name = "ttyS", - .major = TTY_MAJOR, -}; - -static struct uart_port *sunhv_port; - -/* Copy 's' into the con_write_page, decoding "\n" into - * "\r\n" along the way. We have to return two lengths - * because the caller needs to know how much to advance - * 's' and also how many bytes to output via con_write_page. - */ -static int fill_con_write_page(const char *s, unsigned int n, - unsigned long *page_bytes) -{ - const char *orig_s = s; - char *p = con_write_page; - int left = PAGE_SIZE; - - while (n--) { - if (*s == '\n') { - if (left < 2) - break; - *p++ = '\r'; - left--; - } else if (left < 1) - break; - *p++ = *s++; - left--; - } - *page_bytes = p - con_write_page; - return s - orig_s; -} - -static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n) -{ - struct uart_port *port = sunhv_port; - 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); - - while (n > 0) { - unsigned long ra = __pa(con_write_page); - unsigned long page_bytes; - unsigned int cpy = fill_con_write_page(s, n, - &page_bytes); - - n -= cpy; - s += cpy; - while (page_bytes > 0) { - unsigned long written; - int limit = 1000000; - - while (limit--) { - unsigned long stat; - - stat = sun4v_con_write(ra, page_bytes, - &written); - if (stat == HV_EOK) - break; - udelay(1); - } - if (limit < 0) - break; - page_bytes -= written; - ra += written; - } - } - - if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); -} - -static inline void sunhv_console_putchar(struct uart_port *port, char c) -{ - int limit = 1000000; - - while (limit-- > 0) { - long status = sun4v_con_putchar(c); - if (status == HV_EOK) - break; - udelay(1); - } -} - -static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n) -{ - struct uart_port *port = sunhv_port; - unsigned long flags; - int i, 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); - - for (i = 0; i < n; i++) { - if (*s == '\n') - sunhv_console_putchar(port, '\r'); - sunhv_console_putchar(port, *s++); - } - - if (locked) - spin_unlock(&port->lock); - local_irq_restore(flags); -} - -static struct console sunhv_console = { - .name = "ttyHV", - .write = sunhv_console_write_bychar, - .device = uart_console_device, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sunhv_reg, -}; - -static int __devinit hv_probe(struct platform_device *op, const struct of_device_id *match) -{ - struct uart_port *port; - unsigned long minor; - int err; - - if (op->archdata.irqs[0] == 0xffffffff) - return -ENODEV; - - port = kzalloc(sizeof(struct uart_port), GFP_KERNEL); - if (unlikely(!port)) - return -ENOMEM; - - minor = 1; - if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 && - minor >= 1) { - err = -ENOMEM; - con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!con_write_page) - goto out_free_port; - - con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!con_read_page) - goto out_free_con_write_page; - - sunhv_console.write = sunhv_console_write_paged; - sunhv_ops = &bywrite_ops; - } - - sunhv_port = port; - - port->line = 0; - port->ops = &sunhv_pops; - port->type = PORT_SUNHV; - port->uartclk = ( 29491200 / 16 ); /* arbitrary */ - - port->membase = (unsigned char __iomem *) __pa(port); - - port->irq = op->archdata.irqs[0]; - - port->dev = &op->dev; - - err = sunserial_register_minors(&sunhv_reg, 1); - if (err) - goto out_free_con_read_page; - - sunserial_console_match(&sunhv_console, op->dev.of_node, - &sunhv_reg, port->line, false); - - err = uart_add_one_port(&sunhv_reg, port); - if (err) - goto out_unregister_driver; - - err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port); - if (err) - goto out_remove_port; - - dev_set_drvdata(&op->dev, port); - - return 0; - -out_remove_port: - uart_remove_one_port(&sunhv_reg, port); - -out_unregister_driver: - sunserial_unregister_minors(&sunhv_reg, 1); - -out_free_con_read_page: - kfree(con_read_page); - -out_free_con_write_page: - kfree(con_write_page); - -out_free_port: - kfree(port); - sunhv_port = NULL; - return err; -} - -static int __devexit hv_remove(struct platform_device *dev) -{ - struct uart_port *port = dev_get_drvdata(&dev->dev); - - free_irq(port->irq, port); - - uart_remove_one_port(&sunhv_reg, port); - - sunserial_unregister_minors(&sunhv_reg, 1); - - kfree(port); - sunhv_port = NULL; - - dev_set_drvdata(&dev->dev, NULL); - - return 0; -} - -static const struct of_device_id hv_match[] = { - { - .name = "console", - .compatible = "qcn", - }, - { - .name = "console", - .compatible = "SUNW,sun4v-console", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, hv_match); - -static struct of_platform_driver hv_driver = { - .driver = { - .name = "hv", - .owner = THIS_MODULE, - .of_match_table = hv_match, - }, - .probe = hv_probe, - .remove = __devexit_p(hv_remove), -}; - -static int __init sunhv_init(void) -{ - if (tlb_type != hypervisor) - return -ENODEV; - - return of_register_platform_driver(&hv_driver); -} - -static void __exit sunhv_exit(void) -{ - of_unregister_platform_driver(&hv_driver); -} - -module_init(sunhv_init); -module_exit(sunhv_exit); - -MODULE_AUTHOR("David S. Miller"); -MODULE_DESCRIPTION("SUN4V Hypervisor console driver"); -MODULE_VERSION("2.0"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c deleted file mode 100644 index 5b246b18f42f..000000000000 --- a/drivers/serial/sunsab.c +++ /dev/null @@ -1,1152 +0,0 @@ -/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net) - * - * Rewrote buffer handling to use CIRC(Circular Buffer) macros. - * Maxim Krasnyanskiy - * - * Fixed to use tty_get_baud_rate, and to allow for arbitrary baud - * rates to be programmed into the UART. Also eliminated a lot of - * duplicated code in the console setup. - * Theodore Ts'o , 2001-Oct-12 - * - * Ported to new 2.5.x UART layer. - * David S. Miller - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#if defined(CONFIG_SERIAL_SUNSAB_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#include "suncore.h" -#include "sunsab.h" - -struct uart_sunsab_port { - struct uart_port port; /* Generic UART port */ - union sab82532_async_regs __iomem *regs; /* Chip registers */ - unsigned long irqflags; /* IRQ state flags */ - int dsr; /* Current DSR state */ - unsigned int cec_timeout; /* Chip poll timeout... */ - unsigned int tec_timeout; /* likewise */ - unsigned char interrupt_mask0;/* ISR0 masking */ - unsigned char interrupt_mask1;/* ISR1 masking */ - unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */ - unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */ - unsigned int gis_shift; - int type; /* SAB82532 version */ - - /* Setting configuration bits while the transmitter is active - * can cause garbage characters to get emitted by the chip. - * Therefore, we cache such writes here and do the real register - * write the next time the transmitter becomes idle. - */ - unsigned int cached_ebrg; - unsigned char cached_mode; - unsigned char cached_pvr; - unsigned char cached_dafo; -}; - -/* - * This assumes you have a 29.4912 MHz clock for your UART. - */ -#define SAB_BASE_BAUD ( 29491200 / 16 ) - -static char *sab82532_version[16] = { - "V1.0", "V2.0", "V3.2", "V(0x03)", - "V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)", - "V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)", - "V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)" -}; - -#define SAB82532_MAX_TEC_TIMEOUT 200000 /* 1 character time (at 50 baud) */ -#define SAB82532_MAX_CEC_TIMEOUT 50000 /* 2.5 TX CLKs (at 50 baud) */ - -#define SAB82532_RECV_FIFO_SIZE 32 /* Standard async fifo sizes */ -#define SAB82532_XMIT_FIFO_SIZE 32 - -static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up) -{ - int timeout = up->tec_timeout; - - while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout) - udelay(1); -} - -static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up) -{ - int timeout = up->cec_timeout; - - while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout) - udelay(1); -} - -static struct tty_struct * -receive_chars(struct uart_sunsab_port *up, - union sab82532_irq_status *stat) -{ - struct tty_struct *tty = NULL; - unsigned char buf[32]; - int saw_console_brk = 0; - int free_fifo = 0; - int count = 0; - int i; - - if (up->port.state != NULL) /* Unopened serial console */ - tty = up->port.state->port.tty; - - /* Read number of BYTES (Character + Status) available. */ - if (stat->sreg.isr0 & SAB82532_ISR0_RPF) { - count = SAB82532_RECV_FIFO_SIZE; - free_fifo++; - } - - if (stat->sreg.isr0 & SAB82532_ISR0_TCD) { - count = readb(&up->regs->r.rbcl) & (SAB82532_RECV_FIFO_SIZE - 1); - free_fifo++; - } - - /* Issue a FIFO read command in case we where idle. */ - if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { - sunsab_cec_wait(up); - writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr); - return tty; - } - - if (stat->sreg.isr0 & SAB82532_ISR0_RFO) - free_fifo++; - - /* Read the FIFO. */ - for (i = 0; i < count; i++) - buf[i] = readb(&up->regs->r.rfifo[i]); - - /* Issue Receive Message Complete command. */ - if (free_fifo) { - sunsab_cec_wait(up); - writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr); - } - - /* Count may be zero for BRK, so we check for it here */ - if ((stat->sreg.isr1 & SAB82532_ISR1_BRK) && - (up->port.line == up->port.cons->index)) - saw_console_brk = 1; - - for (i = 0; i < count; i++) { - unsigned char ch = buf[i], flag; - - if (tty == NULL) { - uart_handle_sysrq_char(&up->port, ch); - continue; - } - - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR | - SAB82532_ISR0_FERR | - SAB82532_ISR0_RFO)) || - unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) { - /* - * For statistics only - */ - if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { - stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR | - SAB82532_ISR0_FERR); - up->port.icount.brk++; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - continue; - } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR) - up->port.icount.parity++; - else if (stat->sreg.isr0 & SAB82532_ISR0_FERR) - up->port.icount.frame++; - if (stat->sreg.isr0 & SAB82532_ISR0_RFO) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ingored. - */ - stat->sreg.isr0 &= (up->port.read_status_mask & 0xff); - stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff); - - if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { - flag = TTY_BREAK; - } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR) - flag = TTY_PARITY; - else if (stat->sreg.isr0 & SAB82532_ISR0_FERR) - flag = TTY_FRAME; - } - - if (uart_handle_sysrq_char(&up->port, ch)) - continue; - - if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 && - (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0) - tty_insert_flip_char(tty, ch, flag); - if (stat->sreg.isr0 & SAB82532_ISR0_RFO) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - - if (saw_console_brk) - sun_do_break(); - - return tty; -} - -static void sunsab_stop_tx(struct uart_port *); -static void sunsab_tx_idle(struct uart_sunsab_port *); - -static void transmit_chars(struct uart_sunsab_port *up, - union sab82532_irq_status *stat) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int i; - - if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { - up->interrupt_mask1 |= SAB82532_IMR1_ALLS; - writeb(up->interrupt_mask1, &up->regs->w.imr1); - set_bit(SAB82532_ALLS, &up->irqflags); - } - -#if 0 /* bde@nwlink.com says this check causes problems */ - if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR)) - return; -#endif - - if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW)) - return; - - set_bit(SAB82532_XPR, &up->irqflags); - sunsab_tx_idle(up); - - if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - up->interrupt_mask1 |= SAB82532_IMR1_XPR; - writeb(up->interrupt_mask1, &up->regs->w.imr1); - return; - } - - up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); - writeb(up->interrupt_mask1, &up->regs->w.imr1); - clear_bit(SAB82532_ALLS, &up->irqflags); - - /* Stuff 32 bytes into Transmit FIFO. */ - clear_bit(SAB82532_XPR, &up->irqflags); - for (i = 0; i < up->port.fifosize; i++) { - writeb(xmit->buf[xmit->tail], - &up->regs->w.xfifo[i]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - /* Issue a Transmit Frame command. */ - sunsab_cec_wait(up); - writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) - sunsab_stop_tx(&up->port); -} - -static void check_status(struct uart_sunsab_port *up, - union sab82532_irq_status *stat) -{ - if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) - uart_handle_dcd_change(&up->port, - !(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD)); - - if (stat->sreg.isr1 & SAB82532_ISR1_CSC) - uart_handle_cts_change(&up->port, - (readb(&up->regs->r.star) & SAB82532_STAR_CTS)); - - if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) { - up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1; - up->port.icount.dsr++; - } - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); -} - -static irqreturn_t sunsab_interrupt(int irq, void *dev_id) -{ - struct uart_sunsab_port *up = dev_id; - struct tty_struct *tty; - union sab82532_irq_status status; - unsigned long flags; - unsigned char gis; - - spin_lock_irqsave(&up->port.lock, flags); - - status.stat = 0; - gis = readb(&up->regs->r.gis) >> up->gis_shift; - if (gis & 1) - status.sreg.isr0 = readb(&up->regs->r.isr0); - if (gis & 2) - status.sreg.isr1 = readb(&up->regs->r.isr1); - - tty = NULL; - if (status.stat) { - if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | - SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) || - (status.sreg.isr1 & SAB82532_ISR1_BRK)) - tty = receive_chars(up, &status); - if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || - (status.sreg.isr1 & SAB82532_ISR1_CSC)) - check_status(up, &status); - if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR)) - transmit_chars(up, &status); - } - - spin_unlock_irqrestore(&up->port.lock, flags); - - if (tty) - tty_flip_buffer_push(tty); - - return IRQ_HANDLED; -} - -/* port->lock is not held. */ -static unsigned int sunsab_tx_empty(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - int ret; - - /* Do not need a lock for a state test like this. */ - if (test_bit(SAB82532_ALLS, &up->irqflags)) - ret = TIOCSER_TEMT; - else - ret = 0; - - return ret; -} - -/* port->lock held by caller. */ -static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - - if (mctrl & TIOCM_RTS) { - up->cached_mode &= ~SAB82532_MODE_FRTS; - up->cached_mode |= SAB82532_MODE_RTS; - } else { - up->cached_mode |= (SAB82532_MODE_FRTS | - SAB82532_MODE_RTS); - } - if (mctrl & TIOCM_DTR) { - up->cached_pvr &= ~(up->pvr_dtr_bit); - } else { - up->cached_pvr |= up->pvr_dtr_bit; - } - - set_bit(SAB82532_REGS_PENDING, &up->irqflags); - if (test_bit(SAB82532_XPR, &up->irqflags)) - sunsab_tx_idle(up); -} - -/* port->lock is held by caller and interrupts are disabled. */ -static unsigned int sunsab_get_mctrl(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - unsigned char val; - unsigned int result; - - result = 0; - - val = readb(&up->regs->r.pvr); - result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR; - - val = readb(&up->regs->r.vstr); - result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR; - - val = readb(&up->regs->r.star); - result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0; - - return result; -} - -/* port->lock held by caller. */ -static void sunsab_stop_tx(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - - up->interrupt_mask1 |= SAB82532_IMR1_XPR; - writeb(up->interrupt_mask1, &up->regs->w.imr1); -} - -/* port->lock held by caller. */ -static void sunsab_tx_idle(struct uart_sunsab_port *up) -{ - if (test_bit(SAB82532_REGS_PENDING, &up->irqflags)) { - u8 tmp; - - clear_bit(SAB82532_REGS_PENDING, &up->irqflags); - writeb(up->cached_mode, &up->regs->rw.mode); - writeb(up->cached_pvr, &up->regs->rw.pvr); - writeb(up->cached_dafo, &up->regs->w.dafo); - - writeb(up->cached_ebrg & 0xff, &up->regs->w.bgr); - tmp = readb(&up->regs->rw.ccr2); - tmp &= ~0xc0; - tmp |= (up->cached_ebrg >> 2) & 0xc0; - writeb(tmp, &up->regs->rw.ccr2); - } -} - -/* port->lock held by caller. */ -static void sunsab_start_tx(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - struct circ_buf *xmit = &up->port.state->xmit; - int i; - - up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); - writeb(up->interrupt_mask1, &up->regs->w.imr1); - - if (!test_bit(SAB82532_XPR, &up->irqflags)) - return; - - clear_bit(SAB82532_ALLS, &up->irqflags); - clear_bit(SAB82532_XPR, &up->irqflags); - - for (i = 0; i < up->port.fifosize; i++) { - writeb(xmit->buf[xmit->tail], - &up->regs->w.xfifo[i]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } - - /* Issue a Transmit Frame command. */ - sunsab_cec_wait(up); - writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); -} - -/* port->lock is not held. */ -static void sunsab_send_xchar(struct uart_port *port, char ch) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - sunsab_tec_wait(up); - writeb(ch, &up->regs->w.tic); - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* port->lock held by caller. */ -static void sunsab_stop_rx(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - - up->interrupt_mask0 |= SAB82532_IMR0_TCD; - writeb(up->interrupt_mask1, &up->regs->w.imr0); -} - -/* port->lock held by caller. */ -static void sunsab_enable_ms(struct uart_port *port) -{ - /* For now we always receive these interrupts. */ -} - -/* port->lock is not held. */ -static void sunsab_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - unsigned long flags; - unsigned char val; - - spin_lock_irqsave(&up->port.lock, flags); - - val = up->cached_dafo; - if (break_state) - val |= SAB82532_DAFO_XBRK; - else - val &= ~SAB82532_DAFO_XBRK; - up->cached_dafo = val; - - set_bit(SAB82532_REGS_PENDING, &up->irqflags); - if (test_bit(SAB82532_XPR, &up->irqflags)) - sunsab_tx_idle(up); - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -/* port->lock is not held. */ -static int sunsab_startup(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - unsigned long flags; - unsigned char tmp; - int err = request_irq(up->port.irq, sunsab_interrupt, - IRQF_SHARED, "sab", up); - if (err) - return err; - - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Wait for any commands or immediate characters - */ - sunsab_cec_wait(up); - sunsab_tec_wait(up); - - /* - * Clear the FIFO buffers. - */ - writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr); - sunsab_cec_wait(up); - writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr); - - /* - * Clear the interrupt registers. - */ - (void) readb(&up->regs->r.isr0); - (void) readb(&up->regs->r.isr1); - - /* - * Now, initialize the UART - */ - writeb(0, &up->regs->w.ccr0); /* power-down */ - writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | - SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0); - writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1); - writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | - SAB82532_CCR2_TOE, &up->regs->w.ccr2); - writeb(0, &up->regs->w.ccr3); - writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4); - up->cached_mode = (SAB82532_MODE_RTS | SAB82532_MODE_FCTS | - SAB82532_MODE_RAC); - writeb(up->cached_mode, &up->regs->w.mode); - writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc); - - tmp = readb(&up->regs->rw.ccr0); - tmp |= SAB82532_CCR0_PU; /* power-up */ - writeb(tmp, &up->regs->rw.ccr0); - - /* - * Finally, enable interrupts - */ - up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | - SAB82532_IMR0_PLLA); - writeb(up->interrupt_mask0, &up->regs->w.imr0); - up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | - SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | - SAB82532_IMR1_CSC | SAB82532_IMR1_XON | - SAB82532_IMR1_XPR); - writeb(up->interrupt_mask1, &up->regs->w.imr1); - set_bit(SAB82532_ALLS, &up->irqflags); - set_bit(SAB82532_XPR, &up->irqflags); - - spin_unlock_irqrestore(&up->port.lock, flags); - - return 0; -} - -/* port->lock is not held. */ -static void sunsab_shutdown(struct uart_port *port) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - - /* Disable Interrupts */ - up->interrupt_mask0 = 0xff; - writeb(up->interrupt_mask0, &up->regs->w.imr0); - up->interrupt_mask1 = 0xff; - writeb(up->interrupt_mask1, &up->regs->w.imr1); - - /* Disable break condition */ - up->cached_dafo = readb(&up->regs->rw.dafo); - up->cached_dafo &= ~SAB82532_DAFO_XBRK; - writeb(up->cached_dafo, &up->regs->rw.dafo); - - /* Disable Receiver */ - up->cached_mode &= ~SAB82532_MODE_RAC; - writeb(up->cached_mode, &up->regs->rw.mode); - - /* - * XXX FIXME - * - * If the chip is powered down here the system hangs/crashes during - * reboot or shutdown. This needs to be investigated further, - * similar behaviour occurs in 2.4 when the driver is configured - * as a module only. One hint may be that data is sometimes - * transmitted at 9600 baud during shutdown (regardless of the - * speed the chip was configured for when the port was open). - */ -#if 0 - /* Power Down */ - tmp = readb(&up->regs->rw.ccr0); - tmp &= ~SAB82532_CCR0_PU; - writeb(tmp, &up->regs->rw.ccr0); -#endif - - spin_unlock_irqrestore(&up->port.lock, flags); - free_irq(up->port.irq, up); -} - -/* - * This is used to figure out the divisor speeds. - * - * The formula is: Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)), - * - * with 0 <= N < 64 and 0 <= M < 16 - */ - -static void calc_ebrg(int baud, int *n_ret, int *m_ret) -{ - int n, m; - - if (baud == 0) { - *n_ret = 0; - *m_ret = 0; - return; - } - - /* - * We scale numbers by 10 so that we get better accuracy - * without having to use floating point. Here we increment m - * until n is within the valid range. - */ - n = (SAB_BASE_BAUD * 10) / baud; - m = 0; - while (n >= 640) { - n = n / 2; - m++; - } - n = (n+5) / 10; - /* - * We try very hard to avoid speeds with M == 0 since they may - * not work correctly for XTAL frequences above 10 MHz. - */ - if ((m == 0) && ((n & 1) == 0)) { - n = n / 2; - m++; - } - *n_ret = n - 1; - *m_ret = m; -} - -/* Internal routine, port->lock is held and local interrupts are disabled. */ -static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag, - unsigned int iflag, unsigned int baud, - unsigned int quot) -{ - unsigned char dafo; - int bits, n, m; - - /* Byte size and parity */ - switch (cflag & CSIZE) { - case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break; - case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break; - case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break; - case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break; - /* Never happens, but GCC is too dumb to figure it out */ - default: dafo = SAB82532_DAFO_CHL5; bits = 7; break; - } - - if (cflag & CSTOPB) { - dafo |= SAB82532_DAFO_STOP; - bits++; - } - - if (cflag & PARENB) { - dafo |= SAB82532_DAFO_PARE; - bits++; - } - - if (cflag & PARODD) { - dafo |= SAB82532_DAFO_PAR_ODD; - } else { - dafo |= SAB82532_DAFO_PAR_EVEN; - } - up->cached_dafo = dafo; - - calc_ebrg(baud, &n, &m); - - up->cached_ebrg = n | (m << 6); - - up->tec_timeout = (10 * 1000000) / baud; - up->cec_timeout = up->tec_timeout >> 2; - - /* CTS flow control flags */ - /* We encode read_status_mask and ignore_status_mask like so: - * - * --------------------- - * | ... | ISR1 | ISR0 | - * --------------------- - * .. 15 8 7 0 - */ - - up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | - SAB82532_ISR0_RFO | SAB82532_ISR0_RPF | - SAB82532_ISR0_CDSC); - up->port.read_status_mask |= (SAB82532_ISR1_CSC | - SAB82532_ISR1_ALLS | - SAB82532_ISR1_XPR) << 8; - if (iflag & INPCK) - up->port.read_status_mask |= (SAB82532_ISR0_PERR | - SAB82532_ISR0_FERR); - if (iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8); - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (iflag & IGNPAR) - up->port.ignore_status_mask |= (SAB82532_ISR0_PERR | - SAB82532_ISR0_FERR); - if (iflag & IGNBRK) { - up->port.ignore_status_mask |= (SAB82532_ISR1_BRK << 8); - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (iflag & IGNPAR) - up->port.ignore_status_mask |= SAB82532_ISR0_RFO; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - up->port.ignore_status_mask |= (SAB82532_ISR0_RPF | - SAB82532_ISR0_TCD); - - uart_update_timeout(&up->port, cflag, - (up->port.uartclk / (16 * quot))); - - /* Now schedule a register update when the chip's - * transmitter is idle. - */ - up->cached_mode |= SAB82532_MODE_RAC; - set_bit(SAB82532_REGS_PENDING, &up->irqflags); - if (test_bit(SAB82532_XPR, &up->irqflags)) - sunsab_tx_idle(up); -} - -/* port->lock is not held. */ -static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - unsigned long flags; - unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); - unsigned int quot = uart_get_divisor(port, baud); - - spin_lock_irqsave(&up->port.lock, flags); - sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static const char *sunsab_type(struct uart_port *port) -{ - struct uart_sunsab_port *up = (void *)port; - static char buf[36]; - - sprintf(buf, "SAB82532 %s", sab82532_version[up->type]); - return buf; -} - -static void sunsab_release_port(struct uart_port *port) -{ -} - -static int sunsab_request_port(struct uart_port *port) -{ - return 0; -} - -static void sunsab_config_port(struct uart_port *port, int flags) -{ -} - -static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static struct uart_ops sunsab_pops = { - .tx_empty = sunsab_tx_empty, - .set_mctrl = sunsab_set_mctrl, - .get_mctrl = sunsab_get_mctrl, - .stop_tx = sunsab_stop_tx, - .start_tx = sunsab_start_tx, - .send_xchar = sunsab_send_xchar, - .stop_rx = sunsab_stop_rx, - .enable_ms = sunsab_enable_ms, - .break_ctl = sunsab_break_ctl, - .startup = sunsab_startup, - .shutdown = sunsab_shutdown, - .set_termios = sunsab_set_termios, - .type = sunsab_type, - .release_port = sunsab_release_port, - .request_port = sunsab_request_port, - .config_port = sunsab_config_port, - .verify_port = sunsab_verify_port, -}; - -static struct uart_driver sunsab_reg = { - .owner = THIS_MODULE, - .driver_name = "sunsab", - .dev_name = "ttyS", - .major = TTY_MAJOR, -}; - -static struct uart_sunsab_port *sunsab_ports; - -#ifdef CONFIG_SERIAL_SUNSAB_CONSOLE - -static void sunsab_console_putchar(struct uart_port *port, int c) -{ - struct uart_sunsab_port *up = (struct uart_sunsab_port *)port; - - sunsab_tec_wait(up); - writeb(c, &up->regs->w.tic); -} - -static void sunsab_console_write(struct console *con, const char *s, unsigned n) -{ - struct uart_sunsab_port *up = &sunsab_ports[con->index]; - unsigned long flags; - int locked = 1; - - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - uart_console_write(&up->port, s, n, sunsab_console_putchar); - sunsab_tec_wait(up); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static int sunsab_console_setup(struct console *con, char *options) -{ - struct uart_sunsab_port *up = &sunsab_ports[con->index]; - unsigned long flags; - unsigned int baud, quot; - - /* - * The console framework calls us for each and every port - * registered. Defer the console setup until the requested - * port has been properly discovered. A bit of a hack, - * though... - */ - if (up->port.type != PORT_SUNSAB) - return -1; - - printk("Console: ttyS%d (SAB82532)\n", - (sunsab_reg.minor - 64) + con->index); - - sunserial_console_termios(con, up->port.dev->of_node); - - switch (con->cflag & CBAUD) { - case B150: baud = 150; break; - case B300: baud = 300; break; - case B600: baud = 600; break; - case B1200: baud = 1200; break; - case B2400: baud = 2400; break; - case B4800: baud = 4800; break; - default: case B9600: baud = 9600; break; - case B19200: baud = 19200; break; - case B38400: baud = 38400; break; - case B57600: baud = 57600; break; - case B115200: baud = 115200; break; - case B230400: baud = 230400; break; - case B460800: baud = 460800; break; - }; - - /* - * Temporary fix. - */ - spin_lock_init(&up->port.lock); - - /* - * Initialize the hardware - */ - sunsab_startup(&up->port); - - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Finally, enable interrupts - */ - up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | - SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC; - writeb(up->interrupt_mask0, &up->regs->w.imr0); - up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | - SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | - SAB82532_IMR1_CSC | SAB82532_IMR1_XON | - SAB82532_IMR1_XPR; - writeb(up->interrupt_mask1, &up->regs->w.imr1); - - quot = uart_get_divisor(&up->port, baud); - sunsab_convert_to_sab(up, con->cflag, 0, baud, quot); - sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); - - spin_unlock_irqrestore(&up->port.lock, flags); - - return 0; -} - -static struct console sunsab_console = { - .name = "ttyS", - .write = sunsab_console_write, - .device = uart_console_device, - .setup = sunsab_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sunsab_reg, -}; - -static inline struct console *SUNSAB_CONSOLE(void) -{ - return &sunsab_console; -} -#else -#define SUNSAB_CONSOLE() (NULL) -#define sunsab_console_init() do { } while (0) -#endif - -static int __devinit sunsab_init_one(struct uart_sunsab_port *up, - struct platform_device *op, - unsigned long offset, - int line) -{ - up->port.line = line; - up->port.dev = &op->dev; - - up->port.mapbase = op->resource[0].start + offset; - up->port.membase = of_ioremap(&op->resource[0], offset, - sizeof(union sab82532_async_regs), - "sab"); - if (!up->port.membase) - return -ENOMEM; - up->regs = (union sab82532_async_regs __iomem *) up->port.membase; - - up->port.irq = op->archdata.irqs[0]; - - up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; - up->port.iotype = UPIO_MEM; - - writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); - - up->port.ops = &sunsab_pops; - up->port.type = PORT_SUNSAB; - up->port.uartclk = SAB_BASE_BAUD; - - up->type = readb(&up->regs->r.vstr) & 0x0f; - writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); - writeb(0xff, &up->regs->w.pim); - if ((up->port.line & 0x1) == 0) { - up->pvr_dsr_bit = (1 << 0); - up->pvr_dtr_bit = (1 << 1); - up->gis_shift = 2; - } else { - up->pvr_dsr_bit = (1 << 3); - up->pvr_dtr_bit = (1 << 2); - up->gis_shift = 0; - } - up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); - writeb(up->cached_pvr, &up->regs->w.pvr); - up->cached_mode = readb(&up->regs->rw.mode); - up->cached_mode |= SAB82532_MODE_FRTS; - writeb(up->cached_mode, &up->regs->rw.mode); - up->cached_mode |= SAB82532_MODE_RTS; - writeb(up->cached_mode, &up->regs->rw.mode); - - up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; - up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; - - return 0; -} - -static int __devinit sab_probe(struct platform_device *op, const struct of_device_id *match) -{ - static int inst; - struct uart_sunsab_port *up; - int err; - - up = &sunsab_ports[inst * 2]; - - err = sunsab_init_one(&up[0], op, - 0, - (inst * 2) + 0); - if (err) - goto out; - - err = sunsab_init_one(&up[1], op, - sizeof(union sab82532_async_regs), - (inst * 2) + 1); - if (err) - goto out1; - - sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node, - &sunsab_reg, up[0].port.line, - false); - - sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node, - &sunsab_reg, up[1].port.line, - false); - - err = uart_add_one_port(&sunsab_reg, &up[0].port); - if (err) - goto out2; - - err = uart_add_one_port(&sunsab_reg, &up[1].port); - if (err) - goto out3; - - dev_set_drvdata(&op->dev, &up[0]); - - inst++; - - return 0; - -out3: - uart_remove_one_port(&sunsab_reg, &up[0].port); -out2: - of_iounmap(&op->resource[0], - up[1].port.membase, - sizeof(union sab82532_async_regs)); -out1: - of_iounmap(&op->resource[0], - up[0].port.membase, - sizeof(union sab82532_async_regs)); -out: - return err; -} - -static int __devexit sab_remove(struct platform_device *op) -{ - struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); - - uart_remove_one_port(&sunsab_reg, &up[1].port); - uart_remove_one_port(&sunsab_reg, &up[0].port); - of_iounmap(&op->resource[0], - up[1].port.membase, - sizeof(union sab82532_async_regs)); - of_iounmap(&op->resource[0], - up[0].port.membase, - sizeof(union sab82532_async_regs)); - - dev_set_drvdata(&op->dev, NULL); - - return 0; -} - -static const struct of_device_id sab_match[] = { - { - .name = "se", - }, - { - .name = "serial", - .compatible = "sab82532", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, sab_match); - -static struct of_platform_driver sab_driver = { - .driver = { - .name = "sab", - .owner = THIS_MODULE, - .of_match_table = sab_match, - }, - .probe = sab_probe, - .remove = __devexit_p(sab_remove), -}; - -static int __init sunsab_init(void) -{ - struct device_node *dp; - int err; - int num_channels = 0; - - for_each_node_by_name(dp, "se") - num_channels += 2; - for_each_node_by_name(dp, "serial") { - if (of_device_is_compatible(dp, "sab82532")) - num_channels += 2; - } - - if (num_channels) { - sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) * - num_channels, GFP_KERNEL); - if (!sunsab_ports) - return -ENOMEM; - - err = sunserial_register_minors(&sunsab_reg, num_channels); - if (err) { - kfree(sunsab_ports); - sunsab_ports = NULL; - - return err; - } - } - - return of_register_platform_driver(&sab_driver); -} - -static void __exit sunsab_exit(void) -{ - of_unregister_platform_driver(&sab_driver); - if (sunsab_reg.nr) { - sunserial_unregister_minors(&sunsab_reg, sunsab_reg.nr); - } - - kfree(sunsab_ports); - sunsab_ports = NULL; -} - -module_init(sunsab_init); -module_exit(sunsab_exit); - -MODULE_AUTHOR("Eddie C. Dost and David S. Miller"); -MODULE_DESCRIPTION("Sun SAB82532 serial port driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/sunsab.h b/drivers/serial/sunsab.h deleted file mode 100644 index b78e1f7b8050..000000000000 --- a/drivers/serial/sunsab.h +++ /dev/null @@ -1,322 +0,0 @@ -/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - */ - -#ifndef _SUNSAB_H -#define _SUNSAB_H - -struct sab82532_async_rd_regs { - u8 rfifo[0x20]; /* Receive FIFO */ - u8 star; /* Status Register */ - u8 __pad1; - u8 mode; /* Mode Register */ - u8 timr; /* Timer Register */ - u8 xon; /* XON Character */ - u8 xoff; /* XOFF Character */ - u8 tcr; /* Termination Character Register */ - u8 dafo; /* Data Format */ - u8 rfc; /* RFIFO Control Register */ - u8 __pad2; - u8 rbcl; /* Receive Byte Count Low */ - u8 rbch; /* Receive Byte Count High */ - u8 ccr0; /* Channel Configuration Register 0 */ - u8 ccr1; /* Channel Configuration Register 1 */ - u8 ccr2; /* Channel Configuration Register 2 */ - u8 ccr3; /* Channel Configuration Register 3 */ - u8 __pad3[4]; - u8 vstr; /* Version Status Register */ - u8 __pad4[3]; - u8 gis; /* Global Interrupt Status */ - u8 ipc; /* Interrupt Port Configuration */ - u8 isr0; /* Interrupt Status 0 */ - u8 isr1; /* Interrupt Status 1 */ - u8 pvr; /* Port Value Register */ - u8 pis; /* Port Interrupt Status */ - u8 pcr; /* Port Configuration Register */ - u8 ccr4; /* Channel Configuration Register 4 */ -}; - -struct sab82532_async_wr_regs { - u8 xfifo[0x20]; /* Transmit FIFO */ - u8 cmdr; /* Command Register */ - u8 __pad1; - u8 mode; - u8 timr; - u8 xon; - u8 xoff; - u8 tcr; - u8 dafo; - u8 rfc; - u8 __pad2; - u8 xbcl; /* Transmit Byte Count Low */ - u8 xbch; /* Transmit Byte Count High */ - u8 ccr0; - u8 ccr1; - u8 ccr2; - u8 ccr3; - u8 tsax; /* Time-Slot Assignment Reg. Transmit */ - u8 tsar; /* Time-Slot Assignment Reg. Receive */ - u8 xccr; /* Transmit Channel Capacity Register */ - u8 rccr; /* Receive Channel Capacity Register */ - u8 bgr; /* Baud Rate Generator Register */ - u8 tic; /* Transmit Immediate Character */ - u8 mxn; /* Mask XON Character */ - u8 mxf; /* Mask XOFF Character */ - u8 iva; /* Interrupt Vector Address */ - u8 ipc; - u8 imr0; /* Interrupt Mask Register 0 */ - u8 imr1; /* Interrupt Mask Register 1 */ - u8 pvr; - u8 pim; /* Port Interrupt Mask */ - u8 pcr; - u8 ccr4; -}; - -struct sab82532_async_rw_regs { /* Read/Write registers */ - u8 __pad1[0x20]; - u8 __pad2; - u8 __pad3; - u8 mode; - u8 timr; - u8 xon; - u8 xoff; - u8 tcr; - u8 dafo; - u8 rfc; - u8 __pad4; - u8 __pad5; - u8 __pad6; - u8 ccr0; - u8 ccr1; - u8 ccr2; - u8 ccr3; - u8 __pad7; - u8 __pad8; - u8 __pad9; - u8 __pad10; - u8 __pad11; - u8 __pad12; - u8 __pad13; - u8 __pad14; - u8 __pad15; - u8 ipc; - u8 __pad16; - u8 __pad17; - u8 pvr; - u8 __pad18; - u8 pcr; - u8 ccr4; -}; - -union sab82532_async_regs { - __volatile__ struct sab82532_async_rd_regs r; - __volatile__ struct sab82532_async_wr_regs w; - __volatile__ struct sab82532_async_rw_regs rw; -}; - -union sab82532_irq_status { - unsigned short stat; - struct { - unsigned char isr0; - unsigned char isr1; - } sreg; -}; - -/* irqflags bits */ -#define SAB82532_ALLS 0x00000001 -#define SAB82532_XPR 0x00000002 -#define SAB82532_REGS_PENDING 0x00000004 - -/* RFIFO Status Byte */ -#define SAB82532_RSTAT_PE 0x80 -#define SAB82532_RSTAT_FE 0x40 -#define SAB82532_RSTAT_PARITY 0x01 - -/* Status Register (STAR) */ -#define SAB82532_STAR_XDOV 0x80 -#define SAB82532_STAR_XFW 0x40 -#define SAB82532_STAR_RFNE 0x20 -#define SAB82532_STAR_FCS 0x10 -#define SAB82532_STAR_TEC 0x08 -#define SAB82532_STAR_CEC 0x04 -#define SAB82532_STAR_CTS 0x02 - -/* Command Register (CMDR) */ -#define SAB82532_CMDR_RMC 0x80 -#define SAB82532_CMDR_RRES 0x40 -#define SAB82532_CMDR_RFRD 0x20 -#define SAB82532_CMDR_STI 0x10 -#define SAB82532_CMDR_XF 0x08 -#define SAB82532_CMDR_XRES 0x01 - -/* Mode Register (MODE) */ -#define SAB82532_MODE_FRTS 0x40 -#define SAB82532_MODE_FCTS 0x20 -#define SAB82532_MODE_FLON 0x10 -#define SAB82532_MODE_RAC 0x08 -#define SAB82532_MODE_RTS 0x04 -#define SAB82532_MODE_TRS 0x02 -#define SAB82532_MODE_TLP 0x01 - -/* Timer Register (TIMR) */ -#define SAB82532_TIMR_CNT_MASK 0xe0 -#define SAB82532_TIMR_VALUE_MASK 0x1f - -/* Data Format (DAFO) */ -#define SAB82532_DAFO_XBRK 0x40 -#define SAB82532_DAFO_STOP 0x20 -#define SAB82532_DAFO_PAR_SPACE 0x00 -#define SAB82532_DAFO_PAR_ODD 0x08 -#define SAB82532_DAFO_PAR_EVEN 0x10 -#define SAB82532_DAFO_PAR_MARK 0x18 -#define SAB82532_DAFO_PARE 0x04 -#define SAB82532_DAFO_CHL8 0x00 -#define SAB82532_DAFO_CHL7 0x01 -#define SAB82532_DAFO_CHL6 0x02 -#define SAB82532_DAFO_CHL5 0x03 - -/* RFIFO Control Register (RFC) */ -#define SAB82532_RFC_DPS 0x40 -#define SAB82532_RFC_DXS 0x20 -#define SAB82532_RFC_RFDF 0x10 -#define SAB82532_RFC_RFTH_1 0x00 -#define SAB82532_RFC_RFTH_4 0x04 -#define SAB82532_RFC_RFTH_16 0x08 -#define SAB82532_RFC_RFTH_32 0x0c -#define SAB82532_RFC_TCDE 0x01 - -/* Received Byte Count High (RBCH) */ -#define SAB82532_RBCH_DMA 0x80 -#define SAB82532_RBCH_CAS 0x20 - -/* Transmit Byte Count High (XBCH) */ -#define SAB82532_XBCH_DMA 0x80 -#define SAB82532_XBCH_CAS 0x20 -#define SAB82532_XBCH_XC 0x10 - -/* Channel Configuration Register 0 (CCR0) */ -#define SAB82532_CCR0_PU 0x80 -#define SAB82532_CCR0_MCE 0x40 -#define SAB82532_CCR0_SC_NRZ 0x00 -#define SAB82532_CCR0_SC_NRZI 0x08 -#define SAB82532_CCR0_SC_FM0 0x10 -#define SAB82532_CCR0_SC_FM1 0x14 -#define SAB82532_CCR0_SC_MANCH 0x18 -#define SAB82532_CCR0_SM_HDLC 0x00 -#define SAB82532_CCR0_SM_SDLC_LOOP 0x01 -#define SAB82532_CCR0_SM_BISYNC 0x02 -#define SAB82532_CCR0_SM_ASYNC 0x03 - -/* Channel Configuration Register 1 (CCR1) */ -#define SAB82532_CCR1_ODS 0x10 -#define SAB82532_CCR1_BCR 0x08 -#define SAB82532_CCR1_CM_MASK 0x07 - -/* Channel Configuration Register 2 (CCR2) */ -#define SAB82532_CCR2_SOC1 0x80 -#define SAB82532_CCR2_SOC0 0x40 -#define SAB82532_CCR2_BR9 0x80 -#define SAB82532_CCR2_BR8 0x40 -#define SAB82532_CCR2_BDF 0x20 -#define SAB82532_CCR2_SSEL 0x10 -#define SAB82532_CCR2_XCS0 0x20 -#define SAB82532_CCR2_RCS0 0x10 -#define SAB82532_CCR2_TOE 0x08 -#define SAB82532_CCR2_RWX 0x04 -#define SAB82532_CCR2_DIV 0x01 - -/* Channel Configuration Register 3 (CCR3) */ -#define SAB82532_CCR3_PSD 0x01 - -/* Time Slot Assignment Register Transmit (TSAX) */ -#define SAB82532_TSAX_TSNX_MASK 0xfc -#define SAB82532_TSAX_XCS2 0x02 /* see also CCR2 */ -#define SAB82532_TSAX_XCS1 0x01 - -/* Time Slot Assignment Register Receive (TSAR) */ -#define SAB82532_TSAR_TSNR_MASK 0xfc -#define SAB82532_TSAR_RCS2 0x02 /* see also CCR2 */ -#define SAB82532_TSAR_RCS1 0x01 - -/* Version Status Register (VSTR) */ -#define SAB82532_VSTR_CD 0x80 -#define SAB82532_VSTR_DPLA 0x40 -#define SAB82532_VSTR_VN_MASK 0x0f -#define SAB82532_VSTR_VN_1 0x00 -#define SAB82532_VSTR_VN_2 0x01 -#define SAB82532_VSTR_VN_3_2 0x02 - -/* Global Interrupt Status Register (GIS) */ -#define SAB82532_GIS_PI 0x80 -#define SAB82532_GIS_ISA1 0x08 -#define SAB82532_GIS_ISA0 0x04 -#define SAB82532_GIS_ISB1 0x02 -#define SAB82532_GIS_ISB0 0x01 - -/* Interrupt Vector Address (IVA) */ -#define SAB82532_IVA_MASK 0xf1 - -/* Interrupt Port Configuration (IPC) */ -#define SAB82532_IPC_VIS 0x80 -#define SAB82532_IPC_SLA1 0x10 -#define SAB82532_IPC_SLA0 0x08 -#define SAB82532_IPC_CASM 0x04 -#define SAB82532_IPC_IC_OPEN_DRAIN 0x00 -#define SAB82532_IPC_IC_ACT_LOW 0x01 -#define SAB82532_IPC_IC_ACT_HIGH 0x03 - -/* Interrupt Status Register 0 (ISR0) */ -#define SAB82532_ISR0_TCD 0x80 -#define SAB82532_ISR0_TIME 0x40 -#define SAB82532_ISR0_PERR 0x20 -#define SAB82532_ISR0_FERR 0x10 -#define SAB82532_ISR0_PLLA 0x08 -#define SAB82532_ISR0_CDSC 0x04 -#define SAB82532_ISR0_RFO 0x02 -#define SAB82532_ISR0_RPF 0x01 - -/* Interrupt Status Register 1 (ISR1) */ -#define SAB82532_ISR1_BRK 0x80 -#define SAB82532_ISR1_BRKT 0x40 -#define SAB82532_ISR1_ALLS 0x20 -#define SAB82532_ISR1_XOFF 0x10 -#define SAB82532_ISR1_TIN 0x08 -#define SAB82532_ISR1_CSC 0x04 -#define SAB82532_ISR1_XON 0x02 -#define SAB82532_ISR1_XPR 0x01 - -/* Interrupt Mask Register 0 (IMR0) */ -#define SAB82532_IMR0_TCD 0x80 -#define SAB82532_IMR0_TIME 0x40 -#define SAB82532_IMR0_PERR 0x20 -#define SAB82532_IMR0_FERR 0x10 -#define SAB82532_IMR0_PLLA 0x08 -#define SAB82532_IMR0_CDSC 0x04 -#define SAB82532_IMR0_RFO 0x02 -#define SAB82532_IMR0_RPF 0x01 - -/* Interrupt Mask Register 1 (IMR1) */ -#define SAB82532_IMR1_BRK 0x80 -#define SAB82532_IMR1_BRKT 0x40 -#define SAB82532_IMR1_ALLS 0x20 -#define SAB82532_IMR1_XOFF 0x10 -#define SAB82532_IMR1_TIN 0x08 -#define SAB82532_IMR1_CSC 0x04 -#define SAB82532_IMR1_XON 0x02 -#define SAB82532_IMR1_XPR 0x01 - -/* Port Interrupt Status Register (PIS) */ -#define SAB82532_PIS_SYNC_B 0x08 -#define SAB82532_PIS_DTR_B 0x04 -#define SAB82532_PIS_DTR_A 0x02 -#define SAB82532_PIS_SYNC_A 0x01 - -/* Channel Configuration Register 4 (CCR4) */ -#define SAB82532_CCR4_MCK4 0x80 -#define SAB82532_CCR4_EBRG 0x40 -#define SAB82532_CCR4_TST1 0x20 -#define SAB82532_CCR4_ICD 0x10 - - -#endif /* !(_SUNSAB_H) */ diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c deleted file mode 100644 index 551ebfe3ccbb..000000000000 --- a/drivers/serial/sunsu.c +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1998-1999 Pete Zaitcev (zaitcev@yahoo.com) - * - * This is mainly a variation of 8250.c, credits go to authors mentioned - * therein. In fact this driver should be merged into the generic 8250.c - * infrastructure perhaps using a 8250_sparc.c module. - * - * Fixed to use tty_get_baud_rate(). - * Theodore Ts'o , 2001-Oct-12 - * - * Converted to new 2.5.x UART layer. - * David S. Miller (davem@davemloft.net), 2002-Jul-29 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SERIO -#include -#endif -#include -#include -#include -#include - -#include -#include -#include - -#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#include "suncore.h" - -/* We are on a NS PC87303 clocked with 24.0 MHz, which results - * in a UART clock of 1.8462 MHz. - */ -#define SU_BASE_BAUD (1846200 / 16) - -enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT }; -static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" }; - -/* - * Here we define the default xmit fifo size used for each type of UART. - */ -static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { - { "unknown", 1, 0 }, - { "8250", 1, 0 }, - { "16450", 1, 0 }, - { "16550", 1, 0 }, - { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "Cirrus", 1, 0 }, - { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, - { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "Startech", 1, 0 }, - { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO } -}; - -struct uart_sunsu_port { - struct uart_port port; - unsigned char acr; - unsigned char ier; - unsigned short rev; - unsigned char lcr; - unsigned int lsr_break_flag; - unsigned int cflag; - - /* Probing information. */ - enum su_type su_type; - unsigned int type_probed; /* XXX Stupid */ - unsigned long reg_size; - -#ifdef CONFIG_SERIO - struct serio serio; - int serio_open; -#endif -}; - -static unsigned int serial_in(struct uart_sunsu_port *up, int offset) -{ - offset <<= up->port.regshift; - - switch (up->port.iotype) { - case UPIO_HUB6: - outb(up->port.hub6 - 1 + offset, up->port.iobase); - return inb(up->port.iobase + 1); - - case UPIO_MEM: - return readb(up->port.membase + offset); - - default: - return inb(up->port.iobase + offset); - } -} - -static void serial_out(struct uart_sunsu_port *up, int offset, int value) -{ -#ifndef CONFIG_SPARC64 - /* - * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are - * connected with a gate then go to SlavIO. When IRQ4 goes tristated - * gate outputs a logical one. Since we use level triggered interrupts - * we have lockup and watchdog reset. We cannot mask IRQ because - * keyboard shares IRQ with us (Word has it as Bob Smelik's design). - * This problem is similar to what Alpha people suffer, see serial.c. - */ - if (offset == UART_MCR) - value |= UART_MCR_OUT2; -#endif - offset <<= up->port.regshift; - - switch (up->port.iotype) { - case UPIO_HUB6: - outb(up->port.hub6 - 1 + offset, up->port.iobase); - outb(value, up->port.iobase + 1); - break; - - case UPIO_MEM: - writeb(value, up->port.membase + offset); - break; - - default: - outb(value, up->port.iobase + offset); - } -} - -/* - * We used to support using pause I/O for certain machines. We - * haven't supported this for a while, but just in case it's badly - * needed for certain old 386 machines, I've left these #define's - * in.... - */ -#define serial_inp(up, offset) serial_in(up, offset) -#define serial_outp(up, offset, value) serial_out(up, offset, value) - - -/* - * For the 16C950 - */ -static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value) -{ - serial_out(up, UART_SCR, offset); - serial_out(up, UART_ICR, value); -} - -#if 0 /* Unused currently */ -static unsigned int serial_icr_read(struct uart_sunsu_port *up, int offset) -{ - unsigned int value; - - serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); - serial_out(up, UART_SCR, offset); - value = serial_in(up, UART_ICR); - serial_icr_write(up, UART_ACR, up->acr); - - return value; -} -#endif - -#ifdef CONFIG_SERIAL_8250_RSA -/* - * Attempts to turn on the RSA FIFO. Returns zero on failure. - * We set the port uart clock rate if we succeed. - */ -static int __enable_rsa(struct uart_sunsu_port *up) -{ - unsigned char mode; - int result; - - mode = serial_inp(up, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - - if (!result) { - serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); - result = mode & UART_RSA_MSR_FIFO; - } - - if (result) - up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; - - return result; -} - -static void enable_rsa(struct uart_sunsu_port *up) -{ - if (up->port.type == PORT_RSA) { - if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); - __enable_rsa(up); - spin_unlock_irq(&up->port.lock); - } - if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) - serial_outp(up, UART_RSA_FRR, 0); - } -} - -/* - * Attempts to turn off the RSA FIFO. Returns zero on failure. - * It is unknown why interrupts were disabled in here. However, - * the caller is expected to preserve this behaviour by grabbing - * the spinlock before calling this function. - */ -static void disable_rsa(struct uart_sunsu_port *up) -{ - unsigned char mode; - int result; - - if (up->port.type == PORT_RSA && - up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { - spin_lock_irq(&up->port.lock); - - mode = serial_inp(up, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - - if (!result) { - serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); - mode = serial_inp(up, UART_RSA_MSR); - result = !(mode & UART_RSA_MSR_FIFO); - } - - if (result) - up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; - spin_unlock_irq(&up->port.lock); - } -} -#endif /* CONFIG_SERIAL_8250_RSA */ - -static inline void __stop_tx(struct uart_sunsu_port *p) -{ - if (p->ier & UART_IER_THRI) { - p->ier &= ~UART_IER_THRI; - serial_out(p, UART_IER, p->ier); - } -} - -static void sunsu_stop_tx(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - - __stop_tx(up); - - /* - * We really want to stop the transmitter from sending. - */ - if (up->port.type == PORT_16C950) { - up->acr |= UART_ACR_TXDIS; - serial_icr_write(up, UART_ACR, up->acr); - } -} - -static void sunsu_start_tx(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); - } - - /* - * Re-enable the transmitter if we disabled it. - */ - if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { - up->acr &= ~UART_ACR_TXDIS; - serial_icr_write(up, UART_ACR, up->acr); - } -} - -static void sunsu_stop_rx(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); -} - -static void sunsu_enable_ms(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - up->ier |= UART_IER_MSI; - serial_out(up, UART_IER, up->ier); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static struct tty_struct * -receive_chars(struct uart_sunsu_port *up, unsigned char *status) -{ - struct tty_struct *tty = up->port.state->port.tty; - unsigned char ch, flag; - int max_count = 256; - int saw_console_brk = 0; - - do { - ch = serial_inp(up, UART_RX); - flag = TTY_NORMAL; - up->port.icount.rx++; - - if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | - UART_LSR_FE | UART_LSR_OE))) { - /* - * For statistics only - */ - if (*status & UART_LSR_BI) { - *status &= ~(UART_LSR_FE | UART_LSR_PE); - up->port.icount.brk++; - if (up->port.cons != NULL && - up->port.line == up->port.cons->index) - saw_console_brk = 1; - /* - * We do the SysRQ and SAK checking - * here because otherwise the break - * may get masked by ignore_status_mask - * or read_status_mask. - */ - if (uart_handle_break(&up->port)) - goto ignore_char; - } else if (*status & UART_LSR_PE) - up->port.icount.parity++; - else if (*status & UART_LSR_FE) - up->port.icount.frame++; - if (*status & UART_LSR_OE) - up->port.icount.overrun++; - - /* - * Mask off conditions which should be ingored. - */ - *status &= up->port.read_status_mask; - - if (up->port.cons != NULL && - up->port.line == up->port.cons->index) { - /* Recover the break flag from console xmit */ - *status |= up->lsr_break_flag; - up->lsr_break_flag = 0; - } - - if (*status & UART_LSR_BI) { - flag = TTY_BREAK; - } else if (*status & UART_LSR_PE) - flag = TTY_PARITY; - else if (*status & UART_LSR_FE) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - goto ignore_char; - if ((*status & up->port.ignore_status_mask) == 0) - tty_insert_flip_char(tty, ch, flag); - if (*status & UART_LSR_OE) - /* - * Overrun is special, since it's reported - * immediately, and doesn't affect the current - * character. - */ - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - ignore_char: - *status = serial_inp(up, UART_LSR); - } while ((*status & UART_LSR_DR) && (max_count-- > 0)); - - if (saw_console_brk) - sun_do_break(); - - return tty; -} - -static void transmit_chars(struct uart_sunsu_port *up) -{ - struct circ_buf *xmit = &up->port.state->xmit; - int count; - - if (up->port.x_char) { - serial_outp(up, UART_TX, up->port.x_char); - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - if (uart_tx_stopped(&up->port)) { - sunsu_stop_tx(&up->port); - return; - } - if (uart_circ_empty(xmit)) { - __stop_tx(up); - return; - } - - count = up->port.fifosize; - do { - serial_out(up, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (--count > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - if (uart_circ_empty(xmit)) - __stop_tx(up); -} - -static void check_modem_status(struct uart_sunsu_port *up) -{ - int status; - - status = serial_in(up, UART_MSR); - - if ((status & UART_MSR_ANY_DELTA) == 0) - return; - - if (status & UART_MSR_TERI) - up->port.icount.rng++; - if (status & UART_MSR_DDSR) - up->port.icount.dsr++; - if (status & UART_MSR_DDCD) - uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); - if (status & UART_MSR_DCTS) - uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); -} - -static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) -{ - struct uart_sunsu_port *up = dev_id; - unsigned long flags; - unsigned char status; - - spin_lock_irqsave(&up->port.lock, flags); - - do { - struct tty_struct *tty; - - status = serial_inp(up, UART_LSR); - tty = NULL; - if (status & UART_LSR_DR) - tty = receive_chars(up, &status); - check_modem_status(up); - if (status & UART_LSR_THRE) - transmit_chars(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - - if (tty) - tty_flip_buffer_push(tty); - - spin_lock_irqsave(&up->port.lock, flags); - - } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); - - spin_unlock_irqrestore(&up->port.lock, flags); - - return IRQ_HANDLED; -} - -/* Separate interrupt handling path for keyboard/mouse ports. */ - -static void -sunsu_change_speed(struct uart_port *port, unsigned int cflag, - unsigned int iflag, unsigned int quot); - -static void sunsu_change_mouse_baud(struct uart_sunsu_port *up) -{ - unsigned int cur_cflag = up->cflag; - int quot, new_baud; - - up->cflag &= ~CBAUD; - up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud); - - quot = up->port.uartclk / (16 * new_baud); - - sunsu_change_speed(&up->port, up->cflag, 0, quot); -} - -static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break) -{ - do { - unsigned char ch = serial_inp(up, UART_RX); - - /* Stop-A is handled by drivers/char/keyboard.c now. */ - if (up->su_type == SU_PORT_KBD) { -#ifdef CONFIG_SERIO - serio_interrupt(&up->serio, ch, 0); -#endif - } else if (up->su_type == SU_PORT_MS) { - int ret = suncore_mouse_baud_detection(ch, is_break); - - switch (ret) { - case 2: - sunsu_change_mouse_baud(up); - /* fallthru */ - case 1: - break; - - case 0: -#ifdef CONFIG_SERIO - serio_interrupt(&up->serio, ch, 0); -#endif - break; - }; - } - } while (serial_in(up, UART_LSR) & UART_LSR_DR); -} - -static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id) -{ - struct uart_sunsu_port *up = dev_id; - - if (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)) { - unsigned char status = serial_inp(up, UART_LSR); - - if ((status & UART_LSR_DR) || (status & UART_LSR_BI)) - receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0); - } - - return IRQ_HANDLED; -} - -static unsigned int sunsu_tx_empty(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&up->port.lock, flags); - ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; - spin_unlock_irqrestore(&up->port.lock, flags); - - return ret; -} - -static unsigned int sunsu_get_mctrl(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned char status; - unsigned int ret; - - status = serial_in(up, 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 sunsu_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned char mcr = 0; - - 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(up, UART_MCR, mcr); -} - -static void sunsu_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); - if (break_state == -1) - up->lcr |= UART_LCR_SBC; - else - up->lcr &= ~UART_LCR_SBC; - serial_out(up, UART_LCR, up->lcr); - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static int sunsu_startup(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; - int retval; - - if (up->port.type == PORT_16C950) { - /* Wake up and initialize UART */ - up->acr = 0; - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_IER, 0); - serial_outp(up, UART_LCR, 0); - serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, UART_EFR_ECB); - serial_outp(up, UART_LCR, 0); - } - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * If this is an RSA port, see if we can kick it up to the - * higher speed clock. - */ - enable_rsa(up); -#endif - - /* - * Clear the FIFO buffers and disable them. - * (they will be reenabled in set_termios()) - */ - if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); - } - - /* - * Clear the interrupt registers. - */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); - - /* - * At this point, there's no way the LSR could still be 0xff; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (!(up->port.flags & UPF_BUGGY_UART) && - (serial_inp(up, UART_LSR) == 0xff)) { - printk("ttyS%d: LSR safety check engaged!\n", up->port.line); - return -ENODEV; - } - - if (up->su_type != SU_PORT_PORT) { - retval = request_irq(up->port.irq, sunsu_kbd_ms_interrupt, - IRQF_SHARED, su_typev[up->su_type], up); - } else { - retval = request_irq(up->port.irq, sunsu_serial_interrupt, - IRQF_SHARED, su_typev[up->su_type], up); - } - if (retval) { - printk("su: Cannot register IRQ %d\n", up->port.irq); - return retval; - } - - /* - * Now, initialize the UART - */ - serial_outp(up, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irqsave(&up->port.lock, flags); - - up->port.mctrl |= TIOCM_OUT2; - - sunsu_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Finally, enable interrupts. Note: Modem status interrupts - * are set via set_termios(), which will be occurring imminently - * anyway, so we don't enable them here. - */ - up->ier = UART_IER_RLSI | UART_IER_RDI; - serial_outp(up, UART_IER, up->ier); - - if (up->port.flags & UPF_FOURPORT) { - unsigned int icp; - /* - * Enable interrupts on the AST Fourport board - */ - icp = (up->port.iobase & 0xfe0) | 0x01f; - outb_p(0x80, icp); - (void) inb_p(icp); - } - - /* - * And clear the interrupt registers again for luck. - */ - (void) serial_inp(up, UART_LSR); - (void) serial_inp(up, UART_RX); - (void) serial_inp(up, UART_IIR); - (void) serial_inp(up, UART_MSR); - - return 0; -} - -static void sunsu_shutdown(struct uart_port *port) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned long flags; - - /* - * Disable interrupts from this port - */ - up->ier = 0; - serial_outp(up, UART_IER, 0); - - spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_FOURPORT) { - /* reset interrupts on the AST Fourport board */ - inb((up->port.iobase & 0xfe0) | 0x1f); - up->port.mctrl |= TIOCM_OUT1; - } else - up->port.mctrl &= ~TIOCM_OUT2; - - sunsu_set_mctrl(&up->port, up->port.mctrl); - spin_unlock_irqrestore(&up->port.lock, flags); - - /* - * Disable break condition and FIFOs - */ - serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - serial_outp(up, UART_FCR, 0); - -#ifdef CONFIG_SERIAL_8250_RSA - /* - * Reset the RSA board back to 115kbps compat mode. - */ - disable_rsa(up); -#endif - - /* - * Read data port to reset things. - */ - (void) serial_in(up, UART_RX); - - free_irq(up->port.irq, up); -} - -static void -sunsu_change_speed(struct uart_port *port, unsigned int cflag, - unsigned int iflag, unsigned int quot) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - unsigned char cval, fcr = 0; - unsigned long flags; - - switch (cflag & CSIZE) { - case CS5: - cval = 0x00; - break; - case CS6: - cval = 0x01; - break; - case CS7: - cval = 0x02; - break; - default: - case CS8: - cval = 0x03; - break; - } - - if (cflag & CSTOPB) - cval |= 0x04; - if (cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; -#ifdef CMSPAR - if (cflag & CMSPAR) - cval |= UART_LCR_SPAR; -#endif - - /* - * Work around a bug in the Oxford Semiconductor 952 rev B - * chip which causes it to seriously miscalculate baud rates - * when DLL is 0. - */ - if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && - up->rev == 0x5201) - quot ++; - - if (uart_config[up->port.type].flags & UART_USE_FIFO) { - if ((up->port.uartclk / quot) < (2400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; -#ifdef CONFIG_SERIAL_8250_RSA - else if (up->port.type == PORT_RSA) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; -#endif - else - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; - } - if (up->port.type == PORT_16750) - fcr |= UART_FCR7_64BYTE; - - /* - * Ok, we're now changing the port state. Do it with - * interrupts disabled. - */ - spin_lock_irqsave(&up->port.lock, flags); - - /* - * Update the per-port timeout. - */ - uart_update_timeout(port, cflag, (port->uartclk / (16 * quot))); - - up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (iflag & INPCK) - up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= UART_LSR_BI; - - /* - * Characteres to ignore - */ - up->port.ignore_status_mask = 0; - if (iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; - if (iflag & IGNBRK) { - up->port.ignore_status_mask |= UART_LSR_BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (iflag & IGNPAR) - up->port.ignore_status_mask |= UART_LSR_OE; - } - - /* - * ignore all characters if CREAD is not set - */ - if ((cflag & CREAD) == 0) - up->port.ignore_status_mask |= UART_LSR_DR; - - /* - * CTS flow control flag and modem status interrupts - */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, cflag)) - up->ier |= UART_IER_MSI; - - serial_out(up, UART_IER, up->ier); - - if (uart_config[up->port.type].flags & UART_STARTECH) { - serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); - } - serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ - serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ - if (up->port.type == PORT_16750) - serial_outp(up, UART_FCR, fcr); /* set fcr */ - serial_outp(up, UART_LCR, cval); /* reset DLAB */ - up->lcr = cval; /* Save LCR */ - if (up->port.type != PORT_16750) { - if (fcr & UART_FCR_ENABLE_FIFO) { - /* emulated UARTs (Lucent Venus 167x) need two steps */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - } - serial_outp(up, UART_FCR, fcr); /* set fcr */ - } - - up->cflag = cflag; - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static void -sunsu_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud, quot; - - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - sunsu_change_speed(port, termios->c_cflag, termios->c_iflag, quot); -} - -static void sunsu_release_port(struct uart_port *port) -{ -} - -static int sunsu_request_port(struct uart_port *port) -{ - return 0; -} - -static void sunsu_config_port(struct uart_port *port, int flags) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; - - if (flags & UART_CONFIG_TYPE) { - /* - * We are supposed to call autoconfig here, but this requires - * splitting all the OBP probing crap from the UART probing. - * We'll do it when we kill sunsu.c altogether. - */ - port->type = up->type_probed; /* XXX */ - } -} - -static int -sunsu_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -static const char * -sunsu_type(struct uart_port *port) -{ - int type = port->type; - - if (type >= ARRAY_SIZE(uart_config)) - type = 0; - return uart_config[type].name; -} - -static struct uart_ops sunsu_pops = { - .tx_empty = sunsu_tx_empty, - .set_mctrl = sunsu_set_mctrl, - .get_mctrl = sunsu_get_mctrl, - .stop_tx = sunsu_stop_tx, - .start_tx = sunsu_start_tx, - .stop_rx = sunsu_stop_rx, - .enable_ms = sunsu_enable_ms, - .break_ctl = sunsu_break_ctl, - .startup = sunsu_startup, - .shutdown = sunsu_shutdown, - .set_termios = sunsu_set_termios, - .type = sunsu_type, - .release_port = sunsu_release_port, - .request_port = sunsu_request_port, - .config_port = sunsu_config_port, - .verify_port = sunsu_verify_port, -}; - -#define UART_NR 4 - -static struct uart_sunsu_port sunsu_ports[UART_NR]; - -#ifdef CONFIG_SERIO - -static DEFINE_SPINLOCK(sunsu_serio_lock); - -static int sunsu_serio_write(struct serio *serio, unsigned char ch) -{ - struct uart_sunsu_port *up = serio->port_data; - unsigned long flags; - int lsr; - - spin_lock_irqsave(&sunsu_serio_lock, flags); - - do { - lsr = serial_in(up, UART_LSR); - } while (!(lsr & UART_LSR_THRE)); - - /* Send the character out. */ - serial_out(up, UART_TX, ch); - - spin_unlock_irqrestore(&sunsu_serio_lock, flags); - - return 0; -} - -static int sunsu_serio_open(struct serio *serio) -{ - struct uart_sunsu_port *up = serio->port_data; - unsigned long flags; - int ret; - - spin_lock_irqsave(&sunsu_serio_lock, flags); - if (!up->serio_open) { - up->serio_open = 1; - ret = 0; - } else - ret = -EBUSY; - spin_unlock_irqrestore(&sunsu_serio_lock, flags); - - return ret; -} - -static void sunsu_serio_close(struct serio *serio) -{ - struct uart_sunsu_port *up = serio->port_data; - unsigned long flags; - - spin_lock_irqsave(&sunsu_serio_lock, flags); - up->serio_open = 0; - spin_unlock_irqrestore(&sunsu_serio_lock, flags); -} - -#endif /* CONFIG_SERIO */ - -static void sunsu_autoconfig(struct uart_sunsu_port *up) -{ - unsigned char status1, status2, scratch, scratch2, scratch3; - unsigned char save_lcr, save_mcr; - unsigned long flags; - - if (up->su_type == SU_PORT_NONE) - return; - - up->type_probed = PORT_UNKNOWN; - up->port.iotype = UPIO_MEM; - - spin_lock_irqsave(&up->port.lock, flags); - - if (!(up->port.flags & UPF_BUGGY_UART)) { - /* - * Do a simple existence test first; if we fail this, there's - * no point trying anything else. - * - * 0x80 is used as a nonsense port to prevent against false - * positives due to ISA bus float. The assumption is that - * 0x80 is a non-existent port; which should be safe since - * include/asm/io.h also makes this assumption. - */ - scratch = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, 0); -#ifdef __i386__ - outb(0xff, 0x080); -#endif - scratch2 = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, 0x0f); -#ifdef __i386__ - outb(0, 0x080); -#endif - scratch3 = serial_inp(up, UART_IER); - serial_outp(up, UART_IER, scratch); - if (scratch2 != 0 || scratch3 != 0x0F) - goto out; /* We failed; there's nothing here */ - } - - save_mcr = serial_in(up, UART_MCR); - save_lcr = serial_in(up, UART_LCR); - - /* - * Check to see if a UART is really there. Certain broken - * internal modems based on the Rockwell chipset fail this - * test, because they apparently don't implement the loopback - * test mode. So this test is skipped on the COM 1 through - * COM 4 ports. This *should* be safe, since no board - * manufacturer would be stupid enough to design a board - * that conflicts with COM 1-4 --- we hope! - */ - if (!(up->port.flags & UPF_SKIP_TEST)) { - serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); - status1 = serial_inp(up, UART_MSR) & 0xF0; - serial_outp(up, UART_MCR, save_mcr); - if (status1 != 0x90) - goto out; /* We failed loopback test */ - } - serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */ - serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */ - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - scratch = serial_in(up, UART_IIR) >> 6; - switch (scratch) { - case 0: - up->port.type = PORT_16450; - break; - case 1: - up->port.type = PORT_UNKNOWN; - break; - case 2: - up->port.type = PORT_16550; - break; - case 3: - up->port.type = PORT_16550A; - break; - } - if (up->port.type == PORT_16550A) { - /* Check for Startech UART's */ - serial_outp(up, UART_LCR, UART_LCR_DLAB); - if (serial_in(up, UART_EFR) == 0) { - up->port.type = PORT_16650; - } else { - serial_outp(up, UART_LCR, 0xBF); - if (serial_in(up, UART_EFR) == 0) - up->port.type = PORT_16650V2; - } - } - if (up->port.type == PORT_16550A) { - /* Check for TI 16750 */ - serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB); - serial_outp(up, UART_FCR, - UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - scratch = serial_in(up, UART_IIR) >> 5; - if (scratch == 7) { - /* - * If this is a 16750, and not a cheap UART - * clone, then it should only go into 64 byte - * mode if the UART_FCR7_64BYTE bit was set - * while UART_LCR_DLAB was latched. - */ - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, 0); - serial_outp(up, UART_FCR, - UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); - scratch = serial_in(up, UART_IIR) >> 5; - if (scratch == 6) - up->port.type = PORT_16750; - } - serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - } - serial_outp(up, UART_LCR, save_lcr); - if (up->port.type == PORT_16450) { - scratch = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0xa5); - status1 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, 0x5a); - status2 = serial_in(up, UART_SCR); - serial_outp(up, UART_SCR, scratch); - - if ((status1 != 0xa5) || (status2 != 0x5a)) - up->port.type = PORT_8250; - } - - up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; - - if (up->port.type == PORT_UNKNOWN) - goto out; - up->type_probed = up->port.type; /* XXX */ - - /* - * Reset the UART. - */ -#ifdef CONFIG_SERIAL_8250_RSA - if (up->port.type == PORT_RSA) - serial_outp(up, UART_RSA_FRR, 0); -#endif - serial_outp(up, UART_MCR, save_mcr); - serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | - UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT)); - serial_outp(up, UART_FCR, 0); - (void)serial_in(up, UART_RX); - serial_outp(up, UART_IER, 0); - -out: - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static struct uart_driver sunsu_reg = { - .owner = THIS_MODULE, - .driver_name = "sunsu", - .dev_name = "ttyS", - .major = TTY_MAJOR, -}; - -static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up) -{ - int quot, baud; -#ifdef CONFIG_SERIO - struct serio *serio; -#endif - - if (up->su_type == SU_PORT_KBD) { - up->cflag = B1200 | CS8 | CLOCAL | CREAD; - baud = 1200; - } else { - up->cflag = B4800 | CS8 | CLOCAL | CREAD; - baud = 4800; - } - quot = up->port.uartclk / (16 * baud); - - sunsu_autoconfig(up); - if (up->port.type == PORT_UNKNOWN) - return -ENODEV; - - printk("%s: %s port at %llx, irq %u\n", - up->port.dev->of_node->full_name, - (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse", - (unsigned long long) up->port.mapbase, - up->port.irq); - -#ifdef CONFIG_SERIO - serio = &up->serio; - serio->port_data = up; - - serio->id.type = SERIO_RS232; - if (up->su_type == SU_PORT_KBD) { - serio->id.proto = SERIO_SUNKBD; - strlcpy(serio->name, "sukbd", sizeof(serio->name)); - } else { - serio->id.proto = SERIO_SUN; - serio->id.extra = 1; - strlcpy(serio->name, "sums", sizeof(serio->name)); - } - strlcpy(serio->phys, - (!(up->port.line & 1) ? "su/serio0" : "su/serio1"), - sizeof(serio->phys)); - - serio->write = sunsu_serio_write; - serio->open = sunsu_serio_open; - serio->close = sunsu_serio_close; - serio->dev.parent = up->port.dev; - - serio_register_port(serio); -#endif - - sunsu_change_speed(&up->port, up->cflag, 0, quot); - - sunsu_startup(&up->port); - return 0; -} - -/* - * ------------------------------------------------------------ - * Serial console driver - * ------------------------------------------------------------ - */ - -#ifdef CONFIG_SERIAL_SUNSU_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -/* - * Wait for transmitter & holding register to empty - */ -static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = serial_in(up, UART_LSR); - - if (status & UART_LSR_BI) - up->lsr_break_flag = UART_LSR_BI; - - if (--tmout == 0) - break; - udelay(1); - } while ((status & BOTH_EMPTY) != BOTH_EMPTY); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) - udelay(1); - } -} - -static void sunsu_console_putchar(struct uart_port *port, int ch) -{ - struct uart_sunsu_port *up = (struct uart_sunsu_port *)port; - - wait_for_xmitr(up); - serial_out(up, UART_TX, ch); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - */ -static void sunsu_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_sunsu_port *up = &sunsu_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - /* - * First save the UER then disable the interrupts - */ - ier = serial_in(up, UART_IER); - serial_out(up, UART_IER, 0); - - uart_console_write(&up->port, s, count, sunsu_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and restore the IER - */ - wait_for_xmitr(up); - serial_out(up, UART_IER, ier); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -/* - * Setup initial baud/bits/parity. We do two things here: - * - construct a cflag setting for the first su_open() - * - initialize the serial port - * Return non-zero if we didn't find a serial port. - */ -static int __init sunsu_console_setup(struct console *co, char *options) -{ - static struct ktermios dummy; - struct ktermios termios; - struct uart_port *port; - - printk("Console: ttyS%d (SU)\n", - (sunsu_reg.minor - 64) + co->index); - - /* - * Check whether an invalid uart number has been specified, and - * if so, search for the first available port that does have - * console support. - */ - if (co->index >= UART_NR) - co->index = 0; - port = &sunsu_ports[co->index].port; - - /* - * Temporary fix. - */ - spin_lock_init(&port->lock); - - /* Get firmware console settings. */ - sunserial_console_termios(co, port->dev->of_node); - - memset(&termios, 0, sizeof(struct ktermios)); - termios.c_cflag = co->cflag; - port->mctrl |= TIOCM_DTR; - port->ops->set_termios(port, &termios, &dummy); - - return 0; -} - -static struct console sunsu_console = { - .name = "ttyS", - .write = sunsu_console_write, - .device = uart_console_device, - .setup = sunsu_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sunsu_reg, -}; - -/* - * Register console. - */ - -static inline struct console *SUNSU_CONSOLE(void) -{ - return &sunsu_console; -} -#else -#define SUNSU_CONSOLE() (NULL) -#define sunsu_serial_console_init() do { } while (0) -#endif - -static enum su_type __devinit su_get_type(struct device_node *dp) -{ - struct device_node *ap = of_find_node_by_path("/aliases"); - - if (ap) { - const char *keyb = of_get_property(ap, "keyboard", NULL); - const char *ms = of_get_property(ap, "mouse", NULL); - - if (keyb) { - if (dp == of_find_node_by_path(keyb)) - return SU_PORT_KBD; - } - if (ms) { - if (dp == of_find_node_by_path(ms)) - return SU_PORT_MS; - } - } - - return SU_PORT_PORT; -} - -static int __devinit su_probe(struct platform_device *op, const struct of_device_id *match) -{ - static int inst; - struct device_node *dp = op->dev.of_node; - struct uart_sunsu_port *up; - struct resource *rp; - enum su_type type; - bool ignore_line; - int err; - - type = su_get_type(dp); - if (type == SU_PORT_PORT) { - if (inst >= UART_NR) - return -EINVAL; - up = &sunsu_ports[inst]; - } else { - up = kzalloc(sizeof(*up), GFP_KERNEL); - if (!up) - return -ENOMEM; - } - - up->port.line = inst; - - spin_lock_init(&up->port.lock); - - up->su_type = type; - - rp = &op->resource[0]; - up->port.mapbase = rp->start; - up->reg_size = (rp->end - rp->start) + 1; - up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); - if (!up->port.membase) { - if (type != SU_PORT_PORT) - kfree(up); - return -ENOMEM; - } - - up->port.irq = op->archdata.irqs[0]; - - up->port.dev = &op->dev; - - up->port.type = PORT_UNKNOWN; - up->port.uartclk = (SU_BASE_BAUD * 16); - - err = 0; - if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) { - err = sunsu_kbd_ms_init(up); - if (err) { - of_iounmap(&op->resource[0], - up->port.membase, up->reg_size); - kfree(up); - return err; - } - dev_set_drvdata(&op->dev, up); - - return 0; - } - - up->port.flags |= UPF_BOOT_AUTOCONF; - - sunsu_autoconfig(up); - - err = -ENODEV; - if (up->port.type == PORT_UNKNOWN) - goto out_unmap; - - up->port.ops = &sunsu_pops; - - ignore_line = false; - if (!strcmp(dp->name, "rsc-console") || - !strcmp(dp->name, "lom-console")) - ignore_line = true; - - sunserial_console_match(SUNSU_CONSOLE(), dp, - &sunsu_reg, up->port.line, - ignore_line); - err = uart_add_one_port(&sunsu_reg, &up->port); - if (err) - goto out_unmap; - - dev_set_drvdata(&op->dev, up); - - inst++; - - return 0; - -out_unmap: - of_iounmap(&op->resource[0], up->port.membase, up->reg_size); - return err; -} - -static int __devexit su_remove(struct platform_device *op) -{ - struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); - bool kbdms = false; - - if (up->su_type == SU_PORT_MS || - up->su_type == SU_PORT_KBD) - kbdms = true; - - if (kbdms) { -#ifdef CONFIG_SERIO - serio_unregister_port(&up->serio); -#endif - } else if (up->port.type != PORT_UNKNOWN) - uart_remove_one_port(&sunsu_reg, &up->port); - - if (up->port.membase) - of_iounmap(&op->resource[0], up->port.membase, up->reg_size); - - if (kbdms) - kfree(up); - - dev_set_drvdata(&op->dev, NULL); - - return 0; -} - -static const struct of_device_id su_match[] = { - { - .name = "su", - }, - { - .name = "su_pnp", - }, - { - .name = "serial", - .compatible = "su", - }, - { - .type = "serial", - .compatible = "su", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, su_match); - -static struct of_platform_driver su_driver = { - .driver = { - .name = "su", - .owner = THIS_MODULE, - .of_match_table = su_match, - }, - .probe = su_probe, - .remove = __devexit_p(su_remove), -}; - -static int __init sunsu_init(void) -{ - struct device_node *dp; - int err; - int num_uart = 0; - - for_each_node_by_name(dp, "su") { - if (su_get_type(dp) == SU_PORT_PORT) - num_uart++; - } - for_each_node_by_name(dp, "su_pnp") { - if (su_get_type(dp) == SU_PORT_PORT) - num_uart++; - } - for_each_node_by_name(dp, "serial") { - if (of_device_is_compatible(dp, "su")) { - if (su_get_type(dp) == SU_PORT_PORT) - num_uart++; - } - } - for_each_node_by_type(dp, "serial") { - if (of_device_is_compatible(dp, "su")) { - if (su_get_type(dp) == SU_PORT_PORT) - num_uart++; - } - } - - if (num_uart) { - err = sunserial_register_minors(&sunsu_reg, num_uart); - if (err) - return err; - } - - err = of_register_platform_driver(&su_driver); - if (err && num_uart) - sunserial_unregister_minors(&sunsu_reg, num_uart); - - return err; -} - -static void __exit sunsu_exit(void) -{ - if (sunsu_reg.nr) - sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr); -} - -module_init(sunsu_init); -module_exit(sunsu_exit); - -MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller"); -MODULE_DESCRIPTION("Sun SU serial port driver"); -MODULE_VERSION("2.0"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c deleted file mode 100644 index c1967ac1c07f..000000000000 --- a/drivers/serial/sunzilog.c +++ /dev/null @@ -1,1655 +0,0 @@ -/* sunzilog.c: Zilog serial driver for Sparc systems. - * - * Driver for Zilog serial chips found on Sun workstations and - * servers. This driver could actually be made more generic. - * - * This is based on the old drivers/sbus/char/zs.c code. A lot - * of code has been simply moved over directly from there but - * much has been rewritten. Credits therefore go out to Eddie - * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their - * work there. - * - * Copyright (C) 2002, 2006, 2007 David S. Miller (davem@davemloft.net) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef CONFIG_SERIO -#include -#endif -#include -#include - -#include -#include -#include - -#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include - -#include "suncore.h" -#include "sunzilog.h" - -/* On 32-bit sparcs we need to delay after register accesses - * to accommodate sun4 systems, but we do not need to flush writes. - * On 64-bit sparc we only need to flush single writes to ensure - * completion. - */ -#ifndef CONFIG_SPARC64 -#define ZSDELAY() udelay(5) -#define ZSDELAY_LONG() udelay(20) -#define ZS_WSYNC(channel) do { } while (0) -#else -#define ZSDELAY() -#define ZSDELAY_LONG() -#define ZS_WSYNC(__channel) \ - readb(&((__channel)->control)) -#endif - -#define ZS_CLOCK 4915200 /* Zilog input clock rate. */ -#define ZS_CLOCK_DIVISOR 16 /* Divisor this driver uses. */ - -/* - * We wrap our port structure around the generic uart_port. - */ -struct uart_sunzilog_port { - struct uart_port port; - - /* IRQ servicing chain. */ - struct uart_sunzilog_port *next; - - /* Current values of Zilog write registers. */ - unsigned char curregs[NUM_ZSREGS]; - - unsigned int flags; -#define SUNZILOG_FLAG_CONS_KEYB 0x00000001 -#define SUNZILOG_FLAG_CONS_MOUSE 0x00000002 -#define SUNZILOG_FLAG_IS_CONS 0x00000004 -#define SUNZILOG_FLAG_IS_KGDB 0x00000008 -#define SUNZILOG_FLAG_MODEM_STATUS 0x00000010 -#define SUNZILOG_FLAG_IS_CHANNEL_A 0x00000020 -#define SUNZILOG_FLAG_REGS_HELD 0x00000040 -#define SUNZILOG_FLAG_TX_STOPPED 0x00000080 -#define SUNZILOG_FLAG_TX_ACTIVE 0x00000100 -#define SUNZILOG_FLAG_ESCC 0x00000200 -#define SUNZILOG_FLAG_ISR_HANDLER 0x00000400 - - unsigned int cflag; - - unsigned char parity_mask; - unsigned char prev_status; - -#ifdef CONFIG_SERIO - struct serio serio; - int serio_open; -#endif -}; - -static void sunzilog_putchar(struct uart_port *port, int ch); - -#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase)) -#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) - -#define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB) -#define ZS_IS_MOUSE(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE) -#define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS) -#define ZS_IS_KGDB(UP) ((UP)->flags & SUNZILOG_FLAG_IS_KGDB) -#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & SUNZILOG_FLAG_MODEM_STATUS) -#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CHANNEL_A) -#define ZS_REGS_HELD(UP) ((UP)->flags & SUNZILOG_FLAG_REGS_HELD) -#define ZS_TX_STOPPED(UP) ((UP)->flags & SUNZILOG_FLAG_TX_STOPPED) -#define ZS_TX_ACTIVE(UP) ((UP)->flags & SUNZILOG_FLAG_TX_ACTIVE) - -/* Reading and writing Zilog8530 registers. The delays are to make this - * driver work on the Sun4 which needs a settling delay after each chip - * register access, other machines handle this in hardware via auxiliary - * flip-flops which implement the settle time we do in software. - * - * The port lock must be held and local IRQs must be disabled - * when {read,write}_zsreg is invoked. - */ -static unsigned char read_zsreg(struct zilog_channel __iomem *channel, - unsigned char reg) -{ - unsigned char retval; - - writeb(reg, &channel->control); - ZSDELAY(); - retval = readb(&channel->control); - ZSDELAY(); - - return retval; -} - -static void write_zsreg(struct zilog_channel __iomem *channel, - unsigned char reg, unsigned char value) -{ - writeb(reg, &channel->control); - ZSDELAY(); - writeb(value, &channel->control); - ZSDELAY(); -} - -static void sunzilog_clear_fifo(struct zilog_channel __iomem *channel) -{ - int i; - - for (i = 0; i < 32; i++) { - unsigned char regval; - - regval = readb(&channel->control); - ZSDELAY(); - if (regval & Rx_CH_AV) - break; - - regval = read_zsreg(channel, R1); - readb(&channel->data); - ZSDELAY(); - - if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) { - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - } - } -} - -/* This function must only be called when the TX is not busy. The UART - * port lock must be held and local interrupts disabled. - */ -static int __load_zsregs(struct zilog_channel __iomem *channel, unsigned char *regs) -{ - int i; - int escc; - unsigned char r15; - - /* Let pending transmits finish. */ - for (i = 0; i < 1000; i++) { - unsigned char stat = read_zsreg(channel, R1); - if (stat & ALL_SNT) - break; - udelay(100); - } - - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - sunzilog_clear_fifo(channel); - - /* Disable all interrupts. */ - write_zsreg(channel, R1, - regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); - - /* Set parity, sync config, stop bits, and clock divisor. */ - write_zsreg(channel, R4, regs[R4]); - - /* Set misc. TX/RX control bits. */ - write_zsreg(channel, R10, regs[R10]); - - /* Set TX/RX controls sans the enable bits. */ - write_zsreg(channel, R3, regs[R3] & ~RxENAB); - write_zsreg(channel, R5, regs[R5] & ~TxENAB); - - /* Synchronous mode config. */ - write_zsreg(channel, R6, regs[R6]); - write_zsreg(channel, R7, regs[R7]); - - /* Don't mess with the interrupt vector (R2, unused by us) and - * master interrupt control (R9). We make sure this is setup - * properly at probe time then never touch it again. - */ - - /* Disable baud generator. */ - write_zsreg(channel, R14, regs[R14] & ~BRENAB); - - /* Clock mode control. */ - write_zsreg(channel, R11, regs[R11]); - - /* Lower and upper byte of baud rate generator divisor. */ - write_zsreg(channel, R12, regs[R12]); - write_zsreg(channel, R13, regs[R13]); - - /* Now rewrite R14, with BRENAB (if set). */ - write_zsreg(channel, R14, regs[R14]); - - /* External status interrupt control. */ - write_zsreg(channel, R15, (regs[R15] | WR7pEN) & ~FIFOEN); - - /* ESCC Extension Register */ - r15 = read_zsreg(channel, R15); - if (r15 & 0x01) { - write_zsreg(channel, R7, regs[R7p]); - - /* External status interrupt and FIFO control. */ - write_zsreg(channel, R15, regs[R15] & ~WR7pEN); - escc = 1; - } else { - /* Clear FIFO bit case it is an issue */ - regs[R15] &= ~FIFOEN; - escc = 0; - } - - /* Reset external status interrupts. */ - write_zsreg(channel, R0, RES_EXT_INT); /* First Latch */ - write_zsreg(channel, R0, RES_EXT_INT); /* Second Latch */ - - /* Rewrite R3/R5, this time without enables masked. */ - write_zsreg(channel, R3, regs[R3]); - write_zsreg(channel, R5, regs[R5]); - - /* Rewrite R1, this time without IRQ enabled masked. */ - write_zsreg(channel, R1, regs[R1]); - - return escc; -} - -/* Reprogram the Zilog channel HW registers with the copies found in the - * software state struct. If the transmitter is busy, we defer this update - * until the next TX complete interrupt. Else, we do it right now. - * - * The UART port lock must be held and local interrupts disabled. - */ -static void sunzilog_maybe_update_regs(struct uart_sunzilog_port *up, - struct zilog_channel __iomem *channel) -{ - if (!ZS_REGS_HELD(up)) { - if (ZS_TX_ACTIVE(up)) { - up->flags |= SUNZILOG_FLAG_REGS_HELD; - } else { - __load_zsregs(channel, up->curregs); - } - } -} - -static void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up) -{ - unsigned int cur_cflag = up->cflag; - int brg, new_baud; - - up->cflag &= ~CBAUD; - up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud); - - brg = BPS_TO_BRG(new_baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - up->curregs[R12] = (brg & 0xff); - up->curregs[R13] = (brg >> 8) & 0xff; - sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port)); -} - -static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up, - unsigned char ch, int is_break) -{ - if (ZS_IS_KEYB(up)) { - /* Stop-A is handled by drivers/char/keyboard.c now. */ -#ifdef CONFIG_SERIO - if (up->serio_open) - serio_interrupt(&up->serio, ch, 0); -#endif - } else if (ZS_IS_MOUSE(up)) { - int ret = suncore_mouse_baud_detection(ch, is_break); - - switch (ret) { - case 2: - sunzilog_change_mouse_baud(up); - /* fallthru */ - case 1: - break; - - case 0: -#ifdef CONFIG_SERIO - if (up->serio_open) - serio_interrupt(&up->serio, ch, 0); -#endif - break; - }; - } -} - -static struct tty_struct * -sunzilog_receive_chars(struct uart_sunzilog_port *up, - struct zilog_channel __iomem *channel) -{ - struct tty_struct *tty; - unsigned char ch, r1, flag; - - tty = NULL; - if (up->port.state != NULL && /* Unopened serial console */ - up->port.state->port.tty != NULL) /* Keyboard || mouse */ - tty = up->port.state->port.tty; - - for (;;) { - - r1 = read_zsreg(channel, R1); - if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - } - - ch = readb(&channel->control); - ZSDELAY(); - - /* This funny hack depends upon BRK_ABRT not interfering - * with the other bits we care about in R1. - */ - if (ch & BRK_ABRT) - r1 |= BRK_ABRT; - - if (!(ch & Rx_CH_AV)) - break; - - ch = readb(&channel->data); - ZSDELAY(); - - ch &= up->parity_mask; - - if (unlikely(ZS_IS_KEYB(up)) || unlikely(ZS_IS_MOUSE(up))) { - sunzilog_kbdms_receive_chars(up, ch, 0); - continue; - } - - if (tty == NULL) { - uart_handle_sysrq_char(&up->port, ch); - continue; - } - - /* A real serial line, record the character and status. */ - flag = TTY_NORMAL; - up->port.icount.rx++; - if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) { - if (r1 & BRK_ABRT) { - r1 &= ~(PAR_ERR | CRC_ERR); - up->port.icount.brk++; - if (uart_handle_break(&up->port)) - continue; - } - else if (r1 & PAR_ERR) - up->port.icount.parity++; - else if (r1 & CRC_ERR) - up->port.icount.frame++; - if (r1 & Rx_OVR) - up->port.icount.overrun++; - r1 &= up->port.read_status_mask; - if (r1 & BRK_ABRT) - flag = TTY_BREAK; - else if (r1 & PAR_ERR) - flag = TTY_PARITY; - else if (r1 & CRC_ERR) - flag = TTY_FRAME; - } - if (uart_handle_sysrq_char(&up->port, ch)) - continue; - - if (up->port.ignore_status_mask == 0xff || - (r1 & up->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); - } - if (r1 & Rx_OVR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - - return tty; -} - -static void sunzilog_status_handle(struct uart_sunzilog_port *up, - struct zilog_channel __iomem *channel) -{ - unsigned char status; - - status = readb(&channel->control); - ZSDELAY(); - - writeb(RES_EXT_INT, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - if (status & BRK_ABRT) { - if (ZS_IS_MOUSE(up)) - sunzilog_kbdms_receive_chars(up, 0, 1); - if (ZS_IS_CONS(up)) { - /* Wait for BREAK to deassert to avoid potentially - * confusing the PROM. - */ - while (1) { - status = readb(&channel->control); - ZSDELAY(); - if (!(status & BRK_ABRT)) - break; - } - sun_do_break(); - return; - } - } - - if (ZS_WANTS_MODEM_STATUS(up)) { - if (status & SYNC) - up->port.icount.dsr++; - - /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. - * But it does not tell us which bit has changed, we have to keep - * track of this ourselves. - */ - if ((status ^ up->prev_status) ^ DCD) - uart_handle_dcd_change(&up->port, - (status & DCD)); - if ((status ^ up->prev_status) ^ CTS) - uart_handle_cts_change(&up->port, - (status & CTS)); - - wake_up_interruptible(&up->port.state->port.delta_msr_wait); - } - - up->prev_status = status; -} - -static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, - struct zilog_channel __iomem *channel) -{ - struct circ_buf *xmit; - - if (ZS_IS_CONS(up)) { - unsigned char status = readb(&channel->control); - ZSDELAY(); - - /* TX still busy? Just wait for the next TX done interrupt. - * - * It can occur because of how we do serial console writes. It would - * be nice to transmit console writes just like we normally would for - * a TTY line. (ie. buffered and TX interrupt driven). That is not - * easy because console writes cannot sleep. One solution might be - * to poll on enough port->xmit space becomming free. -DaveM - */ - if (!(status & Tx_BUF_EMP)) - return; - } - - up->flags &= ~SUNZILOG_FLAG_TX_ACTIVE; - - if (ZS_REGS_HELD(up)) { - __load_zsregs(channel, up->curregs); - up->flags &= ~SUNZILOG_FLAG_REGS_HELD; - } - - if (ZS_TX_STOPPED(up)) { - up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; - goto ack_tx_int; - } - - if (up->port.x_char) { - up->flags |= SUNZILOG_FLAG_TX_ACTIVE; - writeb(up->port.x_char, &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - up->port.icount.tx++; - up->port.x_char = 0; - return; - } - - if (up->port.state == NULL) - goto ack_tx_int; - xmit = &up->port.state->xmit; - if (uart_circ_empty(xmit)) - goto ack_tx_int; - - if (uart_tx_stopped(&up->port)) - goto ack_tx_int; - - up->flags |= SUNZILOG_FLAG_TX_ACTIVE; - writeb(xmit->buf[xmit->tail], &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - up->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - - return; - -ack_tx_int: - writeb(RES_Tx_P, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); -} - -static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) -{ - struct uart_sunzilog_port *up = dev_id; - - while (up) { - struct zilog_channel __iomem *channel - = ZILOG_CHANNEL_FROM_PORT(&up->port); - struct tty_struct *tty; - unsigned char r3; - - spin_lock(&up->port.lock); - r3 = read_zsreg(channel, R3); - - /* Channel A */ - tty = NULL; - if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { - writeb(RES_H_IUS, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - if (r3 & CHARxIP) - tty = sunzilog_receive_chars(up, channel); - if (r3 & CHAEXT) - sunzilog_status_handle(up, channel); - if (r3 & CHATxIP) - sunzilog_transmit_chars(up, channel); - } - spin_unlock(&up->port.lock); - - if (tty) - tty_flip_buffer_push(tty); - - /* Channel B */ - up = up->next; - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - - spin_lock(&up->port.lock); - tty = NULL; - if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { - writeb(RES_H_IUS, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - - if (r3 & CHBRxIP) - tty = sunzilog_receive_chars(up, channel); - if (r3 & CHBEXT) - sunzilog_status_handle(up, channel); - if (r3 & CHBTxIP) - sunzilog_transmit_chars(up, channel); - } - spin_unlock(&up->port.lock); - - if (tty) - tty_flip_buffer_push(tty); - - up = up->next; - } - - return IRQ_HANDLED; -} - -/* A convenient way to quickly get R0 status. The caller must _not_ hold the - * port lock, it is acquired here. - */ -static __inline__ unsigned char sunzilog_read_channel_status(struct uart_port *port) -{ - struct zilog_channel __iomem *channel; - unsigned char status; - - channel = ZILOG_CHANNEL_FROM_PORT(port); - status = readb(&channel->control); - ZSDELAY(); - - return status; -} - -/* The port lock is not held. */ -static unsigned int sunzilog_tx_empty(struct uart_port *port) -{ - unsigned long flags; - unsigned char status; - unsigned int ret; - - spin_lock_irqsave(&port->lock, flags); - - status = sunzilog_read_channel_status(port); - - spin_unlock_irqrestore(&port->lock, flags); - - if (status & Tx_BUF_EMP) - ret = TIOCSER_TEMT; - else - ret = 0; - - return ret; -} - -/* The port lock is held and interrupts are disabled. */ -static unsigned int sunzilog_get_mctrl(struct uart_port *port) -{ - unsigned char status; - unsigned int ret; - - status = sunzilog_read_channel_status(port); - - ret = 0; - if (status & DCD) - ret |= TIOCM_CAR; - if (status & SYNC) - ret |= TIOCM_DSR; - if (status & CTS) - ret |= TIOCM_CTS; - - return ret; -} - -/* The port lock is held and interrupts are disabled. */ -static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char set_bits, clear_bits; - - set_bits = clear_bits = 0; - - if (mctrl & TIOCM_RTS) - set_bits |= RTS; - else - clear_bits |= RTS; - if (mctrl & TIOCM_DTR) - set_bits |= DTR; - else - clear_bits |= DTR; - - /* NOTE: Not subject to 'transmitter active' rule. */ - up->curregs[R5] |= set_bits; - up->curregs[R5] &= ~clear_bits; - write_zsreg(channel, R5, up->curregs[R5]); -} - -/* The port lock is held and interrupts are disabled. */ -static void sunzilog_stop_tx(struct uart_port *port) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - - up->flags |= SUNZILOG_FLAG_TX_STOPPED; -} - -/* The port lock is held and interrupts are disabled. */ -static void sunzilog_start_tx(struct uart_port *port) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char status; - - up->flags |= SUNZILOG_FLAG_TX_ACTIVE; - up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; - - status = readb(&channel->control); - ZSDELAY(); - - /* TX busy? Just wait for the TX done interrupt. */ - if (!(status & Tx_BUF_EMP)) - return; - - /* Send the first character to jump-start the TX done - * IRQ sending engine. - */ - if (port->x_char) { - writeb(port->x_char, &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - port->icount.tx++; - port->x_char = 0; - } else { - struct circ_buf *xmit = &port->state->xmit; - - writeb(xmit->buf[xmit->tail], &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&up->port); - } -} - -/* The port lock is held. */ -static void sunzilog_stop_rx(struct uart_port *port) -{ - struct uart_sunzilog_port *up = UART_ZILOG(port); - struct zilog_channel __iomem *channel; - - if (ZS_IS_CONS(up)) - return; - - channel = ZILOG_CHANNEL_FROM_PORT(port); - - /* Disable all RX interrupts. */ - up->curregs[R1] &= ~RxINT_MASK; - sunzilog_maybe_update_regs(up, channel); -} - -/* The port lock is held. */ -static void sunzilog_enable_ms(struct uart_port *port) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char new_reg; - - new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE); - if (new_reg != up->curregs[R15]) { - up->curregs[R15] = new_reg; - - /* NOTE: Not subject to 'transmitter active' rule. */ - write_zsreg(channel, R15, up->curregs[R15] & ~WR7pEN); - } -} - -/* The port lock is not held. */ -static void sunzilog_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); - unsigned char set_bits, clear_bits, new_reg; - unsigned long flags; - - set_bits = clear_bits = 0; - - if (break_state) - set_bits |= SND_BRK; - else - clear_bits |= SND_BRK; - - spin_lock_irqsave(&port->lock, flags); - - new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; - if (new_reg != up->curregs[R5]) { - up->curregs[R5] = new_reg; - - /* NOTE: Not subject to 'transmitter active' rule. */ - write_zsreg(channel, R5, up->curregs[R5]); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void __sunzilog_startup(struct uart_sunzilog_port *up) -{ - struct zilog_channel __iomem *channel; - - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - up->prev_status = readb(&channel->control); - - /* Enable receiver and transmitter. */ - up->curregs[R3] |= RxENAB; - up->curregs[R5] |= TxENAB; - - up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; - sunzilog_maybe_update_regs(up, channel); -} - -static int sunzilog_startup(struct uart_port *port) -{ - struct uart_sunzilog_port *up = UART_ZILOG(port); - unsigned long flags; - - if (ZS_IS_CONS(up)) - return 0; - - spin_lock_irqsave(&port->lock, flags); - __sunzilog_startup(up); - spin_unlock_irqrestore(&port->lock, flags); - return 0; -} - -/* - * The test for ZS_IS_CONS is explained by the following e-mail: - ***** - * From: Russell King - * Date: Sun, 8 Dec 2002 10:18:38 +0000 - * - * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: - * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, - * > and I noticed that something is not right with reference - * > counting in this case. It seems that when the console - * > is open by kernel initially, this is not accounted - * > as an open, and uart_startup is not called. - * - * That is correct. We are unable to call uart_startup when the serial - * console is initialised because it may need to allocate memory (as - * request_irq does) and the memory allocators may not have been - * initialised. - * - * 1. initialise the port into a state where it can send characters in the - * console write method. - * - * 2. don't do the actual hardware shutdown in your shutdown() method (but - * do the normal software shutdown - ie, free irqs etc) - ***** - */ -static void sunzilog_shutdown(struct uart_port *port) -{ - struct uart_sunzilog_port *up = UART_ZILOG(port); - struct zilog_channel __iomem *channel; - unsigned long flags; - - if (ZS_IS_CONS(up)) - return; - - spin_lock_irqsave(&port->lock, flags); - - channel = ZILOG_CHANNEL_FROM_PORT(port); - - /* Disable receiver and transmitter. */ - up->curregs[R3] &= ~RxENAB; - up->curregs[R5] &= ~TxENAB; - - /* Disable all interrupts and BRK assertion. */ - up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); - up->curregs[R5] &= ~SND_BRK; - sunzilog_maybe_update_regs(up, channel); - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* Shared by TTY driver and serial console setup. The port lock is held - * and local interrupts are disabled. - */ -static void -sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, - unsigned int iflag, int brg) -{ - - up->curregs[R10] = NRZ; - up->curregs[R11] = TCBR | RCBR; - - /* Program BAUD and clock source. */ - up->curregs[R4] &= ~XCLK_MASK; - up->curregs[R4] |= X16CLK; - up->curregs[R12] = brg & 0xff; - up->curregs[R13] = (brg >> 8) & 0xff; - up->curregs[R14] = BRSRC | BRENAB; - - /* Character size, stop bits, and parity. */ - up->curregs[R3] &= ~RxN_MASK; - up->curregs[R5] &= ~TxN_MASK; - switch (cflag & CSIZE) { - case CS5: - up->curregs[R3] |= Rx5; - up->curregs[R5] |= Tx5; - up->parity_mask = 0x1f; - break; - case CS6: - up->curregs[R3] |= Rx6; - up->curregs[R5] |= Tx6; - up->parity_mask = 0x3f; - break; - case CS7: - up->curregs[R3] |= Rx7; - up->curregs[R5] |= Tx7; - up->parity_mask = 0x7f; - break; - case CS8: - default: - up->curregs[R3] |= Rx8; - up->curregs[R5] |= Tx8; - up->parity_mask = 0xff; - break; - }; - up->curregs[R4] &= ~0x0c; - if (cflag & CSTOPB) - up->curregs[R4] |= SB2; - else - up->curregs[R4] |= SB1; - if (cflag & PARENB) - up->curregs[R4] |= PAR_ENAB; - else - up->curregs[R4] &= ~PAR_ENAB; - if (!(cflag & PARODD)) - up->curregs[R4] |= PAR_EVEN; - else - up->curregs[R4] &= ~PAR_EVEN; - - up->port.read_status_mask = Rx_OVR; - if (iflag & INPCK) - up->port.read_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & (BRKINT | PARMRK)) - up->port.read_status_mask |= BRK_ABRT; - - up->port.ignore_status_mask = 0; - if (iflag & IGNPAR) - up->port.ignore_status_mask |= CRC_ERR | PAR_ERR; - if (iflag & IGNBRK) { - up->port.ignore_status_mask |= BRK_ABRT; - if (iflag & IGNPAR) - up->port.ignore_status_mask |= Rx_OVR; - } - - if ((cflag & CREAD) == 0) - up->port.ignore_status_mask = 0xff; -} - -/* The port lock is not held. */ -static void -sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - unsigned long flags; - int baud, brg; - - baud = uart_get_baud_rate(port, termios, old, 1200, 76800); - - spin_lock_irqsave(&up->port.lock, flags); - - brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - - sunzilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg); - - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->flags |= SUNZILOG_FLAG_MODEM_STATUS; - else - up->flags &= ~SUNZILOG_FLAG_MODEM_STATUS; - - up->cflag = termios->c_cflag; - - sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); - - uart_update_timeout(port, termios->c_cflag, baud); - - spin_unlock_irqrestore(&up->port.lock, flags); -} - -static const char *sunzilog_type(struct uart_port *port) -{ - struct uart_sunzilog_port *up = UART_ZILOG(port); - - return (up->flags & SUNZILOG_FLAG_ESCC) ? "zs (ESCC)" : "zs"; -} - -/* We do not request/release mappings of the registers here, this - * happens at early serial probe time. - */ -static void sunzilog_release_port(struct uart_port *port) -{ -} - -static int sunzilog_request_port(struct uart_port *port) -{ - return 0; -} - -/* These do not need to do anything interesting either. */ -static void sunzilog_config_port(struct uart_port *port, int flags) -{ -} - -/* We do not support letting the user mess with the divisor, IRQ, etc. */ -static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - return -EINVAL; -} - -#ifdef CONFIG_CONSOLE_POLL -static int sunzilog_get_poll_char(struct uart_port *port) -{ - unsigned char ch, r1; - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; - struct zilog_channel __iomem *channel - = ZILOG_CHANNEL_FROM_PORT(&up->port); - - - r1 = read_zsreg(channel, R1); - if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { - writeb(ERR_RES, &channel->control); - ZSDELAY(); - ZS_WSYNC(channel); - } - - ch = readb(&channel->control); - ZSDELAY(); - - /* This funny hack depends upon BRK_ABRT not interfering - * with the other bits we care about in R1. - */ - if (ch & BRK_ABRT) - r1 |= BRK_ABRT; - - if (!(ch & Rx_CH_AV)) - return NO_POLL_CHAR; - - ch = readb(&channel->data); - ZSDELAY(); - - ch &= up->parity_mask; - return ch; -} - -static void sunzilog_put_poll_char(struct uart_port *port, - unsigned char ch) -{ - struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port; - - sunzilog_putchar(&up->port, ch); -} -#endif /* CONFIG_CONSOLE_POLL */ - -static struct uart_ops sunzilog_pops = { - .tx_empty = sunzilog_tx_empty, - .set_mctrl = sunzilog_set_mctrl, - .get_mctrl = sunzilog_get_mctrl, - .stop_tx = sunzilog_stop_tx, - .start_tx = sunzilog_start_tx, - .stop_rx = sunzilog_stop_rx, - .enable_ms = sunzilog_enable_ms, - .break_ctl = sunzilog_break_ctl, - .startup = sunzilog_startup, - .shutdown = sunzilog_shutdown, - .set_termios = sunzilog_set_termios, - .type = sunzilog_type, - .release_port = sunzilog_release_port, - .request_port = sunzilog_request_port, - .config_port = sunzilog_config_port, - .verify_port = sunzilog_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = sunzilog_get_poll_char, - .poll_put_char = sunzilog_put_poll_char, -#endif -}; - -static int uart_chip_count; -static struct uart_sunzilog_port *sunzilog_port_table; -static struct zilog_layout __iomem **sunzilog_chip_regs; - -static struct uart_sunzilog_port *sunzilog_irq_chain; - -static struct uart_driver sunzilog_reg = { - .owner = THIS_MODULE, - .driver_name = "sunzilog", - .dev_name = "ttyS", - .major = TTY_MAJOR, -}; - -static int __init sunzilog_alloc_tables(int num_sunzilog) -{ - struct uart_sunzilog_port *up; - unsigned long size; - int num_channels = num_sunzilog * 2; - int i; - - size = num_channels * sizeof(struct uart_sunzilog_port); - sunzilog_port_table = kzalloc(size, GFP_KERNEL); - if (!sunzilog_port_table) - return -ENOMEM; - - for (i = 0; i < num_channels; i++) { - up = &sunzilog_port_table[i]; - - spin_lock_init(&up->port.lock); - - if (i == 0) - sunzilog_irq_chain = up; - - if (i < num_channels - 1) - up->next = up + 1; - else - up->next = NULL; - } - - size = num_sunzilog * sizeof(struct zilog_layout __iomem *); - sunzilog_chip_regs = kzalloc(size, GFP_KERNEL); - if (!sunzilog_chip_regs) { - kfree(sunzilog_port_table); - sunzilog_irq_chain = NULL; - return -ENOMEM; - } - - return 0; -} - -static void sunzilog_free_tables(void) -{ - kfree(sunzilog_port_table); - sunzilog_irq_chain = NULL; - kfree(sunzilog_chip_regs); -} - -#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */ - -static void sunzilog_putchar(struct uart_port *port, int ch) -{ - struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); - int loops = ZS_PUT_CHAR_MAX_DELAY; - - /* This is a timed polling loop so do not switch the explicit - * udelay with ZSDELAY as that is a NOP on some platforms. -DaveM - */ - do { - unsigned char val = readb(&channel->control); - if (val & Tx_BUF_EMP) { - ZSDELAY(); - break; - } - udelay(5); - } while (--loops); - - writeb(ch, &channel->data); - ZSDELAY(); - ZS_WSYNC(channel); -} - -#ifdef CONFIG_SERIO - -static DEFINE_SPINLOCK(sunzilog_serio_lock); - -static int sunzilog_serio_write(struct serio *serio, unsigned char ch) -{ - struct uart_sunzilog_port *up = serio->port_data; - unsigned long flags; - - spin_lock_irqsave(&sunzilog_serio_lock, flags); - - sunzilog_putchar(&up->port, ch); - - spin_unlock_irqrestore(&sunzilog_serio_lock, flags); - - return 0; -} - -static int sunzilog_serio_open(struct serio *serio) -{ - struct uart_sunzilog_port *up = serio->port_data; - unsigned long flags; - int ret; - - spin_lock_irqsave(&sunzilog_serio_lock, flags); - if (!up->serio_open) { - up->serio_open = 1; - ret = 0; - } else - ret = -EBUSY; - spin_unlock_irqrestore(&sunzilog_serio_lock, flags); - - return ret; -} - -static void sunzilog_serio_close(struct serio *serio) -{ - struct uart_sunzilog_port *up = serio->port_data; - unsigned long flags; - - spin_lock_irqsave(&sunzilog_serio_lock, flags); - up->serio_open = 0; - spin_unlock_irqrestore(&sunzilog_serio_lock, flags); -} - -#endif /* CONFIG_SERIO */ - -#ifdef CONFIG_SERIAL_SUNZILOG_CONSOLE -static void -sunzilog_console_write(struct console *con, const char *s, unsigned int count) -{ - struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; - unsigned long flags; - int locked = 1; - - local_irq_save(flags); - if (up->port.sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = spin_trylock(&up->port.lock); - } else - spin_lock(&up->port.lock); - - uart_console_write(&up->port, s, count, sunzilog_putchar); - udelay(2); - - if (locked) - spin_unlock(&up->port.lock); - local_irq_restore(flags); -} - -static int __init sunzilog_console_setup(struct console *con, char *options) -{ - struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; - unsigned long flags; - int baud, brg; - - if (up->port.type != PORT_SUNZILOG) - return -1; - - printk(KERN_INFO "Console: ttyS%d (SunZilog zs%d)\n", - (sunzilog_reg.minor - 64) + con->index, con->index); - - /* Get firmware console settings. */ - sunserial_console_termios(con, up->port.dev->of_node); - - /* Firmware console speed is limited to 150-->38400 baud so - * this hackish cflag thing is OK. - */ - switch (con->cflag & CBAUD) { - case B150: baud = 150; break; - case B300: baud = 300; break; - case B600: baud = 600; break; - case B1200: baud = 1200; break; - case B2400: baud = 2400; break; - case B4800: baud = 4800; break; - default: case B9600: baud = 9600; break; - case B19200: baud = 19200; break; - case B38400: baud = 38400; break; - }; - - brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - - spin_lock_irqsave(&up->port.lock, flags); - - up->curregs[R15] |= BRKIE; - sunzilog_convert_to_zs(up, con->cflag, 0, brg); - - sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); - __sunzilog_startup(up); - - spin_unlock_irqrestore(&up->port.lock, flags); - - return 0; -} - -static struct console sunzilog_console_ops = { - .name = "ttyS", - .write = sunzilog_console_write, - .device = uart_console_device, - .setup = sunzilog_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &sunzilog_reg, -}; - -static inline struct console *SUNZILOG_CONSOLE(void) -{ - return &sunzilog_console_ops; -} - -#else -#define SUNZILOG_CONSOLE() (NULL) -#endif - -static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up) -{ - int baud, brg; - - if (up->flags & SUNZILOG_FLAG_CONS_KEYB) { - up->cflag = B1200 | CS8 | CLOCAL | CREAD; - baud = 1200; - } else { - up->cflag = B4800 | CS8 | CLOCAL | CREAD; - baud = 4800; - } - - up->curregs[R15] |= BRKIE; - brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - sunzilog_convert_to_zs(up, up->cflag, 0, brg); - sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); - __sunzilog_startup(up); -} - -#ifdef CONFIG_SERIO -static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up) -{ - struct serio *serio = &up->serio; - - serio->port_data = up; - - serio->id.type = SERIO_RS232; - if (up->flags & SUNZILOG_FLAG_CONS_KEYB) { - serio->id.proto = SERIO_SUNKBD; - strlcpy(serio->name, "zskbd", sizeof(serio->name)); - } else { - serio->id.proto = SERIO_SUN; - serio->id.extra = 1; - strlcpy(serio->name, "zsms", sizeof(serio->name)); - } - strlcpy(serio->phys, - ((up->flags & SUNZILOG_FLAG_CONS_KEYB) ? - "zs/serio0" : "zs/serio1"), - sizeof(serio->phys)); - - serio->write = sunzilog_serio_write; - serio->open = sunzilog_serio_open; - serio->close = sunzilog_serio_close; - serio->dev.parent = up->port.dev; - - serio_register_port(serio); -} -#endif - -static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) -{ - struct zilog_channel __iomem *channel; - unsigned long flags; - int baud, brg; - - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - - spin_lock_irqsave(&up->port.lock, flags); - if (ZS_IS_CHANNEL_A(up)) { - write_zsreg(channel, R9, FHWRES); - ZSDELAY_LONG(); - (void) read_zsreg(channel, R0); - } - - if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | - SUNZILOG_FLAG_CONS_MOUSE)) { - up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; - up->curregs[R4] = PAR_EVEN | X16CLK | SB1; - up->curregs[R3] = RxENAB | Rx8; - up->curregs[R5] = TxENAB | Tx8; - up->curregs[R6] = 0x00; /* SDLC Address */ - up->curregs[R7] = 0x7E; /* SDLC Flag */ - up->curregs[R9] = NV; - up->curregs[R7p] = 0x00; - sunzilog_init_kbdms(up); - /* Only enable interrupts if an ISR handler available */ - if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) - up->curregs[R9] |= MIE; - write_zsreg(channel, R9, up->curregs[R9]); - } else { - /* Normal serial TTY. */ - up->parity_mask = 0xff; - up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; - up->curregs[R4] = PAR_EVEN | X16CLK | SB1; - up->curregs[R3] = RxENAB | Rx8; - up->curregs[R5] = TxENAB | Tx8; - up->curregs[R6] = 0x00; /* SDLC Address */ - up->curregs[R7] = 0x7E; /* SDLC Flag */ - up->curregs[R9] = NV; - up->curregs[R10] = NRZ; - up->curregs[R11] = TCBR | RCBR; - baud = 9600; - brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); - up->curregs[R12] = (brg & 0xff); - up->curregs[R13] = (brg >> 8) & 0xff; - up->curregs[R14] = BRSRC | BRENAB; - up->curregs[R15] = FIFOEN; /* Use FIFO if on ESCC */ - up->curregs[R7p] = TxFIFO_LVL | RxFIFO_LVL; - if (__load_zsregs(channel, up->curregs)) { - up->flags |= SUNZILOG_FLAG_ESCC; - } - /* Only enable interrupts if an ISR handler available */ - if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) - up->curregs[R9] |= MIE; - write_zsreg(channel, R9, up->curregs[R9]); - } - - spin_unlock_irqrestore(&up->port.lock, flags); - -#ifdef CONFIG_SERIO - if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | - SUNZILOG_FLAG_CONS_MOUSE)) - sunzilog_register_serio(up); -#endif -} - -static int zilog_irq = -1; - -static int __devinit zs_probe(struct platform_device *op, const struct of_device_id *match) -{ - static int kbm_inst, uart_inst; - int inst; - struct uart_sunzilog_port *up; - struct zilog_layout __iomem *rp; - int keyboard_mouse = 0; - int err; - - if (of_find_property(op->dev.of_node, "keyboard", NULL)) - keyboard_mouse = 1; - - /* uarts must come before keyboards/mice */ - if (keyboard_mouse) - inst = uart_chip_count + kbm_inst; - else - inst = uart_inst; - - sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0, - sizeof(struct zilog_layout), - "zs"); - if (!sunzilog_chip_regs[inst]) - return -ENOMEM; - - rp = sunzilog_chip_regs[inst]; - - if (zilog_irq == -1) - zilog_irq = op->archdata.irqs[0]; - - up = &sunzilog_port_table[inst * 2]; - - /* Channel A */ - up[0].port.mapbase = op->resource[0].start + 0x00; - up[0].port.membase = (void __iomem *) &rp->channelA; - up[0].port.iotype = UPIO_MEM; - up[0].port.irq = op->archdata.irqs[0]; - up[0].port.uartclk = ZS_CLOCK; - up[0].port.fifosize = 1; - up[0].port.ops = &sunzilog_pops; - up[0].port.type = PORT_SUNZILOG; - up[0].port.flags = 0; - up[0].port.line = (inst * 2) + 0; - up[0].port.dev = &op->dev; - up[0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A; - if (keyboard_mouse) - up[0].flags |= SUNZILOG_FLAG_CONS_KEYB; - sunzilog_init_hw(&up[0]); - - /* Channel B */ - up[1].port.mapbase = op->resource[0].start + 0x04; - up[1].port.membase = (void __iomem *) &rp->channelB; - up[1].port.iotype = UPIO_MEM; - up[1].port.irq = op->archdata.irqs[0]; - up[1].port.uartclk = ZS_CLOCK; - up[1].port.fifosize = 1; - up[1].port.ops = &sunzilog_pops; - up[1].port.type = PORT_SUNZILOG; - up[1].port.flags = 0; - up[1].port.line = (inst * 2) + 1; - up[1].port.dev = &op->dev; - up[1].flags |= 0; - if (keyboard_mouse) - up[1].flags |= SUNZILOG_FLAG_CONS_MOUSE; - sunzilog_init_hw(&up[1]); - - if (!keyboard_mouse) { - if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, - &sunzilog_reg, up[0].port.line, - false)) - up->flags |= SUNZILOG_FLAG_IS_CONS; - err = uart_add_one_port(&sunzilog_reg, &up[0].port); - if (err) { - of_iounmap(&op->resource[0], - rp, sizeof(struct zilog_layout)); - return err; - } - if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, - &sunzilog_reg, up[1].port.line, - false)) - up->flags |= SUNZILOG_FLAG_IS_CONS; - err = uart_add_one_port(&sunzilog_reg, &up[1].port); - if (err) { - uart_remove_one_port(&sunzilog_reg, &up[0].port); - of_iounmap(&op->resource[0], - rp, sizeof(struct zilog_layout)); - return err; - } - uart_inst++; - } else { - printk(KERN_INFO "%s: Keyboard at MMIO 0x%llx (irq = %d) " - "is a %s\n", - dev_name(&op->dev), - (unsigned long long) up[0].port.mapbase, - op->archdata.irqs[0], sunzilog_type(&up[0].port)); - printk(KERN_INFO "%s: Mouse at MMIO 0x%llx (irq = %d) " - "is a %s\n", - dev_name(&op->dev), - (unsigned long long) up[1].port.mapbase, - op->archdata.irqs[0], sunzilog_type(&up[1].port)); - kbm_inst++; - } - - dev_set_drvdata(&op->dev, &up[0]); - - return 0; -} - -static void __devexit zs_remove_one(struct uart_sunzilog_port *up) -{ - if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) { -#ifdef CONFIG_SERIO - serio_unregister_port(&up->serio); -#endif - } else - uart_remove_one_port(&sunzilog_reg, &up->port); -} - -static int __devexit zs_remove(struct platform_device *op) -{ - struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); - struct zilog_layout __iomem *regs; - - zs_remove_one(&up[0]); - zs_remove_one(&up[1]); - - regs = sunzilog_chip_regs[up[0].port.line / 2]; - of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); - - dev_set_drvdata(&op->dev, NULL); - - return 0; -} - -static const struct of_device_id zs_match[] = { - { - .name = "zs", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, zs_match); - -static struct of_platform_driver zs_driver = { - .driver = { - .name = "zs", - .owner = THIS_MODULE, - .of_match_table = zs_match, - }, - .probe = zs_probe, - .remove = __devexit_p(zs_remove), -}; - -static int __init sunzilog_init(void) -{ - struct device_node *dp; - int err; - int num_keybms = 0; - int num_sunzilog = 0; - - for_each_node_by_name(dp, "zs") { - num_sunzilog++; - if (of_find_property(dp, "keyboard", NULL)) - num_keybms++; - } - - if (num_sunzilog) { - err = sunzilog_alloc_tables(num_sunzilog); - if (err) - goto out; - - uart_chip_count = num_sunzilog - num_keybms; - - err = sunserial_register_minors(&sunzilog_reg, - uart_chip_count * 2); - if (err) - goto out_free_tables; - } - - err = of_register_platform_driver(&zs_driver); - if (err) - goto out_unregister_uart; - - if (zilog_irq != -1) { - struct uart_sunzilog_port *up = sunzilog_irq_chain; - err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED, - "zs", sunzilog_irq_chain); - if (err) - goto out_unregister_driver; - - /* Enable Interrupts */ - while (up) { - struct zilog_channel __iomem *channel; - - /* printk (KERN_INFO "Enable IRQ for ZILOG Hardware %p\n", up); */ - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - up->flags |= SUNZILOG_FLAG_ISR_HANDLER; - up->curregs[R9] |= MIE; - write_zsreg(channel, R9, up->curregs[R9]); - up = up->next; - } - } - -out: - return err; - -out_unregister_driver: - of_unregister_platform_driver(&zs_driver); - -out_unregister_uart: - if (num_sunzilog) { - sunserial_unregister_minors(&sunzilog_reg, num_sunzilog); - sunzilog_reg.cons = NULL; - } - -out_free_tables: - sunzilog_free_tables(); - goto out; -} - -static void __exit sunzilog_exit(void) -{ - of_unregister_platform_driver(&zs_driver); - - if (zilog_irq != -1) { - struct uart_sunzilog_port *up = sunzilog_irq_chain; - - /* Disable Interrupts */ - while (up) { - struct zilog_channel __iomem *channel; - - /* printk (KERN_INFO "Disable IRQ for ZILOG Hardware %p\n", up); */ - channel = ZILOG_CHANNEL_FROM_PORT(&up->port); - up->flags &= ~SUNZILOG_FLAG_ISR_HANDLER; - up->curregs[R9] &= ~MIE; - write_zsreg(channel, R9, up->curregs[R9]); - up = up->next; - } - - free_irq(zilog_irq, sunzilog_irq_chain); - zilog_irq = -1; - } - - if (sunzilog_reg.nr) { - sunserial_unregister_minors(&sunzilog_reg, sunzilog_reg.nr); - sunzilog_free_tables(); - } -} - -module_init(sunzilog_init); -module_exit(sunzilog_exit); - -MODULE_AUTHOR("David S. Miller"); -MODULE_DESCRIPTION("Sun Zilog serial port driver"); -MODULE_VERSION("2.0"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/sunzilog.h b/drivers/serial/sunzilog.h deleted file mode 100644 index 5dec7b47cc38..000000000000 --- a/drivers/serial/sunzilog.h +++ /dev/null @@ -1,289 +0,0 @@ -#ifndef _SUNZILOG_H -#define _SUNZILOG_H - -struct zilog_channel { - volatile unsigned char control; - volatile unsigned char __pad1; - volatile unsigned char data; - volatile unsigned char __pad2; -}; - -struct zilog_layout { - struct zilog_channel channelB; - struct zilog_channel channelA; -}; - -#define NUM_ZSREGS 17 -#define R7p 16 /* Written as R7 with P15 bit 0 set */ - -/* Conversion routines to/from brg time constants from/to bits - * per second. - */ -#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) -#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) - -/* The Zilog register set */ - -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ -#define RxINT_MASK 0x18 - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENAB 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ -#define RxN_MASK 0xc0 - -/* Write Register 4 */ - -#define PAR_ENAB 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ -#define XCLK_MASK 0xC0 - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define TxN_MASK 0x60 -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 7' (ESCC Only) */ -#define AUTO_TxFLAG 1 /* Automatic Tx SDLC Flag */ -#define AUTO_EOM_RST 2 /* Automatic EOM Reset */ -#define AUTOnRTS 4 /* Automatic /RTS pin deactivation */ -#define RxFIFO_LVL 8 /* Receive FIFO interrupt level */ -#define nDTRnREQ 0x10 /* /DTR/REQ timing */ -#define TxFIFO_LVL 0x20 /* Transmit FIFO interrupt level */ -#define EXT_RD_EN 0x40 /* Extended read register enable */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define SWIACK 0x20 /* Software Interrupt Ack (not on NMOS) */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENAB 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define WR7pEN 1 /* WR7' Enable (ESCC only) */ -#define ZCIE 2 /* Zero count IE */ -#define FIFOEN 4 /* FIFO Enable (ESCC only) */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ -#define CHB_Tx_EMPTY 0x00 -#define CHB_EXT_STAT 0x02 -#define CHB_Rx_AVAIL 0x04 -#define CHB_SPECIAL 0x06 -#define CHA_Tx_EMPTY 0x08 -#define CHA_EXT_STAT 0x0a -#define CHA_Rx_AVAIL 0x0c -#define CHA_SPECIAL 0x0e -#define STATUS_MASK 0x0e - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 6 (LSB frame byte count [Not on NMOS]) */ - -/* Read Register 7 (MSB frame byte count and FIFO status [Not on NMOS]) */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Misc macros */ -#define ZS_CLEARERR(channel) do { sbus_writeb(ERR_RES, &channel->control); \ - udelay(5); } while(0) - -#define ZS_CLEARSTAT(channel) do { sbus_writeb(RES_EXT_INT, &channel->control); \ - udelay(5); } while(0) - -#define ZS_CLEARFIFO(channel) do { sbus_readb(&channel->data); \ - udelay(2); \ - sbus_readb(&channel->data); \ - udelay(2); \ - sbus_readb(&channel->data); \ - udelay(2); } while(0) - -#endif /* _SUNZILOG_H */ diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c deleted file mode 100644 index 1f36b7eb7351..000000000000 --- a/drivers/serial/timbuart.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * timbuart.c timberdale FPGA UART driver - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Timberdale FPGA UART - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "timbuart.h" - -struct timbuart_port { - struct uart_port port; - struct tasklet_struct tasklet; - int usedma; - u32 last_ier; - struct platform_device *dev; -}; - -static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, - 921600, 1843200, 3250000}; - -static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier); - -static irqreturn_t timbuart_handleinterrupt(int irq, void *devid); - -static void timbuart_stop_rx(struct uart_port *port) -{ - /* spin lock held by upper layer, disable all RX interrupts */ - u32 ier = ioread32(port->membase + TIMBUART_IER) & ~RXFLAGS; - iowrite32(ier, port->membase + TIMBUART_IER); -} - -static void timbuart_stop_tx(struct uart_port *port) -{ - /* spinlock held by upper layer, disable TX interrupt */ - u32 ier = ioread32(port->membase + TIMBUART_IER) & ~TXBAE; - iowrite32(ier, port->membase + TIMBUART_IER); -} - -static void timbuart_start_tx(struct uart_port *port) -{ - struct timbuart_port *uart = - container_of(port, struct timbuart_port, port); - - /* do not transfer anything here -> fire off the tasklet */ - tasklet_schedule(&uart->tasklet); -} - -static unsigned int timbuart_tx_empty(struct uart_port *port) -{ - u32 isr = ioread32(port->membase + TIMBUART_ISR); - - return (isr & TXBE) ? TIOCSER_TEMT : 0; -} - -static void timbuart_flush_buffer(struct uart_port *port) -{ - if (!timbuart_tx_empty(port)) { - u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | - TIMBUART_CTRL_FLSHTX; - - iowrite8(ctl, port->membase + TIMBUART_CTRL); - iowrite32(TXBF, port->membase + TIMBUART_ISR); - } -} - -static void timbuart_rx_chars(struct uart_port *port) -{ - struct tty_struct *tty = port->state->port.tty; - - while (ioread32(port->membase + TIMBUART_ISR) & RXDP) { - u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); - port->icount.rx++; - tty_insert_flip_char(tty, ch, TTY_NORMAL); - } - - spin_unlock(&port->lock); - tty_flip_buffer_push(port->state->port.tty); - spin_lock(&port->lock); - - dev_dbg(port->dev, "%s - total read %d bytes\n", - __func__, port->icount.rx); -} - -static void timbuart_tx_chars(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && - !uart_circ_empty(xmit)) { - iowrite8(xmit->buf[xmit->tail], - port->membase + TIMBUART_TXFIFO); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - - dev_dbg(port->dev, - "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n", - __func__, - port->icount.tx, - ioread8(port->membase + TIMBUART_CTRL), - port->mctrl & TIOCM_RTS, - ioread8(port->membase + TIMBUART_BAUDRATE)); -} - -static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) -{ - struct timbuart_port *uart = - container_of(port, struct timbuart_port, port); - struct circ_buf *xmit = &port->state->xmit; - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - return; - - if (port->x_char) - return; - - if (isr & TXFLAGS) { - timbuart_tx_chars(port); - /* clear all TX interrupts */ - iowrite32(TXFLAGS, port->membase + TIMBUART_ISR); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - } else - /* Re-enable any tx interrupt */ - *ier |= uart->last_ier & TXFLAGS; - - /* enable interrupts if there are chars in the transmit buffer, - * Or if we delivered some bytes and want the almost empty interrupt - * we wake up the upper layer later when we got the interrupt - * to give it some time to go out... - */ - if (!uart_circ_empty(xmit)) - *ier |= TXBAE; - - dev_dbg(port->dev, "%s - leaving\n", __func__); -} - -void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) -{ - if (isr & RXFLAGS) { - /* Some RX status is set */ - if (isr & RXBF) { - u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | - TIMBUART_CTRL_FLSHRX; - iowrite8(ctl, port->membase + TIMBUART_CTRL); - port->icount.overrun++; - } else if (isr & (RXDP)) - timbuart_rx_chars(port); - - /* ack all RX interrupts */ - iowrite32(RXFLAGS, port->membase + TIMBUART_ISR); - } - - /* always have the RX interrupts enabled */ - *ier |= RXBAF | RXBF | RXTT; - - dev_dbg(port->dev, "%s - leaving\n", __func__); -} - -void timbuart_tasklet(unsigned long arg) -{ - struct timbuart_port *uart = (struct timbuart_port *)arg; - u32 isr, ier = 0; - - spin_lock(&uart->port.lock); - - isr = ioread32(uart->port.membase + TIMBUART_ISR); - dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); - - if (!uart->usedma) - timbuart_handle_tx_port(&uart->port, isr, &ier); - - timbuart_mctrl_check(&uart->port, isr, &ier); - - if (!uart->usedma) - timbuart_handle_rx_port(&uart->port, isr, &ier); - - iowrite32(ier, uart->port.membase + TIMBUART_IER); - - spin_unlock(&uart->port.lock); - dev_dbg(uart->port.dev, "%s leaving\n", __func__); -} - -static unsigned int timbuart_get_mctrl(struct uart_port *port) -{ - u8 cts = ioread8(port->membase + TIMBUART_CTRL); - dev_dbg(port->dev, "%s - cts %x\n", __func__, cts); - - if (cts & TIMBUART_CTRL_CTS) - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; - else - return TIOCM_DSR | TIOCM_CAR; -} - -static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - dev_dbg(port->dev, "%s - %x\n", __func__, mctrl); - - if (mctrl & TIOCM_RTS) - iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); - else - iowrite8(0, port->membase + TIMBUART_CTRL); -} - -static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) -{ - unsigned int cts; - - if (isr & CTS_DELTA) { - /* ack */ - iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR); - cts = timbuart_get_mctrl(port); - uart_handle_cts_change(port, cts & TIOCM_CTS); - wake_up_interruptible(&port->state->port.delta_msr_wait); - } - - *ier |= CTS_DELTA; -} - -static void timbuart_enable_ms(struct uart_port *port) -{ - /* N/A */ -} - -static void timbuart_break_ctl(struct uart_port *port, int ctl) -{ - /* N/A */ -} - -static int timbuart_startup(struct uart_port *port) -{ - struct timbuart_port *uart = - container_of(port, struct timbuart_port, port); - - dev_dbg(port->dev, "%s\n", __func__); - - iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL); - iowrite32(0x1ff, port->membase + TIMBUART_ISR); - /* Enable all but TX interrupts */ - iowrite32(RXBAF | RXBF | RXTT | CTS_DELTA, - port->membase + TIMBUART_IER); - - return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED, - "timb-uart", uart); -} - -static void timbuart_shutdown(struct uart_port *port) -{ - struct timbuart_port *uart = - container_of(port, struct timbuart_port, port); - dev_dbg(port->dev, "%s\n", __func__); - free_irq(port->irq, uart); - iowrite32(0, port->membase + TIMBUART_IER); -} - -static int get_bindex(int baud) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(baudrates); i++) - if (baud <= baudrates[i]) - return i; - - return -1; -} - -static void timbuart_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - unsigned int baud; - short bindex; - unsigned long flags; - - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - bindex = get_bindex(baud); - dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex); - - if (bindex < 0) - bindex = 0; - baud = baudrates[bindex]; - - /* The serial layer calls into this once with old = NULL when setting - up initially */ - if (old) - tty_termios_copy_hw(termios, old); - tty_termios_encode_baud_rate(termios, baud, baud); - - spin_lock_irqsave(&port->lock, flags); - iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); - uart_update_timeout(port, termios->c_cflag, baud); - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *timbuart_type(struct uart_port *port) -{ - return port->type == PORT_UNKNOWN ? "timbuart" : NULL; -} - -/* We do not request/release mappings of the registers here, - * currently it's done in the proble function. - */ -static void timbuart_release_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - int size = - resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); - - if (port->flags & UPF_IOREMAP) { - iounmap(port->membase); - port->membase = NULL; - } - - release_mem_region(port->mapbase, size); -} - -static int timbuart_request_port(struct uart_port *port) -{ - struct platform_device *pdev = to_platform_device(port->dev); - int size = - resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); - - if (!request_mem_region(port->mapbase, size, "timb-uart")) - return -EBUSY; - - if (port->flags & UPF_IOREMAP) { - port->membase = ioremap(port->mapbase, size); - if (port->membase == NULL) { - release_mem_region(port->mapbase, size); - return -ENOMEM; - } - } - - return 0; -} - -static irqreturn_t timbuart_handleinterrupt(int irq, void *devid) -{ - struct timbuart_port *uart = (struct timbuart_port *)devid; - - if (ioread8(uart->port.membase + TIMBUART_IPR)) { - uart->last_ier = ioread32(uart->port.membase + TIMBUART_IER); - - /* disable interrupts, the tasklet enables them again */ - iowrite32(0, uart->port.membase + TIMBUART_IER); - - /* fire off bottom half */ - tasklet_schedule(&uart->tasklet); - - return IRQ_HANDLED; - } else - return IRQ_NONE; -} - -/* - * Configure/autoconfigure the port. - */ -static void timbuart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_TIMBUART; - timbuart_request_port(port); - } -} - -static int timbuart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - /* we don't want the core code to modify any port params */ - return -EINVAL; -} - -static struct uart_ops timbuart_ops = { - .tx_empty = timbuart_tx_empty, - .set_mctrl = timbuart_set_mctrl, - .get_mctrl = timbuart_get_mctrl, - .stop_tx = timbuart_stop_tx, - .start_tx = timbuart_start_tx, - .flush_buffer = timbuart_flush_buffer, - .stop_rx = timbuart_stop_rx, - .enable_ms = timbuart_enable_ms, - .break_ctl = timbuart_break_ctl, - .startup = timbuart_startup, - .shutdown = timbuart_shutdown, - .set_termios = timbuart_set_termios, - .type = timbuart_type, - .release_port = timbuart_release_port, - .request_port = timbuart_request_port, - .config_port = timbuart_config_port, - .verify_port = timbuart_verify_port -}; - -static struct uart_driver timbuart_driver = { - .owner = THIS_MODULE, - .driver_name = "timberdale_uart", - .dev_name = "ttyTU", - .major = TIMBUART_MAJOR, - .minor = TIMBUART_MINOR, - .nr = 1 -}; - -static int __devinit timbuart_probe(struct platform_device *dev) -{ - int err, irq; - struct timbuart_port *uart; - struct resource *iomem; - - dev_dbg(&dev->dev, "%s\n", __func__); - - uart = kzalloc(sizeof(*uart), GFP_KERNEL); - if (!uart) { - err = -EINVAL; - goto err_mem; - } - - uart->usedma = 0; - - uart->port.uartclk = 3250000 * 16; - uart->port.fifosize = TIMBUART_FIFO_SIZE; - uart->port.regshift = 2; - uart->port.iotype = UPIO_MEM; - uart->port.ops = &timbuart_ops; - uart->port.irq = 0; - uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; - uart->port.line = 0; - uart->port.dev = &dev->dev; - - iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!iomem) { - err = -ENOMEM; - goto err_register; - } - uart->port.mapbase = iomem->start; - uart->port.membase = NULL; - - irq = platform_get_irq(dev, 0); - if (irq < 0) { - err = -EINVAL; - goto err_register; - } - uart->port.irq = irq; - - tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); - - err = uart_register_driver(&timbuart_driver); - if (err) - goto err_register; - - err = uart_add_one_port(&timbuart_driver, &uart->port); - if (err) - goto err_add_port; - - platform_set_drvdata(dev, uart); - - return 0; - -err_add_port: - uart_unregister_driver(&timbuart_driver); -err_register: - kfree(uart); -err_mem: - printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n", - err); - - return err; -} - -static int __devexit timbuart_remove(struct platform_device *dev) -{ - struct timbuart_port *uart = platform_get_drvdata(dev); - - tasklet_kill(&uart->tasklet); - uart_remove_one_port(&timbuart_driver, &uart->port); - uart_unregister_driver(&timbuart_driver); - kfree(uart); - - return 0; -} - -static struct platform_driver timbuart_platform_driver = { - .driver = { - .name = "timb-uart", - .owner = THIS_MODULE, - }, - .probe = timbuart_probe, - .remove = __devexit_p(timbuart_remove), -}; - -/*--------------------------------------------------------------------------*/ - -static int __init timbuart_init(void) -{ - return platform_driver_register(&timbuart_platform_driver); -} - -static void __exit timbuart_exit(void) -{ - platform_driver_unregister(&timbuart_platform_driver); -} - -module_init(timbuart_init); -module_exit(timbuart_exit); - -MODULE_DESCRIPTION("Timberdale UART driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:timb-uart"); - diff --git a/drivers/serial/timbuart.h b/drivers/serial/timbuart.h deleted file mode 100644 index 7e566766bc43..000000000000 --- a/drivers/serial/timbuart.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * timbuart.c timberdale FPGA GPIO driver - * Copyright (c) 2009 Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* Supports: - * Timberdale FPGA UART - */ - -#ifndef _TIMBUART_H -#define _TIMBUART_H - -#define TIMBUART_FIFO_SIZE 2048 - -#define TIMBUART_RXFIFO 0x08 -#define TIMBUART_TXFIFO 0x0c -#define TIMBUART_IER 0x10 -#define TIMBUART_IPR 0x14 -#define TIMBUART_ISR 0x18 -#define TIMBUART_CTRL 0x1c -#define TIMBUART_BAUDRATE 0x20 - -#define TIMBUART_CTRL_RTS 0x01 -#define TIMBUART_CTRL_CTS 0x02 -#define TIMBUART_CTRL_FLSHTX 0x40 -#define TIMBUART_CTRL_FLSHRX 0x80 - -#define TXBF 0x01 -#define TXBAE 0x02 -#define CTS_DELTA 0x04 -#define RXDP 0x08 -#define RXBAF 0x10 -#define RXBF 0x20 -#define RXTT 0x40 -#define RXBNAE 0x80 -#define TXBE 0x100 - -#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE) -#define TXFLAGS (TXBF | TXBAE) - -#define TIMBUART_MAJOR 204 -#define TIMBUART_MINOR 192 - -#endif /* _TIMBUART_H */ - diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c deleted file mode 100644 index d2fce865b731..000000000000 --- a/drivers/serial/uartlite.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * uartlite.c: Serial driver for Xilinx uartlite serial controller - * - * Copyright (C) 2006 Peter Korsgaard - * Copyright (C) 2007 Secret Lab Technologies Ltd. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) -#include -#include -#include -#include - -/* Match table for of_platform binding */ -static struct of_device_id ulite_of_match[] __devinitdata = { - { .compatible = "xlnx,opb-uartlite-1.00.b", }, - { .compatible = "xlnx,xps-uartlite-1.00.a", }, - {} -}; -MODULE_DEVICE_TABLE(of, ulite_of_match); - -#endif - -#define ULITE_NAME "ttyUL" -#define ULITE_MAJOR 204 -#define ULITE_MINOR 187 -#define ULITE_NR_UARTS 4 - -/* --------------------------------------------------------------------- - * Register definitions - * - * For register details see datasheet: - * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf - */ - -#define ULITE_RX 0x00 -#define ULITE_TX 0x04 -#define ULITE_STATUS 0x08 -#define ULITE_CONTROL 0x0c - -#define ULITE_REGION 16 - -#define ULITE_STATUS_RXVALID 0x01 -#define ULITE_STATUS_RXFULL 0x02 -#define ULITE_STATUS_TXEMPTY 0x04 -#define ULITE_STATUS_TXFULL 0x08 -#define ULITE_STATUS_IE 0x10 -#define ULITE_STATUS_OVERRUN 0x20 -#define ULITE_STATUS_FRAME 0x40 -#define ULITE_STATUS_PARITY 0x80 - -#define ULITE_CONTROL_RST_TX 0x01 -#define ULITE_CONTROL_RST_RX 0x02 -#define ULITE_CONTROL_IE 0x10 - - -static struct uart_port ulite_ports[ULITE_NR_UARTS]; - -/* --------------------------------------------------------------------- - * Core UART driver operations - */ - -static int ulite_receive(struct uart_port *port, int stat) -{ - struct tty_struct *tty = port->state->port.tty; - unsigned char ch = 0; - char flag = TTY_NORMAL; - - if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN - | ULITE_STATUS_FRAME)) == 0) - return 0; - - /* stats */ - if (stat & ULITE_STATUS_RXVALID) { - port->icount.rx++; - ch = ioread32be(port->membase + ULITE_RX); - - if (stat & ULITE_STATUS_PARITY) - port->icount.parity++; - } - - if (stat & ULITE_STATUS_OVERRUN) - port->icount.overrun++; - - if (stat & ULITE_STATUS_FRAME) - port->icount.frame++; - - - /* drop byte with parity error if IGNPAR specificed */ - if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY) - stat &= ~ULITE_STATUS_RXVALID; - - stat &= port->read_status_mask; - - if (stat & ULITE_STATUS_PARITY) - flag = TTY_PARITY; - - - stat &= ~port->ignore_status_mask; - - if (stat & ULITE_STATUS_RXVALID) - tty_insert_flip_char(tty, ch, flag); - - if (stat & ULITE_STATUS_FRAME) - tty_insert_flip_char(tty, 0, TTY_FRAME); - - if (stat & ULITE_STATUS_OVERRUN) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - - return 1; -} - -static int ulite_transmit(struct uart_port *port, int stat) -{ - struct circ_buf *xmit = &port->state->xmit; - - if (stat & ULITE_STATUS_TXFULL) - return 0; - - if (port->x_char) { - iowrite32be(port->x_char, port->membase + ULITE_TX); - port->x_char = 0; - port->icount.tx++; - return 1; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) - return 0; - - iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); - port->icount.tx++; - - /* wake up */ - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - return 1; -} - -static irqreturn_t ulite_isr(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - int busy, n = 0; - - do { - int stat = ioread32be(port->membase + ULITE_STATUS); - busy = ulite_receive(port, stat); - busy |= ulite_transmit(port, stat); - n++; - } while (busy); - - /* work done? */ - if (n > 1) { - tty_flip_buffer_push(port->state->port.tty); - return IRQ_HANDLED; - } else { - return IRQ_NONE; - } -} - -static unsigned int ulite_tx_empty(struct uart_port *port) -{ - unsigned long flags; - unsigned int ret; - - spin_lock_irqsave(&port->lock, flags); - ret = ioread32be(port->membase + ULITE_STATUS); - spin_unlock_irqrestore(&port->lock, flags); - - return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; -} - -static unsigned int ulite_get_mctrl(struct uart_port *port) -{ - return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; -} - -static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - /* N/A */ -} - -static void ulite_stop_tx(struct uart_port *port) -{ - /* N/A */ -} - -static void ulite_start_tx(struct uart_port *port) -{ - ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); -} - -static void ulite_stop_rx(struct uart_port *port) -{ - /* don't forward any more data (like !CREAD) */ - port->ignore_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY - | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; -} - -static void ulite_enable_ms(struct uart_port *port) -{ - /* N/A */ -} - -static void ulite_break_ctl(struct uart_port *port, int ctl) -{ - /* N/A */ -} - -static int ulite_startup(struct uart_port *port) -{ - int ret; - - ret = request_irq(port->irq, ulite_isr, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port); - if (ret) - return ret; - - iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, - port->membase + ULITE_CONTROL); - iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); - - return 0; -} - -static void ulite_shutdown(struct uart_port *port) -{ - iowrite32be(0, port->membase + ULITE_CONTROL); - ioread32be(port->membase + ULITE_CONTROL); /* dummy */ - free_irq(port->irq, port); -} - -static void ulite_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) -{ - unsigned long flags; - unsigned int baud; - - spin_lock_irqsave(&port->lock, flags); - - port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN - | ULITE_STATUS_TXFULL; - - if (termios->c_iflag & INPCK) - port->read_status_mask |= - ULITE_STATUS_PARITY | ULITE_STATUS_FRAME; - - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= ULITE_STATUS_PARITY - | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; - - /* ignore all characters if CREAD is not set */ - if ((termios->c_cflag & CREAD) == 0) - port->ignore_status_mask |= - ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY - | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; - - /* update timeout */ - baud = uart_get_baud_rate(port, termios, old, 0, 460800); - uart_update_timeout(port, termios->c_cflag, baud); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *ulite_type(struct uart_port *port) -{ - return port->type == PORT_UARTLITE ? "uartlite" : NULL; -} - -static void ulite_release_port(struct uart_port *port) -{ - release_mem_region(port->mapbase, ULITE_REGION); - iounmap(port->membase); - port->membase = NULL; -} - -static int ulite_request_port(struct uart_port *port) -{ - pr_debug("ulite console: port=%p; port->mapbase=%llx\n", - port, (unsigned long long) port->mapbase); - - if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) { - dev_err(port->dev, "Memory region busy\n"); - return -EBUSY; - } - - port->membase = ioremap(port->mapbase, ULITE_REGION); - if (!port->membase) { - dev_err(port->dev, "Unable to map registers\n"); - release_mem_region(port->mapbase, ULITE_REGION); - return -EBUSY; - } - - return 0; -} - -static void ulite_config_port(struct uart_port *port, int flags) -{ - if (!ulite_request_port(port)) - port->type = PORT_UARTLITE; -} - -static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) -{ - /* we don't want the core code to modify any port params */ - return -EINVAL; -} - -#ifdef CONFIG_CONSOLE_POLL -static int ulite_get_poll_char(struct uart_port *port) -{ - if (!(ioread32be(port->membase + ULITE_STATUS) - & ULITE_STATUS_RXVALID)) - return NO_POLL_CHAR; - - return ioread32be(port->membase + ULITE_RX); -} - -static void ulite_put_poll_char(struct uart_port *port, unsigned char ch) -{ - while (ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_TXFULL) - cpu_relax(); - - /* write char to device */ - iowrite32be(ch, port->membase + ULITE_TX); -} -#endif - -static struct uart_ops ulite_ops = { - .tx_empty = ulite_tx_empty, - .set_mctrl = ulite_set_mctrl, - .get_mctrl = ulite_get_mctrl, - .stop_tx = ulite_stop_tx, - .start_tx = ulite_start_tx, - .stop_rx = ulite_stop_rx, - .enable_ms = ulite_enable_ms, - .break_ctl = ulite_break_ctl, - .startup = ulite_startup, - .shutdown = ulite_shutdown, - .set_termios = ulite_set_termios, - .type = ulite_type, - .release_port = ulite_release_port, - .request_port = ulite_request_port, - .config_port = ulite_config_port, - .verify_port = ulite_verify_port, -#ifdef CONFIG_CONSOLE_POLL - .poll_get_char = ulite_get_poll_char, - .poll_put_char = ulite_put_poll_char, -#endif -}; - -/* --------------------------------------------------------------------- - * Console driver operations - */ - -#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE -static void ulite_console_wait_tx(struct uart_port *port) -{ - int i; - u8 val; - - /* Spin waiting for TX fifo to have space available */ - for (i = 0; i < 100000; i++) { - val = ioread32be(port->membase + ULITE_STATUS); - if ((val & ULITE_STATUS_TXFULL) == 0) - break; - cpu_relax(); - } -} - -static void ulite_console_putchar(struct uart_port *port, int ch) -{ - ulite_console_wait_tx(port); - iowrite32be(ch, port->membase + ULITE_TX); -} - -static void ulite_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct uart_port *port = &ulite_ports[co->index]; - unsigned long flags; - unsigned int ier; - int locked = 1; - - if (oops_in_progress) { - locked = spin_trylock_irqsave(&port->lock, flags); - } else - spin_lock_irqsave(&port->lock, flags); - - /* save and disable interrupt */ - ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; - iowrite32be(0, port->membase + ULITE_CONTROL); - - uart_console_write(port, s, count, ulite_console_putchar); - - ulite_console_wait_tx(port); - - /* restore interrupt state */ - if (ier) - iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); - - if (locked) - spin_unlock_irqrestore(&port->lock, flags); -} - -static int __devinit ulite_console_setup(struct console *co, char *options) -{ - struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (co->index < 0 || co->index >= ULITE_NR_UARTS) - return -EINVAL; - - port = &ulite_ports[co->index]; - - /* Has the device been initialized yet? */ - if (!port->mapbase) { - pr_debug("console on ttyUL%i not present\n", co->index); - return -ENODEV; - } - - /* not initialized yet? */ - if (!port->membase) { - if (ulite_request_port(port)) - return -ENODEV; - } - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, co, baud, parity, bits, flow); -} - -static struct uart_driver ulite_uart_driver; - -static struct console ulite_console = { - .name = ULITE_NAME, - .write = ulite_console_write, - .device = uart_console_device, - .setup = ulite_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */ - .data = &ulite_uart_driver, -}; - -static int __init ulite_console_init(void) -{ - register_console(&ulite_console); - return 0; -} - -console_initcall(ulite_console_init); - -#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */ - -static struct uart_driver ulite_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "uartlite", - .dev_name = ULITE_NAME, - .major = ULITE_MAJOR, - .minor = ULITE_MINOR, - .nr = ULITE_NR_UARTS, -#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE - .cons = &ulite_console, -#endif -}; - -/* --------------------------------------------------------------------- - * Port assignment functions (mapping devices to uart_port structures) - */ - -/** ulite_assign: register a uartlite device with the driver - * - * @dev: pointer to device structure - * @id: requested id number. Pass -1 for automatic port assignment - * @base: base address of uartlite registers - * @irq: irq number for uartlite - * - * Returns: 0 on success, <0 otherwise - */ -static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq) -{ - struct uart_port *port; - int rc; - - /* if id = -1; then scan for a free id and use that */ - if (id < 0) { - for (id = 0; id < ULITE_NR_UARTS; id++) - if (ulite_ports[id].mapbase == 0) - break; - } - if (id < 0 || id >= ULITE_NR_UARTS) { - dev_err(dev, "%s%i too large\n", ULITE_NAME, id); - return -EINVAL; - } - - if ((ulite_ports[id].mapbase) && (ulite_ports[id].mapbase != base)) { - dev_err(dev, "cannot assign to %s%i; it is already in use\n", - ULITE_NAME, id); - return -EBUSY; - } - - port = &ulite_ports[id]; - - spin_lock_init(&port->lock); - port->fifosize = 16; - port->regshift = 2; - port->iotype = UPIO_MEM; - port->iobase = 1; /* mark port in use */ - port->mapbase = base; - port->membase = NULL; - port->ops = &ulite_ops; - port->irq = irq; - port->flags = UPF_BOOT_AUTOCONF; - port->dev = dev; - port->type = PORT_UNKNOWN; - port->line = id; - - dev_set_drvdata(dev, port); - - /* Register the port */ - rc = uart_add_one_port(&ulite_uart_driver, port); - if (rc) { - dev_err(dev, "uart_add_one_port() failed; err=%i\n", rc); - port->mapbase = 0; - dev_set_drvdata(dev, NULL); - return rc; - } - - return 0; -} - -/** ulite_release: register a uartlite device with the driver - * - * @dev: pointer to device structure - */ -static int __devexit ulite_release(struct device *dev) -{ - struct uart_port *port = dev_get_drvdata(dev); - int rc = 0; - - if (port) { - rc = uart_remove_one_port(&ulite_uart_driver, port); - dev_set_drvdata(dev, NULL); - port->mapbase = 0; - } - - return rc; -} - -/* --------------------------------------------------------------------- - * Platform bus binding - */ - -static int __devinit ulite_probe(struct platform_device *pdev) -{ - struct resource *res, *res2; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res2) - return -ENODEV; - - return ulite_assign(&pdev->dev, pdev->id, res->start, res2->start); -} - -static int __devexit ulite_remove(struct platform_device *pdev) -{ - return ulite_release(&pdev->dev); -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:uartlite"); - -static struct platform_driver ulite_platform_driver = { - .probe = ulite_probe, - .remove = __devexit_p(ulite_remove), - .driver = { - .owner = THIS_MODULE, - .name = "uartlite", - }, -}; - -/* --------------------------------------------------------------------- - * OF bus bindings - */ -#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) -static int __devinit -ulite_of_probe(struct platform_device *op, const struct of_device_id *match) -{ - struct resource res; - const unsigned int *id; - int irq, rc; - - dev_dbg(&op->dev, "%s(%p, %p)\n", __func__, op, match); - - rc = of_address_to_resource(op->dev.of_node, 0, &res); - if (rc) { - dev_err(&op->dev, "invalid address\n"); - return rc; - } - - irq = irq_of_parse_and_map(op->dev.of_node, 0); - - id = of_get_property(op->dev.of_node, "port-number", NULL); - - return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); -} - -static int __devexit ulite_of_remove(struct platform_device *op) -{ - return ulite_release(&op->dev); -} - -static struct of_platform_driver ulite_of_driver = { - .probe = ulite_of_probe, - .remove = __devexit_p(ulite_of_remove), - .driver = { - .name = "uartlite", - .owner = THIS_MODULE, - .of_match_table = ulite_of_match, - }, -}; - -/* Registration helpers to keep the number of #ifdefs to a minimum */ -static inline int __init ulite_of_register(void) -{ - pr_debug("uartlite: calling of_register_platform_driver()\n"); - return of_register_platform_driver(&ulite_of_driver); -} - -static inline void __exit ulite_of_unregister(void) -{ - of_unregister_platform_driver(&ulite_of_driver); -} -#else /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ -/* Appropriate config not enabled; do nothing helpers */ -static inline int __init ulite_of_register(void) { return 0; } -static inline void __exit ulite_of_unregister(void) { } -#endif /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ - -/* --------------------------------------------------------------------- - * Module setup/teardown - */ - -int __init ulite_init(void) -{ - int ret; - - pr_debug("uartlite: calling uart_register_driver()\n"); - ret = uart_register_driver(&ulite_uart_driver); - if (ret) - goto err_uart; - - ret = ulite_of_register(); - if (ret) - goto err_of; - - pr_debug("uartlite: calling platform_driver_register()\n"); - ret = platform_driver_register(&ulite_platform_driver); - if (ret) - goto err_plat; - - return 0; - -err_plat: - ulite_of_unregister(); -err_of: - uart_unregister_driver(&ulite_uart_driver); -err_uart: - printk(KERN_ERR "registering uartlite driver failed: err=%i", ret); - return ret; -} - -void __exit ulite_exit(void) -{ - platform_driver_unregister(&ulite_platform_driver); - ulite_of_unregister(); - uart_unregister_driver(&ulite_uart_driver); -} - -module_init(ulite_init); -module_exit(ulite_exit); - -MODULE_AUTHOR("Peter Korsgaard "); -MODULE_DESCRIPTION("Xilinx uartlite serial driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c deleted file mode 100644 index 3f4848e2174a..000000000000 --- a/drivers/serial/ucc_uart.c +++ /dev/null @@ -1,1537 +0,0 @@ -/* - * Freescale QUICC Engine UART device driver - * - * Author: Timur Tabi - * - * Copyright 2007 Freescale Semiconductor, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * - * This driver adds support for UART devices via Freescale's QUICC Engine - * found on some Freescale SOCs. - * - * If Soft-UART support is needed but not already present, then this driver - * will request and upload the "Soft-UART" microcode upon probe. The - * filename of the microcode should be fsl_qe_ucode_uart_X_YZ.bin, where "X" - * is the name of the SOC (e.g. 8323), and YZ is the revision of the SOC, - * (e.g. "11" for 1.1). - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -/* - * The GUMR flag for Soft UART. This would normally be defined in qe.h, - * but Soft-UART is a hack and we want to keep everything related to it in - * this file. - */ -#define UCC_SLOW_GUMR_H_SUART 0x00004000 /* Soft-UART */ - -/* - * soft_uart is 1 if we need to use Soft-UART mode - */ -static int soft_uart; -/* - * firmware_loaded is 1 if the firmware has been loaded, 0 otherwise. - */ -static int firmware_loaded; - -/* Enable this macro to configure all serial ports in internal loopback - mode */ -/* #define LOOPBACK */ - -/* The major and minor device numbers are defined in - * http://www.lanana.org/docs/device-list/devices-2.6+.txt. For the QE - * UART, we have major number 204 and minor numbers 46 - 49, which are the - * same as for the CPM2. This decision was made because no Freescale part - * has both a CPM and a QE. - */ -#define SERIAL_QE_MAJOR 204 -#define SERIAL_QE_MINOR 46 - -/* Since we only have minor numbers 46 - 49, there is a hard limit of 4 ports */ -#define UCC_MAX_UART 4 - -/* The number of buffer descriptors for receiving characters. */ -#define RX_NUM_FIFO 4 - -/* The number of buffer descriptors for transmitting characters. */ -#define TX_NUM_FIFO 4 - -/* The maximum size of the character buffer for a single RX BD. */ -#define RX_BUF_SIZE 32 - -/* The maximum size of the character buffer for a single TX BD. */ -#define TX_BUF_SIZE 32 - -/* - * The number of jiffies to wait after receiving a close command before the - * device is actually closed. This allows the last few characters to be - * sent over the wire. - */ -#define UCC_WAIT_CLOSING 100 - -struct ucc_uart_pram { - struct ucc_slow_pram common; - u8 res1[8]; /* reserved */ - __be16 maxidl; /* Maximum idle chars */ - __be16 idlc; /* temp idle counter */ - __be16 brkcr; /* Break count register */ - __be16 parec; /* receive parity error counter */ - __be16 frmec; /* receive framing error counter */ - __be16 nosec; /* receive noise counter */ - __be16 brkec; /* receive break condition counter */ - __be16 brkln; /* last received break length */ - __be16 uaddr[2]; /* UART address character 1 & 2 */ - __be16 rtemp; /* Temp storage */ - __be16 toseq; /* Transmit out of sequence char */ - __be16 cchars[8]; /* control characters 1-8 */ - __be16 rccm; /* receive control character mask */ - __be16 rccr; /* receive control character register */ - __be16 rlbc; /* receive last break character */ - __be16 res2; /* reserved */ - __be32 res3; /* reserved, should be cleared */ - u8 res4; /* reserved, should be cleared */ - u8 res5[3]; /* reserved, should be cleared */ - __be32 res6; /* reserved, should be cleared */ - __be32 res7; /* reserved, should be cleared */ - __be32 res8; /* reserved, should be cleared */ - __be32 res9; /* reserved, should be cleared */ - __be32 res10; /* reserved, should be cleared */ - __be32 res11; /* reserved, should be cleared */ - __be32 res12; /* reserved, should be cleared */ - __be32 res13; /* reserved, should be cleared */ -/* The rest is for Soft-UART only */ - __be16 supsmr; /* 0x90, Shadow UPSMR */ - __be16 res92; /* 0x92, reserved, initialize to 0 */ - __be32 rx_state; /* 0x94, RX state, initialize to 0 */ - __be32 rx_cnt; /* 0x98, RX count, initialize to 0 */ - u8 rx_length; /* 0x9C, Char length, set to 1+CL+PEN+1+SL */ - u8 rx_bitmark; /* 0x9D, reserved, initialize to 0 */ - u8 rx_temp_dlst_qe; /* 0x9E, reserved, initialize to 0 */ - u8 res14[0xBC - 0x9F]; /* reserved */ - __be32 dump_ptr; /* 0xBC, Dump pointer */ - __be32 rx_frame_rem; /* 0xC0, reserved, initialize to 0 */ - u8 rx_frame_rem_size; /* 0xC4, reserved, initialize to 0 */ - u8 tx_mode; /* 0xC5, mode, 0=AHDLC, 1=UART */ - __be16 tx_state; /* 0xC6, TX state */ - u8 res15[0xD0 - 0xC8]; /* reserved */ - __be32 resD0; /* 0xD0, reserved, initialize to 0 */ - u8 resD4; /* 0xD4, reserved, initialize to 0 */ - __be16 resD5; /* 0xD5, reserved, initialize to 0 */ -} __attribute__ ((packed)); - -/* SUPSMR definitions, for Soft-UART only */ -#define UCC_UART_SUPSMR_SL 0x8000 -#define UCC_UART_SUPSMR_RPM_MASK 0x6000 -#define UCC_UART_SUPSMR_RPM_ODD 0x0000 -#define UCC_UART_SUPSMR_RPM_LOW 0x2000 -#define UCC_UART_SUPSMR_RPM_EVEN 0x4000 -#define UCC_UART_SUPSMR_RPM_HIGH 0x6000 -#define UCC_UART_SUPSMR_PEN 0x1000 -#define UCC_UART_SUPSMR_TPM_MASK 0x0C00 -#define UCC_UART_SUPSMR_TPM_ODD 0x0000 -#define UCC_UART_SUPSMR_TPM_LOW 0x0400 -#define UCC_UART_SUPSMR_TPM_EVEN 0x0800 -#define UCC_UART_SUPSMR_TPM_HIGH 0x0C00 -#define UCC_UART_SUPSMR_FRZ 0x0100 -#define UCC_UART_SUPSMR_UM_MASK 0x00c0 -#define UCC_UART_SUPSMR_UM_NORMAL 0x0000 -#define UCC_UART_SUPSMR_UM_MAN_MULTI 0x0040 -#define UCC_UART_SUPSMR_UM_AUTO_MULTI 0x00c0 -#define UCC_UART_SUPSMR_CL_MASK 0x0030 -#define UCC_UART_SUPSMR_CL_8 0x0030 -#define UCC_UART_SUPSMR_CL_7 0x0020 -#define UCC_UART_SUPSMR_CL_6 0x0010 -#define UCC_UART_SUPSMR_CL_5 0x0000 - -#define UCC_UART_TX_STATE_AHDLC 0x00 -#define UCC_UART_TX_STATE_UART 0x01 -#define UCC_UART_TX_STATE_X1 0x00 -#define UCC_UART_TX_STATE_X16 0x80 - -#define UCC_UART_PRAM_ALIGNMENT 0x100 - -#define UCC_UART_SIZE_OF_BD UCC_SLOW_SIZE_OF_BD -#define NUM_CONTROL_CHARS 8 - -/* Private per-port data structure */ -struct uart_qe_port { - struct uart_port port; - struct ucc_slow __iomem *uccp; - struct ucc_uart_pram __iomem *uccup; - struct ucc_slow_info us_info; - struct ucc_slow_private *us_private; - struct device_node *np; - unsigned int ucc_num; /* First ucc is 0, not 1 */ - - u16 rx_nrfifos; - u16 rx_fifosize; - u16 tx_nrfifos; - u16 tx_fifosize; - int wait_closing; - u32 flags; - struct qe_bd *rx_bd_base; - struct qe_bd *rx_cur; - struct qe_bd *tx_bd_base; - struct qe_bd *tx_cur; - unsigned char *tx_buf; - unsigned char *rx_buf; - void *bd_virt; /* virtual address of the BD buffers */ - dma_addr_t bd_dma_addr; /* bus address of the BD buffers */ - unsigned int bd_size; /* size of BD buffer space */ -}; - -static struct uart_driver ucc_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "ucc_uart", - .dev_name = "ttyQE", - .major = SERIAL_QE_MAJOR, - .minor = SERIAL_QE_MINOR, - .nr = UCC_MAX_UART, -}; - -/* - * Virtual to physical address translation. - * - * Given the virtual address for a character buffer, this function returns - * the physical (DMA) equivalent. - */ -static inline dma_addr_t cpu2qe_addr(void *addr, struct uart_qe_port *qe_port) -{ - if (likely((addr >= qe_port->bd_virt)) && - (addr < (qe_port->bd_virt + qe_port->bd_size))) - return qe_port->bd_dma_addr + (addr - qe_port->bd_virt); - - /* something nasty happened */ - printk(KERN_ERR "%s: addr=%p\n", __func__, addr); - BUG(); - return 0; -} - -/* - * Physical to virtual address translation. - * - * Given the physical (DMA) address for a character buffer, this function - * returns the virtual equivalent. - */ -static inline void *qe2cpu_addr(dma_addr_t addr, struct uart_qe_port *qe_port) -{ - /* sanity check */ - if (likely((addr >= qe_port->bd_dma_addr) && - (addr < (qe_port->bd_dma_addr + qe_port->bd_size)))) - return qe_port->bd_virt + (addr - qe_port->bd_dma_addr); - - /* something nasty happened */ - printk(KERN_ERR "%s: addr=%x\n", __func__, addr); - BUG(); - return NULL; -} - -/* - * Return 1 if the QE is done transmitting all buffers for this port - * - * This function scans each BD in sequence. If we find a BD that is not - * ready (READY=1), then we return 0 indicating that the QE is still sending - * data. If we reach the last BD (WRAP=1), then we know we've scanned - * the entire list, and all BDs are done. - */ -static unsigned int qe_uart_tx_empty(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - struct qe_bd *bdp = qe_port->tx_bd_base; - - while (1) { - if (in_be16(&bdp->status) & BD_SC_READY) - /* This BD is not done, so return "not done" */ - return 0; - - if (in_be16(&bdp->status) & BD_SC_WRAP) - /* - * This BD is done and it's the last one, so return - * "done" - */ - return 1; - - bdp++; - }; -} - -/* - * Set the modem control lines - * - * Although the QE can control the modem control lines (e.g. CTS), we - * don't need that support. This function must exist, however, otherwise - * the kernel will panic. - */ -void qe_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -/* - * Get the current modem control line status - * - * Although the QE can control the modem control lines (e.g. CTS), this - * driver currently doesn't support that, so we always return Carrier - * Detect, Data Set Ready, and Clear To Send. - */ -static unsigned int qe_uart_get_mctrl(struct uart_port *port) -{ - return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; -} - -/* - * Disable the transmit interrupt. - * - * Although this function is called "stop_tx", it does not actually stop - * transmission of data. Instead, it tells the QE to not generate an - * interrupt when the UCC is finished sending characters. - */ -static void qe_uart_stop_tx(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - - clrbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_TX); -} - -/* - * Transmit as many characters to the HW as possible. - * - * This function will attempt to stuff of all the characters from the - * kernel's transmit buffer into TX BDs. - * - * A return value of non-zero indicates that it successfully stuffed all - * characters from the kernel buffer. - * - * A return value of zero indicates that there are still characters in the - * kernel's buffer that have not been transmitted, but there are no more BDs - * available. This function should be called again after a BD has been made - * available. - */ -static int qe_uart_tx_pump(struct uart_qe_port *qe_port) -{ - struct qe_bd *bdp; - unsigned char *p; - unsigned int count; - struct uart_port *port = &qe_port->port; - struct circ_buf *xmit = &port->state->xmit; - - bdp = qe_port->rx_cur; - - /* Handle xon/xoff */ - if (port->x_char) { - /* Pick next descriptor and fill from buffer */ - bdp = qe_port->tx_cur; - - p = qe2cpu_addr(bdp->buf, qe_port); - - *p++ = port->x_char; - out_be16(&bdp->length, 1); - setbits16(&bdp->status, BD_SC_READY); - /* Get next BD. */ - if (in_be16(&bdp->status) & BD_SC_WRAP) - bdp = qe_port->tx_bd_base; - else - bdp++; - qe_port->tx_cur = bdp; - - port->icount.tx++; - port->x_char = 0; - return 1; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - qe_uart_stop_tx(port); - return 0; - } - - /* Pick next descriptor and fill from buffer */ - bdp = qe_port->tx_cur; - - while (!(in_be16(&bdp->status) & BD_SC_READY) && - (xmit->tail != xmit->head)) { - count = 0; - p = qe2cpu_addr(bdp->buf, qe_port); - while (count < qe_port->tx_fifosize) { - *p++ = xmit->buf[xmit->tail]; - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - count++; - if (xmit->head == xmit->tail) - break; - } - - out_be16(&bdp->length, count); - setbits16(&bdp->status, BD_SC_READY); - - /* Get next BD. */ - if (in_be16(&bdp->status) & BD_SC_WRAP) - bdp = qe_port->tx_bd_base; - else - bdp++; - } - qe_port->tx_cur = bdp; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) { - /* The kernel buffer is empty, so turn off TX interrupts. We - don't need to be told when the QE is finished transmitting - the data. */ - qe_uart_stop_tx(port); - return 0; - } - - return 1; -} - -/* - * Start transmitting data - * - * This function will start transmitting any available data, if the port - * isn't already transmitting data. - */ -static void qe_uart_start_tx(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - - /* If we currently are transmitting, then just return */ - if (in_be16(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX) - return; - - /* Otherwise, pump the port and start transmission */ - if (qe_uart_tx_pump(qe_port)) - setbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_TX); -} - -/* - * Stop transmitting data - */ -static void qe_uart_stop_rx(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - - clrbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_RX); -} - -/* - * Enable status change interrupts - * - * We don't support status change interrupts, but we need to define this - * function otherwise the kernel will panic. - */ -static void qe_uart_enable_ms(struct uart_port *port) -{ -} - -/* Start or stop sending break signal - * - * This function controls the sending of a break signal. If break_state=1, - * then we start sending a break signal. If break_state=0, then we stop - * sending the break signal. - */ -static void qe_uart_break_ctl(struct uart_port *port, int break_state) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - - if (break_state) - ucc_slow_stop_tx(qe_port->us_private); - else - ucc_slow_restart_tx(qe_port->us_private); -} - -/* ISR helper function for receiving character. - * - * This function is called by the ISR to handling receiving characters - */ -static void qe_uart_int_rx(struct uart_qe_port *qe_port) -{ - int i; - unsigned char ch, *cp; - struct uart_port *port = &qe_port->port; - struct tty_struct *tty = port->state->port.tty; - struct qe_bd *bdp; - u16 status; - unsigned int flg; - - /* Just loop through the closed BDs and copy the characters into - * the buffer. - */ - bdp = qe_port->rx_cur; - while (1) { - status = in_be16(&bdp->status); - - /* If this one is empty, then we assume we've read them all */ - if (status & BD_SC_EMPTY) - break; - - /* get number of characters, and check space in RX buffer */ - i = in_be16(&bdp->length); - - /* If we don't have enough room in RX buffer for the entire BD, - * then we try later, which will be the next RX interrupt. - */ - if (tty_buffer_request_room(tty, i) < i) { - dev_dbg(port->dev, "ucc-uart: no room in RX buffer\n"); - return; - } - - /* get pointer */ - cp = qe2cpu_addr(bdp->buf, qe_port); - - /* loop through the buffer */ - while (i-- > 0) { - ch = *cp++; - port->icount.rx++; - flg = TTY_NORMAL; - - if (!i && status & - (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV)) - goto handle_error; - if (uart_handle_sysrq_char(port, ch)) - continue; - -error_return: - tty_insert_flip_char(tty, ch, flg); - - } - - /* This BD is ready to be used again. Clear status. get next */ - clrsetbits_be16(&bdp->status, BD_SC_BR | BD_SC_FR | BD_SC_PR | - BD_SC_OV | BD_SC_ID, BD_SC_EMPTY); - if (in_be16(&bdp->status) & BD_SC_WRAP) - bdp = qe_port->rx_bd_base; - else - bdp++; - - } - - /* Write back buffer pointer */ - qe_port->rx_cur = bdp; - - /* Activate BH processing */ - tty_flip_buffer_push(tty); - - return; - - /* Error processing */ - -handle_error: - /* Statistics */ - if (status & BD_SC_BR) - port->icount.brk++; - if (status & BD_SC_PR) - port->icount.parity++; - if (status & BD_SC_FR) - port->icount.frame++; - if (status & BD_SC_OV) - port->icount.overrun++; - - /* Mask out ignored conditions */ - status &= port->read_status_mask; - - /* Handle the remaining ones */ - if (status & BD_SC_BR) - flg = TTY_BREAK; - else if (status & BD_SC_PR) - flg = TTY_PARITY; - else if (status & BD_SC_FR) - flg = TTY_FRAME; - - /* Overrun does not affect the current character ! */ - if (status & BD_SC_OV) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); -#ifdef SUPPORT_SYSRQ - port->sysrq = 0; -#endif - goto error_return; -} - -/* Interrupt handler - * - * This interrupt handler is called after a BD is processed. - */ -static irqreturn_t qe_uart_int(int irq, void *data) -{ - struct uart_qe_port *qe_port = (struct uart_qe_port *) data; - struct ucc_slow __iomem *uccp = qe_port->uccp; - u16 events; - - /* Clear the interrupts */ - events = in_be16(&uccp->ucce); - out_be16(&uccp->ucce, events); - - if (events & UCC_UART_UCCE_BRKE) - uart_handle_break(&qe_port->port); - - if (events & UCC_UART_UCCE_RX) - qe_uart_int_rx(qe_port); - - if (events & UCC_UART_UCCE_TX) - qe_uart_tx_pump(qe_port); - - return events ? IRQ_HANDLED : IRQ_NONE; -} - -/* Initialize buffer descriptors - * - * This function initializes all of the RX and TX buffer descriptors. - */ -static void qe_uart_initbd(struct uart_qe_port *qe_port) -{ - int i; - void *bd_virt; - struct qe_bd *bdp; - - /* Set the physical address of the host memory buffers in the buffer - * descriptors, and the virtual address for us to work with. - */ - bd_virt = qe_port->bd_virt; - bdp = qe_port->rx_bd_base; - qe_port->rx_cur = qe_port->rx_bd_base; - for (i = 0; i < (qe_port->rx_nrfifos - 1); i++) { - out_be16(&bdp->status, BD_SC_EMPTY | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); - bd_virt += qe_port->rx_fifosize; - bdp++; - } - - /* */ - out_be16(&bdp->status, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); - - /* Set the physical address of the host memory - * buffers in the buffer descriptors, and the - * virtual address for us to work with. - */ - bd_virt = qe_port->bd_virt + - L1_CACHE_ALIGN(qe_port->rx_nrfifos * qe_port->rx_fifosize); - qe_port->tx_cur = qe_port->tx_bd_base; - bdp = qe_port->tx_bd_base; - for (i = 0; i < (qe_port->tx_nrfifos - 1); i++) { - out_be16(&bdp->status, BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); - bd_virt += qe_port->tx_fifosize; - bdp++; - } - - /* Loopback requires the preamble bit to be set on the first TX BD */ -#ifdef LOOPBACK - setbits16(&qe_port->tx_cur->status, BD_SC_P); -#endif - - out_be16(&bdp->status, BD_SC_WRAP | BD_SC_INTRPT); - out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); - out_be16(&bdp->length, 0); -} - -/* - * Initialize a UCC for UART. - * - * This function configures a given UCC to be used as a UART device. Basic - * UCC initialization is handled in qe_uart_request_port(). This function - * does all the UART-specific stuff. - */ -static void qe_uart_init_ucc(struct uart_qe_port *qe_port) -{ - u32 cecr_subblock; - struct ucc_slow __iomem *uccp = qe_port->uccp; - struct ucc_uart_pram *uccup = qe_port->uccup; - - unsigned int i; - - /* First, disable TX and RX in the UCC */ - ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX); - - /* Program the UCC UART parameter RAM */ - out_8(&uccup->common.rbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); - out_8(&uccup->common.tbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); - out_be16(&uccup->common.mrblr, qe_port->rx_fifosize); - out_be16(&uccup->maxidl, 0x10); - out_be16(&uccup->brkcr, 1); - out_be16(&uccup->parec, 0); - out_be16(&uccup->frmec, 0); - out_be16(&uccup->nosec, 0); - out_be16(&uccup->brkec, 0); - out_be16(&uccup->uaddr[0], 0); - out_be16(&uccup->uaddr[1], 0); - out_be16(&uccup->toseq, 0); - for (i = 0; i < 8; i++) - out_be16(&uccup->cchars[i], 0xC000); - out_be16(&uccup->rccm, 0xc0ff); - - /* Configure the GUMR registers for UART */ - if (soft_uart) { - /* Soft-UART requires a 1X multiplier for TX */ - clrsetbits_be32(&uccp->gumr_l, - UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK | - UCC_SLOW_GUMR_L_RDCR_MASK, - UCC_SLOW_GUMR_L_MODE_UART | UCC_SLOW_GUMR_L_TDCR_1 | - UCC_SLOW_GUMR_L_RDCR_16); - - clrsetbits_be32(&uccp->gumr_h, UCC_SLOW_GUMR_H_RFW, - UCC_SLOW_GUMR_H_TRX | UCC_SLOW_GUMR_H_TTX); - } else { - clrsetbits_be32(&uccp->gumr_l, - UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK | - UCC_SLOW_GUMR_L_RDCR_MASK, - UCC_SLOW_GUMR_L_MODE_UART | UCC_SLOW_GUMR_L_TDCR_16 | - UCC_SLOW_GUMR_L_RDCR_16); - - clrsetbits_be32(&uccp->gumr_h, - UCC_SLOW_GUMR_H_TRX | UCC_SLOW_GUMR_H_TTX, - UCC_SLOW_GUMR_H_RFW); - } - -#ifdef LOOPBACK - clrsetbits_be32(&uccp->gumr_l, UCC_SLOW_GUMR_L_DIAG_MASK, - UCC_SLOW_GUMR_L_DIAG_LOOP); - clrsetbits_be32(&uccp->gumr_h, - UCC_SLOW_GUMR_H_CTSP | UCC_SLOW_GUMR_H_RSYN, - UCC_SLOW_GUMR_H_CDS); -#endif - - /* Disable rx interrupts and clear all pending events. */ - out_be16(&uccp->uccm, 0); - out_be16(&uccp->ucce, 0xffff); - out_be16(&uccp->udsr, 0x7e7e); - - /* Initialize UPSMR */ - out_be16(&uccp->upsmr, 0); - - if (soft_uart) { - out_be16(&uccup->supsmr, 0x30); - out_be16(&uccup->res92, 0); - out_be32(&uccup->rx_state, 0); - out_be32(&uccup->rx_cnt, 0); - out_8(&uccup->rx_bitmark, 0); - out_8(&uccup->rx_length, 10); - out_be32(&uccup->dump_ptr, 0x4000); - out_8(&uccup->rx_temp_dlst_qe, 0); - out_be32(&uccup->rx_frame_rem, 0); - out_8(&uccup->rx_frame_rem_size, 0); - /* Soft-UART requires TX to be 1X */ - out_8(&uccup->tx_mode, - UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1); - out_be16(&uccup->tx_state, 0); - out_8(&uccup->resD4, 0); - out_be16(&uccup->resD5, 0); - - /* Set UART mode. - * Enable receive and transmit. - */ - - /* From the microcode errata: - * 1.GUMR_L register, set mode=0010 (QMC). - * 2.Set GUMR_H[17] bit. (UART/AHDLC mode). - * 3.Set GUMR_H[19:20] (Transparent mode) - * 4.Clear GUMR_H[26] (RFW) - * ... - * 6.Receiver must use 16x over sampling - */ - clrsetbits_be32(&uccp->gumr_l, - UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK | - UCC_SLOW_GUMR_L_RDCR_MASK, - UCC_SLOW_GUMR_L_MODE_QMC | UCC_SLOW_GUMR_L_TDCR_16 | - UCC_SLOW_GUMR_L_RDCR_16); - - clrsetbits_be32(&uccp->gumr_h, - UCC_SLOW_GUMR_H_RFW | UCC_SLOW_GUMR_H_RSYN, - UCC_SLOW_GUMR_H_SUART | UCC_SLOW_GUMR_H_TRX | - UCC_SLOW_GUMR_H_TTX | UCC_SLOW_GUMR_H_TFL); - -#ifdef LOOPBACK - clrsetbits_be32(&uccp->gumr_l, UCC_SLOW_GUMR_L_DIAG_MASK, - UCC_SLOW_GUMR_L_DIAG_LOOP); - clrbits32(&uccp->gumr_h, UCC_SLOW_GUMR_H_CTSP | - UCC_SLOW_GUMR_H_CDS); -#endif - - cecr_subblock = ucc_slow_get_qe_cr_subblock(qe_port->ucc_num); - qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, - QE_CR_PROTOCOL_UNSPECIFIED, 0); - } else { - cecr_subblock = ucc_slow_get_qe_cr_subblock(qe_port->ucc_num); - qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, - QE_CR_PROTOCOL_UART, 0); - } -} - -/* - * Initialize the port. - */ -static int qe_uart_startup(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - int ret; - - /* - * If we're using Soft-UART mode, then we need to make sure the - * firmware has been uploaded first. - */ - if (soft_uart && !firmware_loaded) { - dev_err(port->dev, "Soft-UART firmware not uploaded\n"); - return -ENODEV; - } - - qe_uart_initbd(qe_port); - qe_uart_init_ucc(qe_port); - - /* Install interrupt handler. */ - ret = request_irq(port->irq, qe_uart_int, IRQF_SHARED, "ucc-uart", - qe_port); - if (ret) { - dev_err(port->dev, "could not claim IRQ %u\n", port->irq); - return ret; - } - - /* Startup rx-int */ - setbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_RX); - ucc_slow_enable(qe_port->us_private, COMM_DIR_RX_AND_TX); - - return 0; -} - -/* - * Shutdown the port. - */ -static void qe_uart_shutdown(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - struct ucc_slow __iomem *uccp = qe_port->uccp; - unsigned int timeout = 20; - - /* Disable RX and TX */ - - /* Wait for all the BDs marked sent */ - while (!qe_uart_tx_empty(port)) { - if (!--timeout) { - dev_warn(port->dev, "shutdown timeout\n"); - break; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(2); - } - - if (qe_port->wait_closing) { - /* Wait a bit longer */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(qe_port->wait_closing); - } - - /* Stop uarts */ - ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX); - clrbits16(&uccp->uccm, UCC_UART_UCCE_TX | UCC_UART_UCCE_RX); - - /* Shut them really down and reinit buffer descriptors */ - ucc_slow_graceful_stop_tx(qe_port->us_private); - qe_uart_initbd(qe_port); - - free_irq(port->irq, qe_port); -} - -/* - * Set the serial port parameters. - */ -static void qe_uart_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - struct ucc_slow __iomem *uccp = qe_port->uccp; - unsigned int baud; - unsigned long flags; - u16 upsmr = in_be16(&uccp->upsmr); - struct ucc_uart_pram __iomem *uccup = qe_port->uccup; - u16 supsmr = in_be16(&uccup->supsmr); - u8 char_length = 2; /* 1 + CL + PEN + 1 + SL */ - - /* Character length programmed into the mode register is the - * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, - * 1 or 2 stop bits, minus 1. - * The value 'bits' counts this for us. - */ - - /* byte size */ - upsmr &= UCC_UART_UPSMR_CL_MASK; - supsmr &= UCC_UART_SUPSMR_CL_MASK; - - switch (termios->c_cflag & CSIZE) { - case CS5: - upsmr |= UCC_UART_UPSMR_CL_5; - supsmr |= UCC_UART_SUPSMR_CL_5; - char_length += 5; - break; - case CS6: - upsmr |= UCC_UART_UPSMR_CL_6; - supsmr |= UCC_UART_SUPSMR_CL_6; - char_length += 6; - break; - case CS7: - upsmr |= UCC_UART_UPSMR_CL_7; - supsmr |= UCC_UART_SUPSMR_CL_7; - char_length += 7; - break; - default: /* case CS8 */ - upsmr |= UCC_UART_UPSMR_CL_8; - supsmr |= UCC_UART_SUPSMR_CL_8; - char_length += 8; - break; - } - - /* If CSTOPB is set, we want two stop bits */ - if (termios->c_cflag & CSTOPB) { - upsmr |= UCC_UART_UPSMR_SL; - supsmr |= UCC_UART_SUPSMR_SL; - char_length++; /* + SL */ - } - - if (termios->c_cflag & PARENB) { - upsmr |= UCC_UART_UPSMR_PEN; - supsmr |= UCC_UART_SUPSMR_PEN; - char_length++; /* + PEN */ - - if (!(termios->c_cflag & PARODD)) { - upsmr &= ~(UCC_UART_UPSMR_RPM_MASK | - UCC_UART_UPSMR_TPM_MASK); - upsmr |= UCC_UART_UPSMR_RPM_EVEN | - UCC_UART_UPSMR_TPM_EVEN; - supsmr &= ~(UCC_UART_SUPSMR_RPM_MASK | - UCC_UART_SUPSMR_TPM_MASK); - supsmr |= UCC_UART_SUPSMR_RPM_EVEN | - UCC_UART_SUPSMR_TPM_EVEN; - } - } - - /* - * Set up parity check flag - */ - port->read_status_mask = BD_SC_EMPTY | BD_SC_OV; - if (termios->c_iflag & INPCK) - port->read_status_mask |= BD_SC_FR | BD_SC_PR; - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= BD_SC_BR; - - /* - * Characters to ignore - */ - port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= BD_SC_PR | BD_SC_FR; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BD_SC_BR; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= BD_SC_OV; - } - /* - * !!! ignore all characters if CREAD is not set - */ - if ((termios->c_cflag & CREAD) == 0) - port->read_status_mask &= ~BD_SC_EMPTY; - - baud = uart_get_baud_rate(port, termios, old, 0, 115200); - - /* Do we really need a spinlock here? */ - spin_lock_irqsave(&port->lock, flags); - - out_be16(&uccp->upsmr, upsmr); - if (soft_uart) { - out_be16(&uccup->supsmr, supsmr); - out_8(&uccup->rx_length, char_length); - - /* Soft-UART requires a 1X multiplier for TX */ - qe_setbrg(qe_port->us_info.rx_clock, baud, 16); - qe_setbrg(qe_port->us_info.tx_clock, baud, 1); - } else { - qe_setbrg(qe_port->us_info.rx_clock, baud, 16); - qe_setbrg(qe_port->us_info.tx_clock, baud, 16); - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* - * Return a pointer to a string that describes what kind of port this is. - */ -static const char *qe_uart_type(struct uart_port *port) -{ - return "QE"; -} - -/* - * Allocate any memory and I/O resources required by the port. - */ -static int qe_uart_request_port(struct uart_port *port) -{ - int ret; - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - struct ucc_slow_info *us_info = &qe_port->us_info; - struct ucc_slow_private *uccs; - unsigned int rx_size, tx_size; - void *bd_virt; - dma_addr_t bd_dma_addr = 0; - - ret = ucc_slow_init(us_info, &uccs); - if (ret) { - dev_err(port->dev, "could not initialize UCC%u\n", - qe_port->ucc_num); - return ret; - } - - qe_port->us_private = uccs; - qe_port->uccp = uccs->us_regs; - qe_port->uccup = (struct ucc_uart_pram *) uccs->us_pram; - qe_port->rx_bd_base = uccs->rx_bd; - qe_port->tx_bd_base = uccs->tx_bd; - - /* - * Allocate the transmit and receive data buffers. - */ - - rx_size = L1_CACHE_ALIGN(qe_port->rx_nrfifos * qe_port->rx_fifosize); - tx_size = L1_CACHE_ALIGN(qe_port->tx_nrfifos * qe_port->tx_fifosize); - - bd_virt = dma_alloc_coherent(port->dev, rx_size + tx_size, &bd_dma_addr, - GFP_KERNEL); - if (!bd_virt) { - dev_err(port->dev, "could not allocate buffer descriptors\n"); - return -ENOMEM; - } - - qe_port->bd_virt = bd_virt; - qe_port->bd_dma_addr = bd_dma_addr; - qe_port->bd_size = rx_size + tx_size; - - qe_port->rx_buf = bd_virt; - qe_port->tx_buf = qe_port->rx_buf + rx_size; - - return 0; -} - -/* - * Configure the port. - * - * We say we're a CPM-type port because that's mostly true. Once the device - * is configured, this driver operates almost identically to the CPM serial - * driver. - */ -static void qe_uart_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = PORT_CPM; - qe_uart_request_port(port); - } -} - -/* - * Release any memory and I/O resources that were allocated in - * qe_uart_request_port(). - */ -static void qe_uart_release_port(struct uart_port *port) -{ - struct uart_qe_port *qe_port = - container_of(port, struct uart_qe_port, port); - struct ucc_slow_private *uccs = qe_port->us_private; - - dma_free_coherent(port->dev, qe_port->bd_size, qe_port->bd_virt, - qe_port->bd_dma_addr); - - ucc_slow_free(uccs); -} - -/* - * Verify that the data in serial_struct is suitable for this device. - */ -static int qe_uart_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM) - return -EINVAL; - - if (ser->irq < 0 || ser->irq >= nr_irqs) - return -EINVAL; - - if (ser->baud_base < 9600) - return -EINVAL; - - return 0; -} -/* UART operations - * - * Details on these functions can be found in Documentation/serial/driver - */ -static struct uart_ops qe_uart_pops = { - .tx_empty = qe_uart_tx_empty, - .set_mctrl = qe_uart_set_mctrl, - .get_mctrl = qe_uart_get_mctrl, - .stop_tx = qe_uart_stop_tx, - .start_tx = qe_uart_start_tx, - .stop_rx = qe_uart_stop_rx, - .enable_ms = qe_uart_enable_ms, - .break_ctl = qe_uart_break_ctl, - .startup = qe_uart_startup, - .shutdown = qe_uart_shutdown, - .set_termios = qe_uart_set_termios, - .type = qe_uart_type, - .release_port = qe_uart_release_port, - .request_port = qe_uart_request_port, - .config_port = qe_uart_config_port, - .verify_port = qe_uart_verify_port, -}; - -/* - * Obtain the SOC model number and revision level - * - * This function parses the device tree to obtain the SOC model. It then - * reads the SVR register to the revision. - * - * The device tree stores the SOC model two different ways. - * - * The new way is: - * - * cpu@0 { - * compatible = "PowerPC,8323"; - * device_type = "cpu"; - * ... - * - * - * The old way is: - * PowerPC,8323@0 { - * device_type = "cpu"; - * ... - * - * This code first checks the new way, and then the old way. - */ -static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l) -{ - struct device_node *np; - const char *soc_string; - unsigned int svr; - unsigned int soc; - - /* Find the CPU node */ - np = of_find_node_by_type(NULL, "cpu"); - if (!np) - return 0; - /* Find the compatible property */ - soc_string = of_get_property(np, "compatible", NULL); - if (!soc_string) - /* No compatible property, so try the name. */ - soc_string = np->name; - - /* Extract the SOC number from the "PowerPC," string */ - if ((sscanf(soc_string, "PowerPC,%u", &soc) != 1) || !soc) - return 0; - - /* Get the revision from the SVR */ - svr = mfspr(SPRN_SVR); - *rev_h = (svr >> 4) & 0xf; - *rev_l = svr & 0xf; - - return soc; -} - -/* - * requst_firmware_nowait() callback function - * - * This function is called by the kernel when a firmware is made available, - * or if it times out waiting for the firmware. - */ -static void uart_firmware_cont(const struct firmware *fw, void *context) -{ - struct qe_firmware *firmware; - struct device *dev = context; - int ret; - - if (!fw) { - dev_err(dev, "firmware not found\n"); - return; - } - - firmware = (struct qe_firmware *) fw->data; - - if (firmware->header.length != fw->size) { - dev_err(dev, "invalid firmware\n"); - goto out; - } - - ret = qe_upload_firmware(firmware); - if (ret) { - dev_err(dev, "could not load firmware\n"); - goto out; - } - - firmware_loaded = 1; - out: - release_firmware(fw); -} - -static int ucc_uart_probe(struct platform_device *ofdev, - const struct of_device_id *match) -{ - struct device_node *np = ofdev->dev.of_node; - const unsigned int *iprop; /* Integer OF properties */ - const char *sprop; /* String OF properties */ - struct uart_qe_port *qe_port = NULL; - struct resource res; - int ret; - - /* - * Determine if we need Soft-UART mode - */ - if (of_find_property(np, "soft-uart", NULL)) { - dev_dbg(&ofdev->dev, "using Soft-UART mode\n"); - soft_uart = 1; - } - - /* - * If we are using Soft-UART, determine if we need to upload the - * firmware, too. - */ - if (soft_uart) { - struct qe_firmware_info *qe_fw_info; - - qe_fw_info = qe_get_firmware_info(); - - /* Check if the firmware has been uploaded. */ - if (qe_fw_info && strstr(qe_fw_info->id, "Soft-UART")) { - firmware_loaded = 1; - } else { - char filename[32]; - unsigned int soc; - unsigned int rev_h; - unsigned int rev_l; - - soc = soc_info(&rev_h, &rev_l); - if (!soc) { - dev_err(&ofdev->dev, "unknown CPU model\n"); - return -ENXIO; - } - sprintf(filename, "fsl_qe_ucode_uart_%u_%u%u.bin", - soc, rev_h, rev_l); - - dev_info(&ofdev->dev, "waiting for firmware %s\n", - filename); - - /* - * We call request_firmware_nowait instead of - * request_firmware so that the driver can load and - * initialize the ports without holding up the rest of - * the kernel. If hotplug support is enabled in the - * kernel, then we use it. - */ - ret = request_firmware_nowait(THIS_MODULE, - FW_ACTION_HOTPLUG, filename, &ofdev->dev, - GFP_KERNEL, &ofdev->dev, uart_firmware_cont); - if (ret) { - dev_err(&ofdev->dev, - "could not load firmware %s\n", - filename); - return ret; - } - } - } - - qe_port = kzalloc(sizeof(struct uart_qe_port), GFP_KERNEL); - if (!qe_port) { - dev_err(&ofdev->dev, "can't allocate QE port structure\n"); - return -ENOMEM; - } - - /* Search for IRQ and mapbase */ - ret = of_address_to_resource(np, 0, &res); - if (ret) { - dev_err(&ofdev->dev, "missing 'reg' property in device tree\n"); - kfree(qe_port); - return ret; - } - if (!res.start) { - dev_err(&ofdev->dev, "invalid 'reg' property in device tree\n"); - kfree(qe_port); - return -EINVAL; - } - qe_port->port.mapbase = res.start; - - /* Get the UCC number (device ID) */ - /* UCCs are numbered 1-7 */ - iprop = of_get_property(np, "cell-index", NULL); - if (!iprop) { - iprop = of_get_property(np, "device-id", NULL); - if (!iprop) { - kfree(qe_port); - dev_err(&ofdev->dev, "UCC is unspecified in " - "device tree\n"); - return -EINVAL; - } - } - - if ((*iprop < 1) || (*iprop > UCC_MAX_NUM)) { - dev_err(&ofdev->dev, "no support for UCC%u\n", *iprop); - kfree(qe_port); - return -ENODEV; - } - qe_port->ucc_num = *iprop - 1; - - /* - * In the future, we should not require the BRG to be specified in the - * device tree. If no clock-source is specified, then just pick a BRG - * to use. This requires a new QE library function that manages BRG - * assignments. - */ - - sprop = of_get_property(np, "rx-clock-name", NULL); - if (!sprop) { - dev_err(&ofdev->dev, "missing rx-clock-name in device tree\n"); - kfree(qe_port); - return -ENODEV; - } - - qe_port->us_info.rx_clock = qe_clock_source(sprop); - if ((qe_port->us_info.rx_clock < QE_BRG1) || - (qe_port->us_info.rx_clock > QE_BRG16)) { - dev_err(&ofdev->dev, "rx-clock-name must be a BRG for UART\n"); - kfree(qe_port); - return -ENODEV; - } - -#ifdef LOOPBACK - /* In internal loopback mode, TX and RX must use the same clock */ - qe_port->us_info.tx_clock = qe_port->us_info.rx_clock; -#else - sprop = of_get_property(np, "tx-clock-name", NULL); - if (!sprop) { - dev_err(&ofdev->dev, "missing tx-clock-name in device tree\n"); - kfree(qe_port); - return -ENODEV; - } - qe_port->us_info.tx_clock = qe_clock_source(sprop); -#endif - if ((qe_port->us_info.tx_clock < QE_BRG1) || - (qe_port->us_info.tx_clock > QE_BRG16)) { - dev_err(&ofdev->dev, "tx-clock-name must be a BRG for UART\n"); - kfree(qe_port); - return -ENODEV; - } - - /* Get the port number, numbered 0-3 */ - iprop = of_get_property(np, "port-number", NULL); - if (!iprop) { - dev_err(&ofdev->dev, "missing port-number in device tree\n"); - kfree(qe_port); - return -EINVAL; - } - qe_port->port.line = *iprop; - if (qe_port->port.line >= UCC_MAX_UART) { - dev_err(&ofdev->dev, "port-number must be 0-%u\n", - UCC_MAX_UART - 1); - kfree(qe_port); - return -EINVAL; - } - - qe_port->port.irq = irq_of_parse_and_map(np, 0); - if (qe_port->port.irq == NO_IRQ) { - dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n", - qe_port->ucc_num + 1); - kfree(qe_port); - return -EINVAL; - } - - /* - * Newer device trees have an "fsl,qe" compatible property for the QE - * node, but we still need to support older device trees. - */ - np = of_find_compatible_node(NULL, NULL, "fsl,qe"); - if (!np) { - np = of_find_node_by_type(NULL, "qe"); - if (!np) { - dev_err(&ofdev->dev, "could not find 'qe' node\n"); - kfree(qe_port); - return -EINVAL; - } - } - - iprop = of_get_property(np, "brg-frequency", NULL); - if (!iprop) { - dev_err(&ofdev->dev, - "missing brg-frequency in device tree\n"); - kfree(qe_port); - return -EINVAL; - } - - if (*iprop) - qe_port->port.uartclk = *iprop; - else { - /* - * Older versions of U-Boot do not initialize the brg-frequency - * property, so in this case we assume the BRG frequency is - * half the QE bus frequency. - */ - iprop = of_get_property(np, "bus-frequency", NULL); - if (!iprop) { - dev_err(&ofdev->dev, - "missing QE bus-frequency in device tree\n"); - kfree(qe_port); - return -EINVAL; - } - if (*iprop) - qe_port->port.uartclk = *iprop / 2; - else { - dev_err(&ofdev->dev, - "invalid QE bus-frequency in device tree\n"); - kfree(qe_port); - return -EINVAL; - } - } - - spin_lock_init(&qe_port->port.lock); - qe_port->np = np; - qe_port->port.dev = &ofdev->dev; - qe_port->port.ops = &qe_uart_pops; - qe_port->port.iotype = UPIO_MEM; - - qe_port->tx_nrfifos = TX_NUM_FIFO; - qe_port->tx_fifosize = TX_BUF_SIZE; - qe_port->rx_nrfifos = RX_NUM_FIFO; - qe_port->rx_fifosize = RX_BUF_SIZE; - - qe_port->wait_closing = UCC_WAIT_CLOSING; - qe_port->port.fifosize = 512; - qe_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; - - qe_port->us_info.ucc_num = qe_port->ucc_num; - qe_port->us_info.regs = (phys_addr_t) res.start; - qe_port->us_info.irq = qe_port->port.irq; - - qe_port->us_info.rx_bd_ring_len = qe_port->rx_nrfifos; - qe_port->us_info.tx_bd_ring_len = qe_port->tx_nrfifos; - - /* Make sure ucc_slow_init() initializes both TX and RX */ - qe_port->us_info.init_tx = 1; - qe_port->us_info.init_rx = 1; - - /* Add the port to the uart sub-system. This will cause - * qe_uart_config_port() to be called, so the us_info structure must - * be initialized. - */ - ret = uart_add_one_port(&ucc_uart_driver, &qe_port->port); - if (ret) { - dev_err(&ofdev->dev, "could not add /dev/ttyQE%u\n", - qe_port->port.line); - kfree(qe_port); - return ret; - } - - dev_set_drvdata(&ofdev->dev, qe_port); - - dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n", - qe_port->ucc_num + 1, qe_port->port.line); - - /* Display the mknod command for this device */ - dev_dbg(&ofdev->dev, "mknod command is 'mknod /dev/ttyQE%u c %u %u'\n", - qe_port->port.line, SERIAL_QE_MAJOR, - SERIAL_QE_MINOR + qe_port->port.line); - - return 0; -} - -static int ucc_uart_remove(struct platform_device *ofdev) -{ - struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); - - dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line); - - uart_remove_one_port(&ucc_uart_driver, &qe_port->port); - - dev_set_drvdata(&ofdev->dev, NULL); - kfree(qe_port); - - return 0; -} - -static struct of_device_id ucc_uart_match[] = { - { - .type = "serial", - .compatible = "ucc_uart", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, ucc_uart_match); - -static struct of_platform_driver ucc_uart_of_driver = { - .driver = { - .name = "ucc_uart", - .owner = THIS_MODULE, - .of_match_table = ucc_uart_match, - }, - .probe = ucc_uart_probe, - .remove = ucc_uart_remove, -}; - -static int __init ucc_uart_init(void) -{ - int ret; - - printk(KERN_INFO "Freescale QUICC Engine UART device driver\n"); -#ifdef LOOPBACK - printk(KERN_INFO "ucc-uart: Using loopback mode\n"); -#endif - - ret = uart_register_driver(&ucc_uart_driver); - if (ret) { - printk(KERN_ERR "ucc-uart: could not register UART driver\n"); - return ret; - } - - ret = of_register_platform_driver(&ucc_uart_of_driver); - if (ret) - printk(KERN_ERR - "ucc-uart: could not register platform driver\n"); - - return ret; -} - -static void __exit ucc_uart_exit(void) -{ - printk(KERN_INFO - "Freescale QUICC Engine UART device driver unloading\n"); - - of_unregister_platform_driver(&ucc_uart_of_driver); - uart_unregister_driver(&ucc_uart_driver); -} - -module_init(ucc_uart_init); -module_exit(ucc_uart_exit); - -MODULE_DESCRIPTION("Freescale QUICC Engine (QE) UART"); -MODULE_AUTHOR("Timur Tabi "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_QE_MAJOR); - diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c deleted file mode 100644 index 3beb6ab4fa68..000000000000 --- a/drivers/serial/vr41xx_siu.c +++ /dev/null @@ -1,978 +0,0 @@ -/* - * Driver for NEC VR4100 series Serial Interface Unit. - * - * Copyright (C) 2004-2008 Yoichi Yuasa - * - * Based on drivers/serial/8250.c, by Russell King. - * - * 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 - */ - -#if defined(CONFIG_SERIAL_VR41XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define SIU_BAUD_BASE 1152000 -#define SIU_MAJOR 204 -#define SIU_MINOR_BASE 82 - -#define RX_MAX_COUNT 256 -#define TX_MAX_COUNT 15 - -#define SIUIRSEL 0x08 - #define TMICMODE 0x20 - #define TMICTX 0x10 - #define IRMSEL 0x0c - #define IRMSEL_HP 0x08 - #define IRMSEL_TEMIC 0x04 - #define IRMSEL_SHARP 0x00 - #define IRUSESEL 0x02 - #define SIRSEL 0x01 - -static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = { - [0 ... SIU_PORTS_MAX-1] = { - .lock = __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock), - .irq = -1, - }, -}; - -#ifdef CONFIG_SERIAL_VR41XX_CONSOLE -static uint8_t lsr_break_flag[SIU_PORTS_MAX]; -#endif - -#define siu_read(port, offset) readb((port)->membase + (offset)) -#define siu_write(port, offset, value) writeb((value), (port)->membase + (offset)) - -void vr41xx_select_siu_interface(siu_interface_t interface) -{ - struct uart_port *port; - unsigned long flags; - uint8_t irsel; - - port = &siu_uart_ports[0]; - - spin_lock_irqsave(&port->lock, flags); - - irsel = siu_read(port, SIUIRSEL); - if (interface == SIU_INTERFACE_IRDA) - irsel |= SIRSEL; - else - irsel &= ~SIRSEL; - siu_write(port, SIUIRSEL, irsel); - - spin_unlock_irqrestore(&port->lock, flags); -} -EXPORT_SYMBOL_GPL(vr41xx_select_siu_interface); - -void vr41xx_use_irda(irda_use_t use) -{ - struct uart_port *port; - unsigned long flags; - uint8_t irsel; - - port = &siu_uart_ports[0]; - - spin_lock_irqsave(&port->lock, flags); - - irsel = siu_read(port, SIUIRSEL); - if (use == FIR_USE_IRDA) - irsel |= IRUSESEL; - else - irsel &= ~IRUSESEL; - siu_write(port, SIUIRSEL, irsel); - - spin_unlock_irqrestore(&port->lock, flags); -} -EXPORT_SYMBOL_GPL(vr41xx_use_irda); - -void vr41xx_select_irda_module(irda_module_t module, irda_speed_t speed) -{ - struct uart_port *port; - unsigned long flags; - uint8_t irsel; - - port = &siu_uart_ports[0]; - - spin_lock_irqsave(&port->lock, flags); - - irsel = siu_read(port, SIUIRSEL); - irsel &= ~(IRMSEL | TMICTX | TMICMODE); - switch (module) { - case SHARP_IRDA: - irsel |= IRMSEL_SHARP; - break; - case TEMIC_IRDA: - irsel |= IRMSEL_TEMIC | TMICMODE; - if (speed == IRDA_TX_4MBPS) - irsel |= TMICTX; - break; - case HP_IRDA: - irsel |= IRMSEL_HP; - break; - default: - break; - } - siu_write(port, SIUIRSEL, irsel); - - spin_unlock_irqrestore(&port->lock, flags); -} -EXPORT_SYMBOL_GPL(vr41xx_select_irda_module); - -static inline void siu_clear_fifo(struct uart_port *port) -{ - siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO); - siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT); - siu_write(port, UART_FCR, 0); -} - -static inline unsigned long siu_port_size(struct uart_port *port) -{ - switch (port->type) { - case PORT_VR41XX_SIU: - return 11UL; - case PORT_VR41XX_DSIU: - return 8UL; - } - - return 0; -} - -static inline unsigned int siu_check_type(struct uart_port *port) -{ - if (port->line == 0) - return PORT_VR41XX_SIU; - if (port->line == 1 && port->irq != -1) - return PORT_VR41XX_DSIU; - - return PORT_UNKNOWN; -} - -static inline const char *siu_type_name(struct uart_port *port) -{ - switch (port->type) { - case PORT_VR41XX_SIU: - return "SIU"; - case PORT_VR41XX_DSIU: - return "DSIU"; - } - - return NULL; -} - -static unsigned int siu_tx_empty(struct uart_port *port) -{ - uint8_t lsr; - - lsr = siu_read(port, UART_LSR); - if (lsr & UART_LSR_TEMT) - return TIOCSER_TEMT; - - return 0; -} - -static void siu_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - uint8_t mcr = 0; - - if (mctrl & TIOCM_DTR) - mcr |= UART_MCR_DTR; - if (mctrl & TIOCM_RTS) - mcr |= UART_MCR_RTS; - if (mctrl & TIOCM_OUT1) - mcr |= UART_MCR_OUT1; - if (mctrl & TIOCM_OUT2) - mcr |= UART_MCR_OUT2; - if (mctrl & TIOCM_LOOP) - mcr |= UART_MCR_LOOP; - - siu_write(port, UART_MCR, mcr); -} - -static unsigned int siu_get_mctrl(struct uart_port *port) -{ - uint8_t msr; - unsigned int mctrl = 0; - - msr = siu_read(port, UART_MSR); - if (msr & UART_MSR_DCD) - mctrl |= TIOCM_CAR; - if (msr & UART_MSR_RI) - mctrl |= TIOCM_RNG; - if (msr & UART_MSR_DSR) - mctrl |= TIOCM_DSR; - if (msr & UART_MSR_CTS) - mctrl |= TIOCM_CTS; - - return mctrl; -} - -static void siu_stop_tx(struct uart_port *port) -{ - unsigned long flags; - uint8_t ier; - - spin_lock_irqsave(&port->lock, flags); - - ier = siu_read(port, UART_IER); - ier &= ~UART_IER_THRI; - siu_write(port, UART_IER, ier); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void siu_start_tx(struct uart_port *port) -{ - unsigned long flags; - uint8_t ier; - - spin_lock_irqsave(&port->lock, flags); - - ier = siu_read(port, UART_IER); - ier |= UART_IER_THRI; - siu_write(port, UART_IER, ier); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void siu_stop_rx(struct uart_port *port) -{ - unsigned long flags; - uint8_t ier; - - spin_lock_irqsave(&port->lock, flags); - - ier = siu_read(port, UART_IER); - ier &= ~UART_IER_RLSI; - siu_write(port, UART_IER, ier); - - port->read_status_mask &= ~UART_LSR_DR; - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void siu_enable_ms(struct uart_port *port) -{ - unsigned long flags; - uint8_t ier; - - spin_lock_irqsave(&port->lock, flags); - - ier = siu_read(port, UART_IER); - ier |= UART_IER_MSI; - siu_write(port, UART_IER, ier); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void siu_break_ctl(struct uart_port *port, int ctl) -{ - unsigned long flags; - uint8_t lcr; - - spin_lock_irqsave(&port->lock, flags); - - lcr = siu_read(port, UART_LCR); - if (ctl == -1) - lcr |= UART_LCR_SBC; - else - lcr &= ~UART_LCR_SBC; - siu_write(port, UART_LCR, lcr); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static inline void receive_chars(struct uart_port *port, uint8_t *status) -{ - struct tty_struct *tty; - uint8_t lsr, ch; - char flag; - int max_count = RX_MAX_COUNT; - - tty = port->state->port.tty; - lsr = *status; - - do { - ch = siu_read(port, UART_RX); - port->icount.rx++; - flag = TTY_NORMAL; - -#ifdef CONFIG_SERIAL_VR41XX_CONSOLE - lsr |= lsr_break_flag[port->line]; - lsr_break_flag[port->line] = 0; -#endif - if (unlikely(lsr & (UART_LSR_BI | UART_LSR_FE | - UART_LSR_PE | UART_LSR_OE))) { - if (lsr & UART_LSR_BI) { - lsr &= ~(UART_LSR_FE | UART_LSR_PE); - port->icount.brk++; - - if (uart_handle_break(port)) - goto ignore_char; - } - - if (lsr & UART_LSR_FE) - port->icount.frame++; - if (lsr & UART_LSR_PE) - port->icount.parity++; - if (lsr & UART_LSR_OE) - port->icount.overrun++; - - lsr &= port->read_status_mask; - if (lsr & UART_LSR_BI) - flag = TTY_BREAK; - if (lsr & UART_LSR_FE) - flag = TTY_FRAME; - if (lsr & UART_LSR_PE) - flag = TTY_PARITY; - } - - if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; - - uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); - - ignore_char: - lsr = siu_read(port, UART_LSR); - } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); - - tty_flip_buffer_push(tty); - - *status = lsr; -} - -static inline void check_modem_status(struct uart_port *port) -{ - uint8_t msr; - - msr = siu_read(port, UART_MSR); - if ((msr & UART_MSR_ANY_DELTA) == 0) - return; - if (msr & UART_MSR_DDCD) - uart_handle_dcd_change(port, msr & UART_MSR_DCD); - if (msr & UART_MSR_TERI) - port->icount.rng++; - if (msr & UART_MSR_DDSR) - port->icount.dsr++; - if (msr & UART_MSR_DCTS) - uart_handle_cts_change(port, msr & UART_MSR_CTS); - - wake_up_interruptible(&port->state->port.delta_msr_wait); -} - -static inline void transmit_chars(struct uart_port *port) -{ - struct circ_buf *xmit; - int max_count = TX_MAX_COUNT; - - xmit = &port->state->xmit; - - if (port->x_char) { - siu_write(port, UART_TX, port->x_char); - port->icount.tx++; - port->x_char = 0; - return; - } - - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - siu_stop_tx(port); - return; - } - - do { - siu_write(port, UART_TX, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - if (uart_circ_empty(xmit)) - break; - } while (max_count-- > 0); - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - siu_stop_tx(port); -} - -static irqreturn_t siu_interrupt(int irq, void *dev_id) -{ - struct uart_port *port; - uint8_t iir, lsr; - - port = (struct uart_port *)dev_id; - - iir = siu_read(port, UART_IIR); - if (iir & UART_IIR_NO_INT) - return IRQ_NONE; - - lsr = siu_read(port, UART_LSR); - if (lsr & UART_LSR_DR) - receive_chars(port, &lsr); - - check_modem_status(port); - - if (lsr & UART_LSR_THRE) - transmit_chars(port); - - return IRQ_HANDLED; -} - -static int siu_startup(struct uart_port *port) -{ - int retval; - - if (port->membase == NULL) - return -ENODEV; - - siu_clear_fifo(port); - - (void)siu_read(port, UART_LSR); - (void)siu_read(port, UART_RX); - (void)siu_read(port, UART_IIR); - (void)siu_read(port, UART_MSR); - - if (siu_read(port, UART_LSR) == 0xff) - return -ENODEV; - - retval = request_irq(port->irq, siu_interrupt, 0, siu_type_name(port), port); - if (retval) - return retval; - - if (port->type == PORT_VR41XX_DSIU) - vr41xx_enable_dsiuint(DSIUINT_ALL); - - siu_write(port, UART_LCR, UART_LCR_WLEN8); - - spin_lock_irq(&port->lock); - siu_set_mctrl(port, port->mctrl); - spin_unlock_irq(&port->lock); - - siu_write(port, UART_IER, UART_IER_RLSI | UART_IER_RDI); - - (void)siu_read(port, UART_LSR); - (void)siu_read(port, UART_RX); - (void)siu_read(port, UART_IIR); - (void)siu_read(port, UART_MSR); - - return 0; -} - -static void siu_shutdown(struct uart_port *port) -{ - unsigned long flags; - uint8_t lcr; - - siu_write(port, UART_IER, 0); - - spin_lock_irqsave(&port->lock, flags); - - port->mctrl &= ~TIOCM_OUT2; - siu_set_mctrl(port, port->mctrl); - - spin_unlock_irqrestore(&port->lock, flags); - - lcr = siu_read(port, UART_LCR); - lcr &= ~UART_LCR_SBC; - siu_write(port, UART_LCR, lcr); - - siu_clear_fifo(port); - - (void)siu_read(port, UART_RX); - - if (port->type == PORT_VR41XX_DSIU) - vr41xx_disable_dsiuint(DSIUINT_ALL); - - free_irq(port->irq, port); -} - -static void siu_set_termios(struct uart_port *port, struct ktermios *new, - struct ktermios *old) -{ - tcflag_t c_cflag, c_iflag; - uint8_t lcr, fcr, ier; - unsigned int baud, quot; - unsigned long flags; - - c_cflag = new->c_cflag; - switch (c_cflag & CSIZE) { - case CS5: - lcr = UART_LCR_WLEN5; - break; - case CS6: - lcr = UART_LCR_WLEN6; - break; - case CS7: - lcr = UART_LCR_WLEN7; - break; - default: - lcr = UART_LCR_WLEN8; - break; - } - - if (c_cflag & CSTOPB) - lcr |= UART_LCR_STOP; - if (c_cflag & PARENB) - lcr |= UART_LCR_PARITY; - if ((c_cflag & PARODD) != PARODD) - lcr |= UART_LCR_EPAR; - if (c_cflag & CMSPAR) - lcr |= UART_LCR_SPAR; - - baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10; - - spin_lock_irqsave(&port->lock, flags); - - uart_update_timeout(port, c_cflag, baud); - - c_iflag = new->c_iflag; - - port->read_status_mask = UART_LSR_THRE | UART_LSR_OE | UART_LSR_DR; - if (c_iflag & INPCK) - port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= UART_LSR_BI; - - port->ignore_status_mask = 0; - if (c_iflag & IGNPAR) - port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (c_iflag & IGNBRK) { - port->ignore_status_mask |= UART_LSR_BI; - if (c_iflag & IGNPAR) - port->ignore_status_mask |= UART_LSR_OE; - } - - if ((c_cflag & CREAD) == 0) - port->ignore_status_mask |= UART_LSR_DR; - - ier = siu_read(port, UART_IER); - ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(port, c_cflag)) - ier |= UART_IER_MSI; - siu_write(port, UART_IER, ier); - - siu_write(port, UART_LCR, lcr | UART_LCR_DLAB); - - siu_write(port, UART_DLL, (uint8_t)quot); - siu_write(port, UART_DLM, (uint8_t)(quot >> 8)); - - siu_write(port, UART_LCR, lcr); - - siu_write(port, UART_FCR, fcr); - - siu_set_mctrl(port, port->mctrl); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static void siu_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) -{ - switch (state) { - case 0: - switch (port->type) { - case PORT_VR41XX_SIU: - vr41xx_supply_clock(SIU_CLOCK); - break; - case PORT_VR41XX_DSIU: - vr41xx_supply_clock(DSIU_CLOCK); - break; - } - break; - case 3: - switch (port->type) { - case PORT_VR41XX_SIU: - vr41xx_mask_clock(SIU_CLOCK); - break; - case PORT_VR41XX_DSIU: - vr41xx_mask_clock(DSIU_CLOCK); - break; - } - break; - } -} - -static const char *siu_type(struct uart_port *port) -{ - return siu_type_name(port); -} - -static void siu_release_port(struct uart_port *port) -{ - unsigned long size; - - if (port->flags & UPF_IOREMAP) { - iounmap(port->membase); - port->membase = NULL; - } - - size = siu_port_size(port); - release_mem_region(port->mapbase, size); -} - -static int siu_request_port(struct uart_port *port) -{ - unsigned long size; - struct resource *res; - - size = siu_port_size(port); - res = request_mem_region(port->mapbase, size, siu_type_name(port)); - if (res == NULL) - return -EBUSY; - - if (port->flags & UPF_IOREMAP) { - port->membase = ioremap(port->mapbase, size); - if (port->membase == NULL) { - release_resource(res); - return -ENOMEM; - } - } - - return 0; -} - -static void siu_config_port(struct uart_port *port, int flags) -{ - if (flags & UART_CONFIG_TYPE) { - port->type = siu_check_type(port); - (void)siu_request_port(port); - } -} - -static int siu_verify_port(struct uart_port *port, struct serial_struct *serial) -{ - if (port->type != PORT_VR41XX_SIU && port->type != PORT_VR41XX_DSIU) - return -EINVAL; - if (port->irq != serial->irq) - return -EINVAL; - if (port->iotype != serial->io_type) - return -EINVAL; - if (port->mapbase != (unsigned long)serial->iomem_base) - return -EINVAL; - - return 0; -} - -static struct uart_ops siu_uart_ops = { - .tx_empty = siu_tx_empty, - .set_mctrl = siu_set_mctrl, - .get_mctrl = siu_get_mctrl, - .stop_tx = siu_stop_tx, - .start_tx = siu_start_tx, - .stop_rx = siu_stop_rx, - .enable_ms = siu_enable_ms, - .break_ctl = siu_break_ctl, - .startup = siu_startup, - .shutdown = siu_shutdown, - .set_termios = siu_set_termios, - .pm = siu_pm, - .type = siu_type, - .release_port = siu_release_port, - .request_port = siu_request_port, - .config_port = siu_config_port, - .verify_port = siu_verify_port, -}; - -static int siu_init_ports(struct platform_device *pdev) -{ - struct uart_port *port; - struct resource *res; - int *type = pdev->dev.platform_data; - int i; - - if (!type) - return 0; - - port = siu_uart_ports; - for (i = 0; i < SIU_PORTS_MAX; i++) { - port->type = type[i]; - if (port->type == PORT_UNKNOWN) - continue; - port->irq = platform_get_irq(pdev, i); - port->uartclk = SIU_BAUD_BASE * 16; - port->fifosize = 16; - port->regshift = 0; - port->iotype = UPIO_MEM; - port->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - port->line = i; - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - port->mapbase = res->start; - port++; - } - - return i; -} - -#ifdef CONFIG_SERIAL_VR41XX_CONSOLE - -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - -static void wait_for_xmitr(struct uart_port *port) -{ - int timeout = 10000; - uint8_t lsr, msr; - - do { - lsr = siu_read(port, UART_LSR); - if (lsr & UART_LSR_BI) - lsr_break_flag[port->line] = UART_LSR_BI; - - if ((lsr & BOTH_EMPTY) == BOTH_EMPTY) - break; - } while (timeout-- > 0); - - if (port->flags & UPF_CONS_FLOW) { - timeout = 1000000; - - do { - msr = siu_read(port, UART_MSR); - if ((msr & UART_MSR_CTS) != 0) - break; - } while (timeout-- > 0); - } -} - -static void siu_console_putchar(struct uart_port *port, int ch) -{ - wait_for_xmitr(port); - siu_write(port, UART_TX, ch); -} - -static void siu_console_write(struct console *con, const char *s, unsigned count) -{ - struct uart_port *port; - uint8_t ier; - - port = &siu_uart_ports[con->index]; - - ier = siu_read(port, UART_IER); - siu_write(port, UART_IER, 0); - - uart_console_write(port, s, count, siu_console_putchar); - - wait_for_xmitr(port); - siu_write(port, UART_IER, ier); -} - -static int __init siu_console_setup(struct console *con, char *options) -{ - struct uart_port *port; - int baud = 9600; - int parity = 'n'; - int bits = 8; - int flow = 'n'; - - if (con->index >= SIU_PORTS_MAX) - con->index = 0; - - port = &siu_uart_ports[con->index]; - if (port->membase == NULL) { - if (port->mapbase == 0) - return -ENODEV; - port->membase = ioremap(port->mapbase, siu_port_size(port)); - } - - if (port->type == PORT_VR41XX_SIU) - vr41xx_select_siu_interface(SIU_INTERFACE_RS232C); - - if (options != NULL) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(port, con, baud, parity, bits, flow); -} - -static struct uart_driver siu_uart_driver; - -static struct console siu_console = { - .name = "ttyVR", - .write = siu_console_write, - .device = uart_console_device, - .setup = siu_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &siu_uart_driver, -}; - -static int __devinit siu_console_init(void) -{ - struct uart_port *port; - int i; - - for (i = 0; i < SIU_PORTS_MAX; i++) { - port = &siu_uart_ports[i]; - port->ops = &siu_uart_ops; - } - - register_console(&siu_console); - - return 0; -} - -console_initcall(siu_console_init); - -void __init vr41xx_siu_early_setup(struct uart_port *port) -{ - if (port->type == PORT_UNKNOWN) - return; - - siu_uart_ports[port->line].line = port->line; - siu_uart_ports[port->line].type = port->type; - siu_uart_ports[port->line].uartclk = SIU_BAUD_BASE * 16; - siu_uart_ports[port->line].mapbase = port->mapbase; - siu_uart_ports[port->line].mapbase = port->mapbase; - siu_uart_ports[port->line].ops = &siu_uart_ops; -} - -#define SERIAL_VR41XX_CONSOLE &siu_console -#else -#define SERIAL_VR41XX_CONSOLE NULL -#endif - -static struct uart_driver siu_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "SIU", - .dev_name = "ttyVR", - .major = SIU_MAJOR, - .minor = SIU_MINOR_BASE, - .cons = SERIAL_VR41XX_CONSOLE, -}; - -static int __devinit siu_probe(struct platform_device *dev) -{ - struct uart_port *port; - int num, i, retval; - - num = siu_init_ports(dev); - if (num <= 0) - return -ENODEV; - - siu_uart_driver.nr = num; - retval = uart_register_driver(&siu_uart_driver); - if (retval) - return retval; - - for (i = 0; i < num; i++) { - port = &siu_uart_ports[i]; - port->ops = &siu_uart_ops; - port->dev = &dev->dev; - - retval = uart_add_one_port(&siu_uart_driver, port); - if (retval < 0) { - port->dev = NULL; - break; - } - } - - if (i == 0 && retval < 0) { - uart_unregister_driver(&siu_uart_driver); - return retval; - } - - return 0; -} - -static int __devexit siu_remove(struct platform_device *dev) -{ - struct uart_port *port; - int i; - - for (i = 0; i < siu_uart_driver.nr; i++) { - port = &siu_uart_ports[i]; - if (port->dev == &dev->dev) { - uart_remove_one_port(&siu_uart_driver, port); - port->dev = NULL; - } - } - - uart_unregister_driver(&siu_uart_driver); - - return 0; -} - -static int siu_suspend(struct platform_device *dev, pm_message_t state) -{ - struct uart_port *port; - int i; - - for (i = 0; i < siu_uart_driver.nr; i++) { - port = &siu_uart_ports[i]; - if ((port->type == PORT_VR41XX_SIU || - port->type == PORT_VR41XX_DSIU) && port->dev == &dev->dev) - uart_suspend_port(&siu_uart_driver, port); - - } - - return 0; -} - -static int siu_resume(struct platform_device *dev) -{ - struct uart_port *port; - int i; - - for (i = 0; i < siu_uart_driver.nr; i++) { - port = &siu_uart_ports[i]; - if ((port->type == PORT_VR41XX_SIU || - port->type == PORT_VR41XX_DSIU) && port->dev == &dev->dev) - uart_resume_port(&siu_uart_driver, port); - } - - return 0; -} - -static struct platform_driver siu_device_driver = { - .probe = siu_probe, - .remove = __devexit_p(siu_remove), - .suspend = siu_suspend, - .resume = siu_resume, - .driver = { - .name = "SIU", - .owner = THIS_MODULE, - }, -}; - -static int __init vr41xx_siu_init(void) -{ - return platform_driver_register(&siu_device_driver); -} - -static void __exit vr41xx_siu_exit(void) -{ - platform_driver_unregister(&siu_device_driver); -} - -module_init(vr41xx_siu_init); -module_exit(vr41xx_siu_exit); - -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:SIU"); diff --git a/drivers/serial/vt8500_serial.c b/drivers/serial/vt8500_serial.c deleted file mode 100644 index 322bf56c0d89..000000000000 --- a/drivers/serial/vt8500_serial.c +++ /dev/null @@ -1,648 +0,0 @@ -/* - * drivers/serial/vt8500_serial.c - * - * Copyright (C) 2010 Alexey Charkov - * - * Based on msm_serial.c, which is: - * Copyright (C) 2007 Google, Inc. - * Author: Robert Love - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - */ - -#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -# define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * UART Register offsets - */ - -#define VT8500_URTDR 0x0000 /* Transmit data */ -#define VT8500_URRDR 0x0004 /* Receive data */ -#define VT8500_URDIV 0x0008 /* Clock/Baud rate divisor */ -#define VT8500_URLCR 0x000C /* Line control */ -#define VT8500_URICR 0x0010 /* IrDA control */ -#define VT8500_URIER 0x0014 /* Interrupt enable */ -#define VT8500_URISR 0x0018 /* Interrupt status */ -#define VT8500_URUSR 0x001c /* UART status */ -#define VT8500_URFCR 0x0020 /* FIFO control */ -#define VT8500_URFIDX 0x0024 /* FIFO index */ -#define VT8500_URBKR 0x0028 /* Break signal count */ -#define VT8500_URTOD 0x002c /* Time out divisor */ -#define VT8500_TXFIFO 0x1000 /* Transmit FIFO (16x8) */ -#define VT8500_RXFIFO 0x1020 /* Receive FIFO (16x10) */ - -/* - * Interrupt enable and status bits - */ - -#define TXDE (1 << 0) /* Tx Data empty */ -#define RXDF (1 << 1) /* Rx Data full */ -#define TXFAE (1 << 2) /* Tx FIFO almost empty */ -#define TXFE (1 << 3) /* Tx FIFO empty */ -#define RXFAF (1 << 4) /* Rx FIFO almost full */ -#define RXFF (1 << 5) /* Rx FIFO full */ -#define TXUDR (1 << 6) /* Tx underrun */ -#define RXOVER (1 << 7) /* Rx overrun */ -#define PER (1 << 8) /* Parity error */ -#define FER (1 << 9) /* Frame error */ -#define TCTS (1 << 10) /* Toggle of CTS */ -#define RXTOUT (1 << 11) /* Rx timeout */ -#define BKDONE (1 << 12) /* Break signal done */ -#define ERR (1 << 13) /* AHB error response */ - -#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) -#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) - -struct vt8500_port { - struct uart_port uart; - char name[16]; - struct clk *clk; - unsigned int ier; -}; - -static inline void vt8500_write(struct uart_port *port, unsigned int val, - unsigned int off) -{ - writel(val, port->membase + off); -} - -static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off) -{ - return readl(port->membase + off); -} - -static void vt8500_stop_tx(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = container_of(port, - struct vt8500_port, - uart); - - vt8500_port->ier &= ~TX_FIFO_INTS; - vt8500_write(port, vt8500_port->ier, VT8500_URIER); -} - -static void vt8500_stop_rx(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = container_of(port, - struct vt8500_port, - uart); - - vt8500_port->ier &= ~RX_FIFO_INTS; - vt8500_write(port, vt8500_port->ier, VT8500_URIER); -} - -static void vt8500_enable_ms(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = container_of(port, - struct vt8500_port, - uart); - - vt8500_port->ier |= TCTS; - vt8500_write(port, vt8500_port->ier, VT8500_URIER); -} - -static void handle_rx(struct uart_port *port) -{ - struct tty_struct *tty = tty_port_tty_get(&port->state->port); - if (!tty) { - /* Discard data: no tty available */ - int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8; - u16 ch; - while (count--) - ch = readw(port->membase + VT8500_RXFIFO); - return; - } - - /* - * Handle overrun - */ - if ((vt8500_read(port, VT8500_URISR) & RXOVER)) { - port->icount.overrun++; - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - } - - /* and now the main RX loop */ - while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) { - unsigned int c; - char flag = TTY_NORMAL; - - c = readw(port->membase + VT8500_RXFIFO) & 0x3ff; - - /* Mask conditions we're ignorning. */ - c &= ~port->read_status_mask; - - if (c & FER) { - port->icount.frame++; - flag = TTY_FRAME; - } else if (c & PER) { - port->icount.parity++; - flag = TTY_PARITY; - } - port->icount.rx++; - - if (!uart_handle_sysrq_char(port, c)) - tty_insert_flip_char(tty, c, flag); - } - - tty_flip_buffer_push(tty); - tty_kref_put(tty); -} - -static void handle_tx(struct uart_port *port) -{ - struct circ_buf *xmit = &port->state->xmit; - - if (port->x_char) { - writeb(port->x_char, port->membase + VT8500_TXFIFO); - port->icount.tx++; - port->x_char = 0; - } - if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - vt8500_stop_tx(port); - return; - } - - while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) { - if (uart_circ_empty(xmit)) - break; - - writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO); - - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - port->icount.tx++; - } - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (uart_circ_empty(xmit)) - vt8500_stop_tx(port); -} - -static void vt8500_start_tx(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = container_of(port, - struct vt8500_port, - uart); - - vt8500_port->ier &= ~TX_FIFO_INTS; - vt8500_write(port, vt8500_port->ier, VT8500_URIER); - handle_tx(port); - vt8500_port->ier |= TX_FIFO_INTS; - vt8500_write(port, vt8500_port->ier, VT8500_URIER); -} - -static void handle_delta_cts(struct uart_port *port) -{ - port->icount.cts++; - wake_up_interruptible(&port->state->port.delta_msr_wait); -} - -static irqreturn_t vt8500_irq(int irq, void *dev_id) -{ - struct uart_port *port = dev_id; - unsigned long isr; - - spin_lock(&port->lock); - isr = vt8500_read(port, VT8500_URISR); - - /* Acknowledge active status bits */ - vt8500_write(port, isr, VT8500_URISR); - - if (isr & RX_FIFO_INTS) - handle_rx(port); - if (isr & TX_FIFO_INTS) - handle_tx(port); - if (isr & TCTS) - handle_delta_cts(port); - - spin_unlock(&port->lock); - - return IRQ_HANDLED; -} - -static unsigned int vt8500_tx_empty(struct uart_port *port) -{ - return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ? - TIOCSER_TEMT : 0; -} - -static unsigned int vt8500_get_mctrl(struct uart_port *port) -{ - unsigned int usr; - - usr = vt8500_read(port, VT8500_URUSR); - if (usr & (1 << 4)) - return TIOCM_CTS; - else - return 0; -} - -static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ -} - -static void vt8500_break_ctl(struct uart_port *port, int break_ctl) -{ - if (break_ctl) - vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9), - VT8500_URLCR); -} - -static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud) -{ - unsigned long div; - unsigned int loops = 1000; - - div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff); - - if (unlikely((baud < 900) || (baud > 921600))) - div |= 7; - else - div |= (921600 / baud) - 1; - - while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops) - cpu_relax(); - vt8500_write(port, div, VT8500_URDIV); - - return baud; -} - -static int vt8500_startup(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = - container_of(port, struct vt8500_port, uart); - int ret; - - snprintf(vt8500_port->name, sizeof(vt8500_port->name), - "vt8500_serial%d", port->line); - - ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH, - vt8500_port->name, port); - if (unlikely(ret)) - return ret; - - vt8500_write(port, 0x03, VT8500_URLCR); /* enable TX & RX */ - - return 0; -} - -static void vt8500_shutdown(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = - container_of(port, struct vt8500_port, uart); - - vt8500_port->ier = 0; - - /* disable interrupts and FIFOs */ - vt8500_write(&vt8500_port->uart, 0, VT8500_URIER); - vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR); - free_irq(port->irq, port); -} - -static void vt8500_set_termios(struct uart_port *port, - struct ktermios *termios, - struct ktermios *old) -{ - struct vt8500_port *vt8500_port = - container_of(port, struct vt8500_port, uart); - unsigned long flags; - unsigned int baud, lcr; - unsigned int loops = 1000; - - spin_lock_irqsave(&port->lock, flags); - - /* calculate and set baud rate */ - baud = uart_get_baud_rate(port, termios, old, 900, 921600); - baud = vt8500_set_baud_rate(port, baud); - if (tty_termios_baud_rate(termios)) - tty_termios_encode_baud_rate(termios, baud, baud); - - /* calculate parity */ - lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR); - lcr &= ~((1 << 5) | (1 << 4)); - if (termios->c_cflag & PARENB) { - lcr |= (1 << 4); - termios->c_cflag &= ~CMSPAR; - if (termios->c_cflag & PARODD) - lcr |= (1 << 5); - } - - /* calculate bits per char */ - lcr &= ~(1 << 2); - switch (termios->c_cflag & CSIZE) { - case CS7: - break; - case CS8: - default: - lcr |= (1 << 2); - termios->c_cflag &= ~CSIZE; - termios->c_cflag |= CS8; - break; - } - - /* calculate stop bits */ - lcr &= ~(1 << 3); - if (termios->c_cflag & CSTOPB) - lcr |= (1 << 3); - - /* set parity, bits per char, and stop bit */ - vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR); - - /* Configure status bits to ignore based on termio flags. */ - port->read_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->read_status_mask = FER | PER; - - uart_update_timeout(port, termios->c_cflag, baud); - - /* Reset FIFOs */ - vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR); - while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc) - && --loops) - cpu_relax(); - - /* Every possible FIFO-related interrupt */ - vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS; - - /* - * CTS flow control - */ - if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag)) - vt8500_port->ier |= TCTS; - - vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR); - vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER); - - spin_unlock_irqrestore(&port->lock, flags); -} - -static const char *vt8500_type(struct uart_port *port) -{ - struct vt8500_port *vt8500_port = - container_of(port, struct vt8500_port, uart); - return vt8500_port->name; -} - -static void vt8500_release_port(struct uart_port *port) -{ -} - -static int vt8500_request_port(struct uart_port *port) -{ - return 0; -} - -static void vt8500_config_port(struct uart_port *port, int flags) -{ - port->type = PORT_VT8500; -} - -static int vt8500_verify_port(struct uart_port *port, - struct serial_struct *ser) -{ - if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500)) - return -EINVAL; - if (unlikely(port->irq != ser->irq)) - return -EINVAL; - return 0; -} - -static struct vt8500_port *vt8500_uart_ports[4]; -static struct uart_driver vt8500_uart_driver; - -#ifdef CONFIG_SERIAL_VT8500_CONSOLE - -static inline void wait_for_xmitr(struct uart_port *port) -{ - unsigned int status, tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - do { - status = vt8500_read(port, VT8500_URFIDX); - - if (--tmout == 0) - break; - udelay(1); - } while (status & 0x10); -} - -static void vt8500_console_putchar(struct uart_port *port, int c) -{ - wait_for_xmitr(port); - writeb(c, port->membase + VT8500_TXFIFO); -} - -static void vt8500_console_write(struct console *co, const char *s, - unsigned int count) -{ - struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index]; - unsigned long ier; - - BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr); - - ier = vt8500_read(&vt8500_port->uart, VT8500_URIER); - vt8500_write(&vt8500_port->uart, VT8500_URIER, 0); - - uart_console_write(&vt8500_port->uart, s, count, - vt8500_console_putchar); - - /* - * Finally, wait for transmitter to become empty - * and switch back to FIFO - */ - wait_for_xmitr(&vt8500_port->uart); - vt8500_write(&vt8500_port->uart, VT8500_URIER, ier); -} - -static int __init vt8500_console_setup(struct console *co, char *options) -{ - struct vt8500_port *vt8500_port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - - if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0)) - return -ENXIO; - - vt8500_port = vt8500_uart_ports[co->index]; - - if (!vt8500_port) - return -ENODEV; - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&vt8500_port->uart, - co, baud, parity, bits, flow); -} - -static struct console vt8500_console = { - .name = "ttyWMT", - .write = vt8500_console_write, - .device = uart_console_device, - .setup = vt8500_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &vt8500_uart_driver, -}; - -#define VT8500_CONSOLE (&vt8500_console) - -#else -#define VT8500_CONSOLE NULL -#endif - -static struct uart_ops vt8500_uart_pops = { - .tx_empty = vt8500_tx_empty, - .set_mctrl = vt8500_set_mctrl, - .get_mctrl = vt8500_get_mctrl, - .stop_tx = vt8500_stop_tx, - .start_tx = vt8500_start_tx, - .stop_rx = vt8500_stop_rx, - .enable_ms = vt8500_enable_ms, - .break_ctl = vt8500_break_ctl, - .startup = vt8500_startup, - .shutdown = vt8500_shutdown, - .set_termios = vt8500_set_termios, - .type = vt8500_type, - .release_port = vt8500_release_port, - .request_port = vt8500_request_port, - .config_port = vt8500_config_port, - .verify_port = vt8500_verify_port, -}; - -static struct uart_driver vt8500_uart_driver = { - .owner = THIS_MODULE, - .driver_name = "vt8500_serial", - .dev_name = "ttyWMT", - .nr = 6, - .cons = VT8500_CONSOLE, -}; - -static int __init vt8500_serial_probe(struct platform_device *pdev) -{ - struct vt8500_port *vt8500_port; - struct resource *mmres, *irqres; - int ret; - - mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!mmres || !irqres) - return -ENODEV; - - 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; - vt8500_port->uart.irq = irqres->start; - vt8500_port->uart.fifosize = 16; - vt8500_port->uart.ops = &vt8500_uart_pops; - vt8500_port->uart.line = pdev->id; - vt8500_port->uart.dev = &pdev->dev; - vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - vt8500_port->uart.uartclk = 24000000; - - snprintf(vt8500_port->name, sizeof(vt8500_port->name), - "VT8500 UART%d", pdev->id); - - vt8500_port->uart.membase = ioremap(mmres->start, - mmres->end - mmres->start + 1); - if (!vt8500_port->uart.membase) { - ret = -ENOMEM; - goto err; - } - - vt8500_uart_ports[pdev->id] = vt8500_port; - - uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); - - platform_set_drvdata(pdev, vt8500_port); - - return 0; - -err: - kfree(vt8500_port); - return ret; -} - -static int __devexit vt8500_serial_remove(struct platform_device *pdev) -{ - struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart); - kfree(vt8500_port); - - return 0; -} - -static struct platform_driver vt8500_platform_driver = { - .probe = vt8500_serial_probe, - .remove = vt8500_serial_remove, - .driver = { - .name = "vt8500_serial", - .owner = THIS_MODULE, - }, -}; - -static int __init vt8500_serial_init(void) -{ - int ret; - - ret = uart_register_driver(&vt8500_uart_driver); - if (unlikely(ret)) - return ret; - - ret = platform_driver_register(&vt8500_platform_driver); - - if (unlikely(ret)) - uart_unregister_driver(&vt8500_uart_driver); - - return ret; -} - -static void __exit vt8500_serial_exit(void) -{ -#ifdef CONFIG_SERIAL_VT8500_CONSOLE - unregister_console(&vt8500_console); -#endif - platform_driver_unregister(&vt8500_platform_driver); - uart_unregister_driver(&vt8500_uart_driver); -} - -module_init(vt8500_serial_init); -module_exit(vt8500_serial_exit); - -MODULE_AUTHOR("Alexey Charkov "); -MODULE_DESCRIPTION("Driver for vt8500 serial device"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/zs.c b/drivers/serial/zs.c deleted file mode 100644 index 1a7fd3e70315..000000000000 --- a/drivers/serial/zs.c +++ /dev/null @@ -1,1304 +0,0 @@ -/* - * zs.c: Serial port driver for IOASIC DECstations. - * - * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. - * Derived from drivers/macintosh/macserial.c by Harald Koerfgen. - * - * DECstation changes - * Copyright (C) 1998-2000 Harald Koerfgen - * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki - * - * For the rest of the code the original Copyright applies: - * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * - * - * Note: for IOASIC systems the wiring is as follows: - * - * mouse/keyboard: - * DIN-7 MJ-4 signal SCC - * 2 1 TxD <- A.TxD - * 3 4 RxD -> A.RxD - * - * EIA-232/EIA-423: - * DB-25 MMJ-6 signal SCC - * 2 2 TxD <- B.TxD - * 3 5 RxD -> B.RxD - * 4 RTS <- ~A.RTS - * 5 CTS -> ~B.CTS - * 6 6 DSR -> ~A.SYNC - * 8 CD -> ~B.DCD - * 12 DSRS(DCE) -> ~A.CTS (*) - * 15 TxC -> B.TxC - * 17 RxC -> B.RxC - * 20 1 DTR <- ~A.DTR - * 22 RI -> ~A.DCD - * 23 DSRS(DTE) <- ~B.RTS - * - * (*) EIA-232 defines the signal at this pin to be SCD, while DSRS(DCE) - * is shared with DSRS(DTE) at pin 23. - * - * As you can immediately notice the wiring of the RTS, DTR and DSR signals - * is a bit odd. This makes the handling of port B unnecessarily - * complicated and prevents the use of some automatic modes of operation. - */ - -#if defined(CONFIG_SERIAL_ZS_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include "zs.h" - - -MODULE_AUTHOR("Maciej W. Rozycki "); -MODULE_DESCRIPTION("DECstation Z85C30 serial driver"); -MODULE_LICENSE("GPL"); - - -static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; -static char zs_version[] __initdata = "0.10"; - -/* - * It would be nice to dynamically allocate everything that - * depends on ZS_NUM_SCCS, so we could support any number of - * Z85C30s, but for now... - */ -#define ZS_NUM_SCCS 2 /* Max # of ZS chips supported. */ -#define ZS_NUM_CHAN 2 /* 2 channels per chip. */ -#define ZS_CHAN_A 0 /* Index of the channel A. */ -#define ZS_CHAN_B 1 /* Index of the channel B. */ -#define ZS_CHAN_IO_SIZE 8 /* IOMEM space size. */ -#define ZS_CHAN_IO_STRIDE 4 /* Register alignment. */ -#define ZS_CHAN_IO_OFFSET 1 /* The SCC resides on the high byte - of the 16-bit IOBUS. */ -#define ZS_CLOCK 7372800 /* Z85C30 PCLK input clock rate. */ - -#define to_zport(uport) container_of(uport, struct zs_port, port) - -struct zs_parms { - resource_size_t scc[ZS_NUM_SCCS]; - int irq[ZS_NUM_SCCS]; -}; - -static struct zs_scc zs_sccs[ZS_NUM_SCCS]; - -static u8 zs_init_regs[ZS_NUM_REGS] __initdata = { - 0, /* write 0 */ - PAR_SPEC, /* write 1 */ - 0, /* write 2 */ - 0, /* write 3 */ - X16CLK | SB1, /* write 4 */ - 0, /* write 5 */ - 0, 0, 0, /* write 6, 7, 8 */ - MIE | DLC | NV, /* write 9 */ - NRZ, /* write 10 */ - TCBR | RCBR, /* write 11 */ - 0, 0, /* BRG time constant, write 12 + 13 */ - BRSRC | BRENABL, /* write 14 */ - 0, /* write 15 */ -}; - -/* - * Debugging. - */ -#undef ZS_DEBUG_REGS - - -/* - * Reading and writing Z85C30 registers. - */ -static void recovery_delay(void) -{ - udelay(2); -} - -static u8 read_zsreg(struct zs_port *zport, int reg) -{ - void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET; - u8 retval; - - if (reg != 0) { - writeb(reg & 0xf, control); - fast_iob(); - recovery_delay(); - } - retval = readb(control); - recovery_delay(); - return retval; -} - -static void write_zsreg(struct zs_port *zport, int reg, u8 value) -{ - void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET; - - if (reg != 0) { - writeb(reg & 0xf, control); - fast_iob(); recovery_delay(); - } - writeb(value, control); - fast_iob(); - recovery_delay(); - return; -} - -static u8 read_zsdata(struct zs_port *zport) -{ - void __iomem *data = zport->port.membase + - ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET; - u8 retval; - - retval = readb(data); - recovery_delay(); - return retval; -} - -static void write_zsdata(struct zs_port *zport, u8 value) -{ - void __iomem *data = zport->port.membase + - ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET; - - writeb(value, data); - fast_iob(); - recovery_delay(); - return; -} - -#ifdef ZS_DEBUG_REGS -void zs_dump(void) -{ - struct zs_port *zport; - int i, j; - - for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { - zport = &zs_sccs[i / ZS_NUM_CHAN].zport[i % ZS_NUM_CHAN]; - - if (!zport->scc) - continue; - - for (j = 0; j < 16; j++) - printk("W%-2d = 0x%02x\t", j, zport->regs[j]); - printk("\n"); - for (j = 0; j < 16; j++) - printk("R%-2d = 0x%02x\t", j, read_zsreg(zport, j)); - printk("\n\n"); - } -} -#endif - - -static void zs_spin_lock_cond_irq(spinlock_t *lock, int irq) -{ - if (irq) - spin_lock_irq(lock); - else - spin_lock(lock); -} - -static void zs_spin_unlock_cond_irq(spinlock_t *lock, int irq) -{ - if (irq) - spin_unlock_irq(lock); - else - spin_unlock(lock); -} - -static int zs_receive_drain(struct zs_port *zport) -{ - int loops = 10000; - - while ((read_zsreg(zport, R0) & Rx_CH_AV) && --loops) - read_zsdata(zport); - return loops; -} - -static int zs_transmit_drain(struct zs_port *zport, int irq) -{ - struct zs_scc *scc = zport->scc; - int loops = 10000; - - while (!(read_zsreg(zport, R0) & Tx_BUF_EMP) && --loops) { - zs_spin_unlock_cond_irq(&scc->zlock, irq); - udelay(2); - zs_spin_lock_cond_irq(&scc->zlock, irq); - } - return loops; -} - -static int zs_line_drain(struct zs_port *zport, int irq) -{ - struct zs_scc *scc = zport->scc; - int loops = 10000; - - while (!(read_zsreg(zport, R1) & ALL_SNT) && --loops) { - zs_spin_unlock_cond_irq(&scc->zlock, irq); - udelay(2); - zs_spin_lock_cond_irq(&scc->zlock, irq); - } - return loops; -} - - -static void load_zsregs(struct zs_port *zport, u8 *regs, int irq) -{ - /* Let the current transmission finish. */ - zs_line_drain(zport, irq); - /* Load 'em up. */ - write_zsreg(zport, R3, regs[3] & ~RxENABLE); - write_zsreg(zport, R5, regs[5] & ~TxENAB); - write_zsreg(zport, R4, regs[4]); - write_zsreg(zport, R9, regs[9]); - write_zsreg(zport, R1, regs[1]); - write_zsreg(zport, R2, regs[2]); - write_zsreg(zport, R10, regs[10]); - write_zsreg(zport, R14, regs[14] & ~BRENABL); - write_zsreg(zport, R11, regs[11]); - write_zsreg(zport, R12, regs[12]); - write_zsreg(zport, R13, regs[13]); - write_zsreg(zport, R14, regs[14]); - write_zsreg(zport, R15, regs[15]); - if (regs[3] & RxENABLE) - write_zsreg(zport, R3, regs[3]); - if (regs[5] & TxENAB) - write_zsreg(zport, R5, regs[5]); - return; -} - - -/* - * Status handling routines. - */ - -/* - * zs_tx_empty() -- get the transmitter empty status - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static unsigned int zs_tx_empty(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - unsigned long flags; - u8 status; - - spin_lock_irqsave(&scc->zlock, flags); - status = read_zsreg(zport, R1); - spin_unlock_irqrestore(&scc->zlock, flags); - - return status & ALL_SNT ? TIOCSER_TEMT : 0; -} - -static unsigned int zs_raw_get_ab_mctrl(struct zs_port *zport_a, - struct zs_port *zport_b) -{ - u8 status_a, status_b; - unsigned int mctrl; - - status_a = read_zsreg(zport_a, R0); - status_b = read_zsreg(zport_b, R0); - - mctrl = ((status_b & CTS) ? TIOCM_CTS : 0) | - ((status_b & DCD) ? TIOCM_CAR : 0) | - ((status_a & DCD) ? TIOCM_RNG : 0) | - ((status_a & SYNC_HUNT) ? TIOCM_DSR : 0); - - return mctrl; -} - -static unsigned int zs_raw_get_mctrl(struct zs_port *zport) -{ - struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; - - return zport != zport_a ? zs_raw_get_ab_mctrl(zport_a, zport) : 0; -} - -static unsigned int zs_raw_xor_mctrl(struct zs_port *zport) -{ - struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; - unsigned int mmask, mctrl, delta; - u8 mask_a, mask_b; - - if (zport == zport_a) - return 0; - - mask_a = zport_a->regs[15]; - mask_b = zport->regs[15]; - - mmask = ((mask_b & CTSIE) ? TIOCM_CTS : 0) | - ((mask_b & DCDIE) ? TIOCM_CAR : 0) | - ((mask_a & DCDIE) ? TIOCM_RNG : 0) | - ((mask_a & SYNCIE) ? TIOCM_DSR : 0); - - mctrl = zport->mctrl; - if (mmask) { - mctrl &= ~mmask; - mctrl |= zs_raw_get_ab_mctrl(zport_a, zport) & mmask; - } - - delta = mctrl ^ zport->mctrl; - if (delta) - zport->mctrl = mctrl; - - return delta; -} - -static unsigned int zs_get_mctrl(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - unsigned int mctrl; - - spin_lock(&scc->zlock); - mctrl = zs_raw_get_mctrl(zport); - spin_unlock(&scc->zlock); - - return mctrl; -} - -static void zs_set_mctrl(struct uart_port *uport, unsigned int mctrl) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; - u8 oldloop, newloop; - - spin_lock(&scc->zlock); - if (zport != zport_a) { - if (mctrl & TIOCM_DTR) - zport_a->regs[5] |= DTR; - else - zport_a->regs[5] &= ~DTR; - if (mctrl & TIOCM_RTS) - zport_a->regs[5] |= RTS; - else - zport_a->regs[5] &= ~RTS; - write_zsreg(zport_a, R5, zport_a->regs[5]); - } - - /* Rarely modified, so don't poke at hardware unless necessary. */ - oldloop = zport->regs[14]; - newloop = oldloop; - if (mctrl & TIOCM_LOOP) - newloop |= LOOPBAK; - else - newloop &= ~LOOPBAK; - if (newloop != oldloop) { - zport->regs[14] = newloop; - write_zsreg(zport, R14, zport->regs[14]); - } - spin_unlock(&scc->zlock); -} - -static void zs_raw_stop_tx(struct zs_port *zport) -{ - write_zsreg(zport, R0, RES_Tx_P); - zport->tx_stopped = 1; -} - -static void zs_stop_tx(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - - spin_lock(&scc->zlock); - zs_raw_stop_tx(zport); - spin_unlock(&scc->zlock); -} - -static void zs_raw_transmit_chars(struct zs_port *); - -static void zs_start_tx(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - - spin_lock(&scc->zlock); - if (zport->tx_stopped) { - zs_transmit_drain(zport, 0); - zport->tx_stopped = 0; - zs_raw_transmit_chars(zport); - } - spin_unlock(&scc->zlock); -} - -static void zs_stop_rx(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; - - spin_lock(&scc->zlock); - zport->regs[15] &= ~BRKIE; - zport->regs[1] &= ~(RxINT_MASK | TxINT_ENAB); - zport->regs[1] |= RxINT_DISAB; - - if (zport != zport_a) { - /* A-side DCD tracks RI and SYNC tracks DSR. */ - zport_a->regs[15] &= ~(DCDIE | SYNCIE); - write_zsreg(zport_a, R15, zport_a->regs[15]); - if (!(zport_a->regs[15] & BRKIE)) { - zport_a->regs[1] &= ~EXT_INT_ENAB; - write_zsreg(zport_a, R1, zport_a->regs[1]); - } - - /* This-side DCD tracks DCD and CTS tracks CTS. */ - zport->regs[15] &= ~(DCDIE | CTSIE); - zport->regs[1] &= ~EXT_INT_ENAB; - } else { - /* DCD tracks RI and SYNC tracks DSR for the B side. */ - if (!(zport->regs[15] & (DCDIE | SYNCIE))) - zport->regs[1] &= ~EXT_INT_ENAB; - } - - write_zsreg(zport, R15, zport->regs[15]); - write_zsreg(zport, R1, zport->regs[1]); - spin_unlock(&scc->zlock); -} - -static void zs_enable_ms(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; - - if (zport == zport_a) - return; - - spin_lock(&scc->zlock); - - /* Clear Ext interrupts if not being handled already. */ - if (!(zport_a->regs[1] & EXT_INT_ENAB)) - write_zsreg(zport_a, R0, RES_EXT_INT); - - /* A-side DCD tracks RI and SYNC tracks DSR. */ - zport_a->regs[1] |= EXT_INT_ENAB; - zport_a->regs[15] |= DCDIE | SYNCIE; - - /* This-side DCD tracks DCD and CTS tracks CTS. */ - zport->regs[15] |= DCDIE | CTSIE; - - zs_raw_xor_mctrl(zport); - - write_zsreg(zport_a, R1, zport_a->regs[1]); - write_zsreg(zport_a, R15, zport_a->regs[15]); - write_zsreg(zport, R15, zport->regs[15]); - spin_unlock(&scc->zlock); -} - -static void zs_break_ctl(struct uart_port *uport, int break_state) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - unsigned long flags; - - spin_lock_irqsave(&scc->zlock, flags); - if (break_state == -1) - zport->regs[5] |= SND_BRK; - else - zport->regs[5] &= ~SND_BRK; - write_zsreg(zport, R5, zport->regs[5]); - spin_unlock_irqrestore(&scc->zlock, flags); -} - - -/* - * Interrupt handling routines. - */ -#define Rx_BRK 0x0100 /* BREAK event software flag. */ -#define Rx_SYS 0x0200 /* SysRq event software flag. */ - -static void zs_receive_chars(struct zs_port *zport) -{ - struct uart_port *uport = &zport->port; - struct zs_scc *scc = zport->scc; - struct uart_icount *icount; - unsigned int avail, status, ch, flag; - int count; - - for (count = 16; count; count--) { - spin_lock(&scc->zlock); - avail = read_zsreg(zport, R0) & Rx_CH_AV; - spin_unlock(&scc->zlock); - if (!avail) - break; - - spin_lock(&scc->zlock); - status = read_zsreg(zport, R1) & (Rx_OVR | FRM_ERR | PAR_ERR); - ch = read_zsdata(zport); - spin_unlock(&scc->zlock); - - flag = TTY_NORMAL; - - icount = &uport->icount; - icount->rx++; - - /* Handle the null char got when BREAK is removed. */ - if (!ch) - status |= zport->tty_break; - if (unlikely(status & - (Rx_OVR | FRM_ERR | PAR_ERR | Rx_SYS | Rx_BRK))) { - zport->tty_break = 0; - - /* Reset the error indication. */ - if (status & (Rx_OVR | FRM_ERR | PAR_ERR)) { - spin_lock(&scc->zlock); - write_zsreg(zport, R0, ERR_RES); - spin_unlock(&scc->zlock); - } - - if (status & (Rx_SYS | Rx_BRK)) { - icount->brk++; - /* SysRq discards the null char. */ - if (status & Rx_SYS) - continue; - } else if (status & FRM_ERR) - icount->frame++; - else if (status & PAR_ERR) - icount->parity++; - if (status & Rx_OVR) - icount->overrun++; - - status &= uport->read_status_mask; - if (status & Rx_BRK) - flag = TTY_BREAK; - else if (status & FRM_ERR) - flag = TTY_FRAME; - else if (status & PAR_ERR) - flag = TTY_PARITY; - } - - if (uart_handle_sysrq_char(uport, ch)) - continue; - - uart_insert_char(uport, status, Rx_OVR, ch, flag); - } - - tty_flip_buffer_push(uport->state->port.tty); -} - -static void zs_raw_transmit_chars(struct zs_port *zport) -{ - struct circ_buf *xmit = &zport->port.state->xmit; - - /* XON/XOFF chars. */ - if (zport->port.x_char) { - write_zsdata(zport, zport->port.x_char); - zport->port.icount.tx++; - zport->port.x_char = 0; - return; - } - - /* If nothing to do or stopped or hardware stopped. */ - if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) { - zs_raw_stop_tx(zport); - return; - } - - /* Send char. */ - write_zsdata(zport, xmit->buf[xmit->tail]); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - zport->port.icount.tx++; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&zport->port); - - /* Are we are done? */ - if (uart_circ_empty(xmit)) - zs_raw_stop_tx(zport); -} - -static void zs_transmit_chars(struct zs_port *zport) -{ - struct zs_scc *scc = zport->scc; - - spin_lock(&scc->zlock); - zs_raw_transmit_chars(zport); - spin_unlock(&scc->zlock); -} - -static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) -{ - struct uart_port *uport = &zport->port; - struct zs_scc *scc = zport->scc; - unsigned int delta; - u8 status, brk; - - spin_lock(&scc->zlock); - - /* Get status from Read Register 0. */ - status = read_zsreg(zport, R0); - - if (zport->regs[15] & BRKIE) { - brk = status & BRK_ABRT; - if (brk && !zport->brk) { - spin_unlock(&scc->zlock); - if (uart_handle_break(uport)) - zport->tty_break = Rx_SYS; - else - zport->tty_break = Rx_BRK; - spin_lock(&scc->zlock); - } - zport->brk = brk; - } - - if (zport != zport_a) { - delta = zs_raw_xor_mctrl(zport); - spin_unlock(&scc->zlock); - - if (delta & TIOCM_CTS) - uart_handle_cts_change(uport, - zport->mctrl & TIOCM_CTS); - if (delta & TIOCM_CAR) - uart_handle_dcd_change(uport, - zport->mctrl & TIOCM_CAR); - if (delta & TIOCM_RNG) - uport->icount.dsr++; - if (delta & TIOCM_DSR) - uport->icount.rng++; - - if (delta) - wake_up_interruptible(&uport->state->port.delta_msr_wait); - - spin_lock(&scc->zlock); - } - - /* Clear the status condition... */ - write_zsreg(zport, R0, RES_EXT_INT); - - spin_unlock(&scc->zlock); -} - -/* - * This is the Z85C30 driver's generic interrupt routine. - */ -static irqreturn_t zs_interrupt(int irq, void *dev_id) -{ - struct zs_scc *scc = dev_id; - struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; - struct zs_port *zport_b = &scc->zport[ZS_CHAN_B]; - irqreturn_t status = IRQ_NONE; - u8 zs_intreg; - int count; - - /* - * NOTE: The read register 3, which holds the irq status, - * does so for both channels on each chip. Although - * the status value itself must be read from the A - * channel and is only valid when read from channel A. - * Yes... broken hardware... - */ - for (count = 16; count; count--) { - spin_lock(&scc->zlock); - zs_intreg = read_zsreg(zport_a, R3); - spin_unlock(&scc->zlock); - if (!zs_intreg) - break; - - /* - * We do not like losing characters, so we prioritise - * interrupt sources a little bit differently than - * the SCC would, was it allowed to. - */ - if (zs_intreg & CHBRxIP) - zs_receive_chars(zport_b); - if (zs_intreg & CHARxIP) - zs_receive_chars(zport_a); - if (zs_intreg & CHBEXT) - zs_status_handle(zport_b, zport_a); - if (zs_intreg & CHAEXT) - zs_status_handle(zport_a, zport_a); - if (zs_intreg & CHBTxIP) - zs_transmit_chars(zport_b); - if (zs_intreg & CHATxIP) - zs_transmit_chars(zport_a); - - status = IRQ_HANDLED; - } - - return status; -} - - -/* - * Finally, routines used to initialize the serial port. - */ -static int zs_startup(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - unsigned long flags; - int irq_guard; - int ret; - - irq_guard = atomic_add_return(1, &scc->irq_guard); - if (irq_guard == 1) { - ret = request_irq(zport->port.irq, zs_interrupt, - IRQF_SHARED, "scc", scc); - if (ret) { - atomic_add(-1, &scc->irq_guard); - printk(KERN_ERR "zs: can't get irq %d\n", - zport->port.irq); - return ret; - } - } - - spin_lock_irqsave(&scc->zlock, flags); - - /* Clear the receive FIFO. */ - zs_receive_drain(zport); - - /* Clear the interrupt registers. */ - write_zsreg(zport, R0, ERR_RES); - write_zsreg(zport, R0, RES_Tx_P); - /* But Ext only if not being handled already. */ - if (!(zport->regs[1] & EXT_INT_ENAB)) - write_zsreg(zport, R0, RES_EXT_INT); - - /* Finally, enable sequencing and interrupts. */ - zport->regs[1] &= ~RxINT_MASK; - zport->regs[1] |= RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB; - zport->regs[3] |= RxENABLE; - zport->regs[15] |= BRKIE; - write_zsreg(zport, R1, zport->regs[1]); - write_zsreg(zport, R3, zport->regs[3]); - write_zsreg(zport, R5, zport->regs[5]); - write_zsreg(zport, R15, zport->regs[15]); - - /* Record the current state of RR0. */ - zport->mctrl = zs_raw_get_mctrl(zport); - zport->brk = read_zsreg(zport, R0) & BRK_ABRT; - - zport->tx_stopped = 1; - - spin_unlock_irqrestore(&scc->zlock, flags); - - return 0; -} - -static void zs_shutdown(struct uart_port *uport) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - unsigned long flags; - int irq_guard; - - spin_lock_irqsave(&scc->zlock, flags); - - zport->regs[3] &= ~RxENABLE; - write_zsreg(zport, R5, zport->regs[5]); - write_zsreg(zport, R3, zport->regs[3]); - - spin_unlock_irqrestore(&scc->zlock, flags); - - irq_guard = atomic_add_return(-1, &scc->irq_guard); - if (!irq_guard) - free_irq(zport->port.irq, scc); -} - - -static void zs_reset(struct zs_port *zport) -{ - struct zs_scc *scc = zport->scc; - int irq; - unsigned long flags; - - spin_lock_irqsave(&scc->zlock, flags); - irq = !irqs_disabled_flags(flags); - if (!scc->initialised) { - /* Reset the pointer first, just in case... */ - read_zsreg(zport, R0); - /* And let the current transmission finish. */ - zs_line_drain(zport, irq); - write_zsreg(zport, R9, FHWRES); - udelay(10); - write_zsreg(zport, R9, 0); - scc->initialised = 1; - } - load_zsregs(zport, zport->regs, irq); - spin_unlock_irqrestore(&scc->zlock, flags); -} - -static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, - struct ktermios *old_termios) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; - int irq; - unsigned int baud, brg; - unsigned long flags; - - spin_lock_irqsave(&scc->zlock, flags); - irq = !irqs_disabled_flags(flags); - - /* Byte size. */ - zport->regs[3] &= ~RxNBITS_MASK; - zport->regs[5] &= ~TxNBITS_MASK; - switch (termios->c_cflag & CSIZE) { - case CS5: - zport->regs[3] |= Rx5; - zport->regs[5] |= Tx5; - break; - case CS6: - zport->regs[3] |= Rx6; - zport->regs[5] |= Tx6; - break; - case CS7: - zport->regs[3] |= Rx7; - zport->regs[5] |= Tx7; - break; - case CS8: - default: - zport->regs[3] |= Rx8; - zport->regs[5] |= Tx8; - break; - } - - /* Parity and stop bits. */ - zport->regs[4] &= ~(XCLK_MASK | SB_MASK | PAR_ENA | PAR_EVEN); - if (termios->c_cflag & CSTOPB) - zport->regs[4] |= SB2; - else - zport->regs[4] |= SB1; - if (termios->c_cflag & PARENB) - zport->regs[4] |= PAR_ENA; - if (!(termios->c_cflag & PARODD)) - zport->regs[4] |= PAR_EVEN; - switch (zport->clk_mode) { - case 64: - zport->regs[4] |= X64CLK; - break; - case 32: - zport->regs[4] |= X32CLK; - break; - case 16: - zport->regs[4] |= X16CLK; - break; - case 1: - zport->regs[4] |= X1CLK; - break; - default: - BUG(); - } - - baud = uart_get_baud_rate(uport, termios, old_termios, 0, - uport->uartclk / zport->clk_mode / 4); - - brg = ZS_BPS_TO_BRG(baud, uport->uartclk / zport->clk_mode); - zport->regs[12] = brg & 0xff; - zport->regs[13] = (brg >> 8) & 0xff; - - uart_update_timeout(uport, termios->c_cflag, baud); - - uport->read_status_mask = Rx_OVR; - if (termios->c_iflag & INPCK) - uport->read_status_mask |= FRM_ERR | PAR_ERR; - if (termios->c_iflag & (BRKINT | PARMRK)) - uport->read_status_mask |= Rx_BRK; - - uport->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - uport->ignore_status_mask |= FRM_ERR | PAR_ERR; - if (termios->c_iflag & IGNBRK) { - uport->ignore_status_mask |= Rx_BRK; - if (termios->c_iflag & IGNPAR) - uport->ignore_status_mask |= Rx_OVR; - } - - if (termios->c_cflag & CREAD) - zport->regs[3] |= RxENABLE; - else - zport->regs[3] &= ~RxENABLE; - - if (zport != zport_a) { - if (!(termios->c_cflag & CLOCAL)) { - zport->regs[15] |= DCDIE; - } else - zport->regs[15] &= ~DCDIE; - if (termios->c_cflag & CRTSCTS) { - zport->regs[15] |= CTSIE; - } else - zport->regs[15] &= ~CTSIE; - zs_raw_xor_mctrl(zport); - } - - /* Load up the new values. */ - load_zsregs(zport, zport->regs, irq); - - spin_unlock_irqrestore(&scc->zlock, flags); -} - -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void zs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct zs_port *zport = to_zport(uport); - - if (state < 3) - zport->regs[5] |= TxENAB; - else - zport->regs[5] &= ~TxENAB; - write_zsreg(zport, R5, zport->regs[5]); -} - - -static const char *zs_type(struct uart_port *uport) -{ - return "Z85C30 SCC"; -} - -static void zs_release_port(struct uart_port *uport) -{ - iounmap(uport->membase); - uport->membase = 0; - release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE); -} - -static int zs_map_port(struct uart_port *uport) -{ - if (!uport->membase) - uport->membase = ioremap_nocache(uport->mapbase, - ZS_CHAN_IO_SIZE); - if (!uport->membase) { - printk(KERN_ERR "zs: Cannot map MMIO\n"); - return -ENOMEM; - } - return 0; -} - -static int zs_request_port(struct uart_port *uport) -{ - int ret; - - if (!request_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE, "scc")) { - printk(KERN_ERR "zs: Unable to reserve MMIO resource\n"); - return -EBUSY; - } - ret = zs_map_port(uport); - if (ret) { - release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE); - return ret; - } - return 0; -} - -static void zs_config_port(struct uart_port *uport, int flags) -{ - struct zs_port *zport = to_zport(uport); - - if (flags & UART_CONFIG_TYPE) { - if (zs_request_port(uport)) - return; - - uport->type = PORT_ZS; - - zs_reset(zport); - } -} - -static int zs_verify_port(struct uart_port *uport, struct serial_struct *ser) -{ - struct zs_port *zport = to_zport(uport); - int ret = 0; - - if (ser->type != PORT_UNKNOWN && ser->type != PORT_ZS) - ret = -EINVAL; - if (ser->irq != uport->irq) - ret = -EINVAL; - if (ser->baud_base != uport->uartclk / zport->clk_mode / 4) - ret = -EINVAL; - return ret; -} - - -static struct uart_ops zs_ops = { - .tx_empty = zs_tx_empty, - .set_mctrl = zs_set_mctrl, - .get_mctrl = zs_get_mctrl, - .stop_tx = zs_stop_tx, - .start_tx = zs_start_tx, - .stop_rx = zs_stop_rx, - .enable_ms = zs_enable_ms, - .break_ctl = zs_break_ctl, - .startup = zs_startup, - .shutdown = zs_shutdown, - .set_termios = zs_set_termios, - .pm = zs_pm, - .type = zs_type, - .release_port = zs_release_port, - .request_port = zs_request_port, - .config_port = zs_config_port, - .verify_port = zs_verify_port, -}; - -/* - * Initialize Z85C30 port structures. - */ -static int __init zs_probe_sccs(void) -{ - static int probed; - struct zs_parms zs_parms; - int chip, side, irq; - int n_chips = 0; - int i; - - if (probed) - return 0; - - irq = dec_interrupt[DEC_IRQ_SCC0]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC0; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; - n_chips++; - } - irq = dec_interrupt[DEC_IRQ_SCC1]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC1; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; - n_chips++; - } - if (!n_chips) - return -ENXIO; - - probed = 1; - - for (chip = 0; chip < n_chips; chip++) { - spin_lock_init(&zs_sccs[chip].zlock); - for (side = 0; side < ZS_NUM_CHAN; side++) { - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct uart_port *uport = &zport->port; - - zport->scc = &zs_sccs[chip]; - zport->clk_mode = 16; - - uport->irq = zs_parms.irq[chip]; - uport->uartclk = ZS_CLOCK; - uport->fifosize = 1; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &zs_ops; - uport->line = chip * ZS_NUM_CHAN + side; - uport->mapbase = dec_kn_slot_base + - zs_parms.scc[chip] + - (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; - - for (i = 0; i < ZS_NUM_REGS; i++) - zport->regs[i] = zs_init_regs[i]; - } - } - - return 0; -} - - -#ifdef CONFIG_SERIAL_ZS_CONSOLE -static void zs_console_putchar(struct uart_port *uport, int ch) -{ - struct zs_port *zport = to_zport(uport); - struct zs_scc *scc = zport->scc; - int irq; - unsigned long flags; - - spin_lock_irqsave(&scc->zlock, flags); - irq = !irqs_disabled_flags(flags); - if (zs_transmit_drain(zport, irq)) - write_zsdata(zport, ch); - spin_unlock_irqrestore(&scc->zlock, flags); -} - -/* - * Print a string to the serial port trying not to disturb - * any possible real use of the port... - */ -static void zs_console_write(struct console *co, const char *s, - unsigned int count) -{ - int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN; - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct zs_scc *scc = zport->scc; - unsigned long flags; - u8 txint, txenb; - int irq; - - /* Disable transmit interrupts and enable the transmitter. */ - spin_lock_irqsave(&scc->zlock, flags); - txint = zport->regs[1]; - txenb = zport->regs[5]; - if (txint & TxINT_ENAB) { - zport->regs[1] = txint & ~TxINT_ENAB; - write_zsreg(zport, R1, zport->regs[1]); - } - if (!(txenb & TxENAB)) { - zport->regs[5] = txenb | TxENAB; - write_zsreg(zport, R5, zport->regs[5]); - } - spin_unlock_irqrestore(&scc->zlock, flags); - - uart_console_write(&zport->port, s, count, zs_console_putchar); - - /* Restore transmit interrupts and the transmitter enable. */ - spin_lock_irqsave(&scc->zlock, flags); - irq = !irqs_disabled_flags(flags); - zs_line_drain(zport, irq); - if (!(txenb & TxENAB)) { - zport->regs[5] &= ~TxENAB; - write_zsreg(zport, R5, zport->regs[5]); - } - if (txint & TxINT_ENAB) { - zport->regs[1] |= TxINT_ENAB; - write_zsreg(zport, R1, zport->regs[1]); - } - spin_unlock_irqrestore(&scc->zlock, flags); -} - -/* - * Setup serial console baud/bits/parity. We do two things here: - * - construct a cflag setting for the first uart_open() - * - initialise the serial port - * Return non-zero if we didn't find a serial port. - */ -static int __init zs_console_setup(struct console *co, char *options) -{ - int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN; - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct uart_port *uport = &zport->port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; - int ret; - - ret = zs_map_port(uport); - if (ret) - return ret; - - zs_reset(zport); - zs_pm(uport, 0, -1); - - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); - return uart_set_options(uport, co, baud, parity, bits, flow); -} - -static struct uart_driver zs_reg; -static struct console zs_console = { - .name = "ttyS", - .write = zs_console_write, - .device = uart_console_device, - .setup = zs_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, - .data = &zs_reg, -}; - -/* - * Register console. - */ -static int __init zs_serial_console_init(void) -{ - int ret; - - ret = zs_probe_sccs(); - if (ret) - return ret; - register_console(&zs_console); - - return 0; -} - -console_initcall(zs_serial_console_init); - -#define SERIAL_ZS_CONSOLE &zs_console -#else -#define SERIAL_ZS_CONSOLE NULL -#endif /* CONFIG_SERIAL_ZS_CONSOLE */ - -static struct uart_driver zs_reg = { - .owner = THIS_MODULE, - .driver_name = "serial", - .dev_name = "ttyS", - .major = TTY_MAJOR, - .minor = 64, - .nr = ZS_NUM_SCCS * ZS_NUM_CHAN, - .cons = SERIAL_ZS_CONSOLE, -}; - -/* zs_init inits the driver. */ -static int __init zs_init(void) -{ - int i, ret; - - pr_info("%s%s\n", zs_name, zs_version); - - /* Find out how many Z85C30 SCCs we have. */ - ret = zs_probe_sccs(); - if (ret) - return ret; - - ret = uart_register_driver(&zs_reg); - if (ret) - return ret; - - for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_add_one_port(&zs_reg, uport); - } - - return 0; -} - -static void __exit zs_exit(void) -{ - int i; - - for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_remove_one_port(&zs_reg, uport); - } - - uart_unregister_driver(&zs_reg); -} - -module_init(zs_init); -module_exit(zs_exit); diff --git a/drivers/serial/zs.h b/drivers/serial/zs.h deleted file mode 100644 index aa921b57d827..000000000000 --- a/drivers/serial/zs.h +++ /dev/null @@ -1,284 +0,0 @@ -/* - * zs.h: Definitions for the DECstation Z85C30 serial driver. - * - * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras. - * Adapted from drivers/macintosh/macserial.h by Harald Koerfgen. - * - * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 2004, 2005, 2007 Maciej W. Rozycki - */ -#ifndef _SERIAL_ZS_H -#define _SERIAL_ZS_H - -#ifdef __KERNEL__ - -#define ZS_NUM_REGS 16 - -/* - * This is our internal structure for each serial port's state. - */ -struct zs_port { - struct zs_scc *scc; /* Containing SCC. */ - struct uart_port port; /* Underlying UART. */ - - int clk_mode; /* May be 1, 16, 32, or 64. */ - - unsigned int tty_break; /* Set on BREAK condition. */ - int tx_stopped; /* Output is suspended. */ - - unsigned int mctrl; /* State of modem lines. */ - u8 brk; /* BREAK state from RR0. */ - - u8 regs[ZS_NUM_REGS]; /* Channel write registers. */ -}; - -/* - * Per-SCC state for locking and the interrupt handler. - */ -struct zs_scc { - struct zs_port zport[2]; - spinlock_t zlock; - atomic_t irq_guard; - int initialised; -}; - -#endif /* __KERNEL__ */ - -/* - * Conversion routines to/from brg time constants from/to bits per second. - */ -#define ZS_BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) -#define ZS_BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) - -/* - * The Zilog register set. - */ - -/* Write Register 0 (Command) */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 (Tx/Rx/Ext Int Enable and WAIT/DMA Commands) */ -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define RxINT_ALL 0x10 /* Int on all Rx Characters or error */ -#define RxINT_ERR 0x18 /* Int on error only */ -#define RxINT_MASK 0x18 - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register 2 (Interrupt Vector) */ - -/* Write Register 3 (Receive Parameters and Control) */ -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ -#define RxNBITS_MASK 0xc0 - -/* Write Register 4 (Transmit/Receive Miscellaneous Parameters and Modes) */ -#define PAR_ENA 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ -#define SB_MASK 0xc - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xc0 /* x64 clock mode */ -#define XCLK_MASK 0xc0 - -/* Write Register 5 (Transmit Parameters and Controls) */ -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define TxNBITS_MASK 0x60 -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 8 (Transmit Buffer) */ - -/* Write Register 9 (Master Interrupt Control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define SOFTACK 0x20 /* Software Interrupt Acknowledge */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (Miscellaneous Transmitter/Receiver Control Bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode Control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (Lower Byte of Baud Rate Generator Time Constant) */ - -/* Write Register 13 (Upper Byte of Baud Rate Generator Time Constant) */ - -/* Write Register 14 (Miscellaneous Control Bits) */ -#define BRENABL 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (External/Status Interrupt Control) */ -#define WR7P_EN 1 /* WR7 Prime SDLC Feature Enable */ -#define ZCIE 2 /* Zero count IE */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 (Transmit/Receive Buffer Status and External Status) */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 (Special Receive Condition Status) */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity Error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define FRM_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (Interrupt Vector (WR2) -- channel A). */ - -/* Read Register 2 (Modified Interrupt Vector -- channel B). */ - -/* Read Register 3 (Interrupt Pending Bits -- channel A only). */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 6 (SDLC FIFO Status and Byte Count LSB) */ - -/* Read Register 7 (SDLC FIFO Status and Byte Count MSB) */ - -/* Read Register 8 (Receive Data) */ - -/* Read Register 10 (Miscellaneous Status Bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (Lower Byte of Baud Rate Generator Constant (WR12)) */ - -/* Read Register 13 (Upper Byte of Baud Rate Generator Constant (WR13) */ - -/* Read Register 15 (External/Status Interrupt Control (WR15)) */ - -#endif /* _SERIAL_ZS_H */ diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index d3685f071b8d..396277216e4f 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_R3964) += n_r3964.o obj-y += vt/ obj-$(CONFIG_HVC_DRIVER) += hvc/ +obj-y += serial/ diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c new file mode 100644 index 000000000000..d89aa38c5cf0 --- /dev/null +++ b/drivers/tty/serial/21285.c @@ -0,0 +1,513 @@ +/* + * linux/drivers/serial/21285.c + * + * Driver for the serial port on the 21285 StrongArm-110 core logic chip. + * + * Based on drivers/char/serial.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BAUD_BASE (mem_fclk_21285/64) + +#define SERIAL_21285_NAME "ttyFB" +#define SERIAL_21285_MAJOR 204 +#define SERIAL_21285_MINOR 4 + +#define RXSTAT_DUMMY_READ 0x80000000 +#define RXSTAT_FRAME (1 << 0) +#define RXSTAT_PARITY (1 << 1) +#define RXSTAT_OVERRUN (1 << 2) +#define RXSTAT_ANYERR (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN) + +#define H_UBRLCR_BREAK (1 << 0) +#define H_UBRLCR_PARENB (1 << 1) +#define H_UBRLCR_PAREVN (1 << 2) +#define H_UBRLCR_STOPB (1 << 3) +#define H_UBRLCR_FIFO (1 << 4) + +static const char serial21285_name[] = "Footbridge UART"; + +#define tx_enabled(port) ((port)->unused[0]) +#define rx_enabled(port) ((port)->unused[1]) + +/* + * The documented expression for selecting the divisor is: + * BAUD_BASE / baud - 1 + * However, typically BAUD_BASE is not divisible by baud, so + * we want to select the divisor that gives us the minimum + * error. Therefore, we want: + * int(BAUD_BASE / baud - 0.5) -> + * int(BAUD_BASE / baud - (baud >> 1) / baud) -> + * int((BAUD_BASE - (baud >> 1)) / baud) + */ + +static void serial21285_stop_tx(struct uart_port *port) +{ + if (tx_enabled(port)) { + disable_irq_nosync(IRQ_CONTX); + tx_enabled(port) = 0; + } +} + +static void serial21285_start_tx(struct uart_port *port) +{ + if (!tx_enabled(port)) { + enable_irq(IRQ_CONTX); + tx_enabled(port) = 1; + } +} + +static void serial21285_stop_rx(struct uart_port *port) +{ + if (rx_enabled(port)) { + disable_irq_nosync(IRQ_CONRX); + rx_enabled(port) = 0; + } +} + +static void serial21285_enable_ms(struct uart_port *port) +{ +} + +static irqreturn_t serial21285_rx_chars(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = port->state->port.tty; + unsigned int status, ch, flag, rxs, max_count = 256; + + status = *CSR_UARTFLG; + while (!(status & 0x10) && max_count--) { + ch = *CSR_UARTDR; + flag = TTY_NORMAL; + port->icount.rx++; + + rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ; + if (unlikely(rxs & RXSTAT_ANYERR)) { + if (rxs & RXSTAT_PARITY) + port->icount.parity++; + else if (rxs & RXSTAT_FRAME) + port->icount.frame++; + if (rxs & RXSTAT_OVERRUN) + port->icount.overrun++; + + rxs &= port->read_status_mask; + + if (rxs & RXSTAT_PARITY) + flag = TTY_PARITY; + else if (rxs & RXSTAT_FRAME) + flag = TTY_FRAME; + } + + uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag); + + status = *CSR_UARTFLG; + } + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + +static irqreturn_t serial21285_tx_chars(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct circ_buf *xmit = &port->state->xmit; + int count = 256; + + if (port->x_char) { + *CSR_UARTDR = port->x_char; + port->icount.tx++; + port->x_char = 0; + goto out; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + serial21285_stop_tx(port); + goto out; + } + + do { + *CSR_UARTDR = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0 && !(*CSR_UARTFLG & 0x20)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + serial21285_stop_tx(port); + + out: + return IRQ_HANDLED; +} + +static unsigned int serial21285_tx_empty(struct uart_port *port) +{ + return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT; +} + +/* no modem control lines */ +static unsigned int serial21285_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void serial21285_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int h_lcr; + + spin_lock_irqsave(&port->lock, flags); + h_lcr = *CSR_H_UBRLCR; + if (break_state) + h_lcr |= H_UBRLCR_BREAK; + else + h_lcr &= ~H_UBRLCR_BREAK; + *CSR_H_UBRLCR = h_lcr; + spin_unlock_irqrestore(&port->lock, flags); +} + +static int serial21285_startup(struct uart_port *port) +{ + int ret; + + tx_enabled(port) = 1; + rx_enabled(port) = 1; + + ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0, + serial21285_name, port); + if (ret == 0) { + ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0, + serial21285_name, port); + if (ret) + free_irq(IRQ_CONRX, port); + } + + return ret; +} + +static void serial21285_shutdown(struct uart_port *port) +{ + free_irq(IRQ_CONTX, port); + free_irq(IRQ_CONRX, port); +} + +static void +serial21285_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, quot, h_lcr, b; + + /* + * We don't support modem control lines. + */ + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); + termios->c_cflag |= CLOCAL; + + /* + * We don't support BREAK character recognition. + */ + termios->c_iflag &= ~(IGNBRK | BRKINT); + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + b = port->uartclk / (16 * quot); + tty_termios_encode_baud_rate(termios, b, b); + + switch (termios->c_cflag & CSIZE) { + case CS5: + h_lcr = 0x00; + break; + case CS6: + h_lcr = 0x20; + break; + case CS7: + h_lcr = 0x40; + break; + default: /* CS8 */ + h_lcr = 0x60; + break; + } + + if (termios->c_cflag & CSTOPB) + h_lcr |= H_UBRLCR_STOPB; + if (termios->c_cflag & PARENB) { + h_lcr |= H_UBRLCR_PARENB; + if (!(termios->c_cflag & PARODD)) + h_lcr |= H_UBRLCR_PAREVN; + } + + if (port->fifosize) + h_lcr |= H_UBRLCR_FIFO; + + spin_lock_irqsave(&port->lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Which character status flags are we interested in? + */ + port->read_status_mask = RXSTAT_OVERRUN; + if (termios->c_iflag & INPCK) + port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; + + /* + * Which character status flags should we ignore? + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY; + if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) + port->ignore_status_mask |= RXSTAT_OVERRUN; + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= RXSTAT_DUMMY_READ; + + quot -= 1; + + *CSR_UARTCON = 0; + *CSR_L_UBRLCR = quot & 0xff; + *CSR_M_UBRLCR = (quot >> 8) & 0x0f; + *CSR_H_UBRLCR = h_lcr; + *CSR_UARTCON = 1; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *serial21285_type(struct uart_port *port) +{ + return port->type == PORT_21285 ? "DC21285" : NULL; +} + +static void serial21285_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, 32); +} + +static int serial21285_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, 32, serial21285_name) + != NULL ? 0 : -EBUSY; +} + +static void serial21285_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0) + port->type = PORT_21285; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285) + ret = -EINVAL; + if (ser->irq != NO_IRQ) + ret = -EINVAL; + if (ser->baud_base != port->uartclk / 16) + ret = -EINVAL; + return ret; +} + +static struct uart_ops serial21285_ops = { + .tx_empty = serial21285_tx_empty, + .get_mctrl = serial21285_get_mctrl, + .set_mctrl = serial21285_set_mctrl, + .stop_tx = serial21285_stop_tx, + .start_tx = serial21285_start_tx, + .stop_rx = serial21285_stop_rx, + .enable_ms = serial21285_enable_ms, + .break_ctl = serial21285_break_ctl, + .startup = serial21285_startup, + .shutdown = serial21285_shutdown, + .set_termios = serial21285_set_termios, + .type = serial21285_type, + .release_port = serial21285_release_port, + .request_port = serial21285_request_port, + .config_port = serial21285_config_port, + .verify_port = serial21285_verify_port, +}; + +static struct uart_port serial21285_port = { + .mapbase = 0x42000160, + .iotype = UPIO_MEM, + .irq = NO_IRQ, + .fifosize = 16, + .ops = &serial21285_ops, + .flags = UPF_BOOT_AUTOCONF, +}; + +static void serial21285_setup_ports(void) +{ + serial21285_port.uartclk = mem_fclk_21285 / 4; +} + +#ifdef CONFIG_SERIAL_21285_CONSOLE +static void serial21285_console_putchar(struct uart_port *port, int ch) +{ + while (*CSR_UARTFLG & 0x20) + barrier(); + *CSR_UARTDR = ch; +} + +static void +serial21285_console_write(struct console *co, const char *s, + unsigned int count) +{ + uart_console_write(&serial21285_port, s, count, serial21285_console_putchar); +} + +static void __init +serial21285_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (*CSR_UARTCON == 1) { + unsigned int tmp; + + tmp = *CSR_H_UBRLCR; + switch (tmp & 0x60) { + case 0x00: + *bits = 5; + break; + case 0x20: + *bits = 6; + break; + case 0x40: + *bits = 7; + break; + default: + case 0x60: + *bits = 8; + break; + } + + if (tmp & H_UBRLCR_PARENB) { + *parity = 'o'; + if (tmp & H_UBRLCR_PAREVN) + *parity = 'e'; + } + + tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8); + + *baud = port->uartclk / (16 * (tmp + 1)); + } +} + +static int __init serial21285_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &serial21285_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (machine_is_personal_server()) + baud = 57600; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + serial21285_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver serial21285_reg; + +static struct console serial21285_console = +{ + .name = SERIAL_21285_NAME, + .write = serial21285_console_write, + .device = uart_console_device, + .setup = serial21285_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial21285_reg, +}; + +static int __init rs285_console_init(void) +{ + serial21285_setup_ports(); + register_console(&serial21285_console); + return 0; +} +console_initcall(rs285_console_init); + +#define SERIAL_21285_CONSOLE &serial21285_console +#else +#define SERIAL_21285_CONSOLE NULL +#endif + +static struct uart_driver serial21285_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyFB", + .dev_name = "ttyFB", + .major = SERIAL_21285_MAJOR, + .minor = SERIAL_21285_MINOR, + .nr = 1, + .cons = SERIAL_21285_CONSOLE, +}; + +static int __init serial21285_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: 21285 driver\n"); + + serial21285_setup_ports(); + + ret = uart_register_driver(&serial21285_reg); + if (ret == 0) + uart_add_one_port(&serial21285_reg, &serial21285_port); + + return ret; +} + +static void __exit serial21285_exit(void) +{ + uart_remove_one_port(&serial21285_reg, &serial21285_port); + uart_unregister_driver(&serial21285_reg); +} + +module_init(serial21285_init); +module_exit(serial21285_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver"); +MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR); diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c new file mode 100644 index 000000000000..be0ebce36e54 --- /dev/null +++ b/drivers/tty/serial/68328serial.c @@ -0,0 +1,1472 @@ +/* 68328serial.c: Serial port driver for 68328 microcontroller + * + * Copyright (C) 1995 David S. Miller + * Copyright (C) 1998 Kenneth Albanowski + * Copyright (C) 1998, 1999 D. Jeff Dionne + * Copyright (C) 1999 Vladimir Gurevich + * Copyright (C) 2002-2003 David McCullough + * Copyright (C) 2002 Greg Ungerer + * + * VZ Support/Fixes Evan Stawnyczy + * Multiple UART support Daniel Potts + * Power management support Daniel Potts + * VZ Second Serial Port enable Phil Wilshire + * 2.4/2.5 port David McCullough + */ + +#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 +#include +#include + +/* (es) */ +/* note: perhaps we can murge these files, so that you can just + * define 1 of them, and they can sort that out for themselves + */ +#if defined(CONFIG_M68EZ328) +#include +#else +#if defined(CONFIG_M68VZ328) +#include +#else +#include +#endif /* CONFIG_M68VZ328 */ +#endif /* CONFIG_M68EZ328 */ + +#include "68328serial.h" + +/* Turn off usage of real serial interrupt code, to "support" Copilot */ +#ifdef CONFIG_XCOPILOT_BUGS +#undef USE_INTS +#else +#define USE_INTS +#endif + +static struct m68k_serial m68k_soft[NR_PORTS]; + +static unsigned int uart_irqs[NR_PORTS] = UART_IRQ_DEFNS; + +/* multiple ports are contiguous in memory */ +m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR; + +struct tty_struct m68k_ttys; +struct m68k_serial *m68k_consinfo = 0; + +#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */ + +struct tty_driver *serial_driver; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* Debugging... DEBUG_INTR is bad to use when one of the zs + * lines is your console ;( + */ +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW + +#define RS_ISR_PASS_LIMIT 256 + +static void change_speed(struct m68k_serial *info); + +/* + * Setup for console. Argument comes from the boot command line. + */ + +/* note: this is messy, but it works, again, perhaps defined somewhere else?*/ +#ifdef CONFIG_M68VZ328 +#define CONSOLE_BAUD_RATE 19200 +#define DEFAULT_CBAUD B19200 +#endif + + +#ifndef CONSOLE_BAUD_RATE +#define CONSOLE_BAUD_RATE 9600 +#define DEFAULT_CBAUD B9600 +#endif + + +static int m68328_console_initted = 0; +static int m68328_console_baud = CONSOLE_BAUD_RATE; +static int m68328_console_cbaud = DEFAULT_CBAUD; + + +static inline int serial_paranoia_check(struct m68k_serial *info, + char *name, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct %s in %s\n"; + static const char *badinfo = + "Warning: null m68k_serial for %s in %s\n"; + + if (!info) { + printk(badinfo, name, routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#endif + return 0; +} + +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0 }; + +/* Sets or clears DTR/RTS on the requested line */ +static inline void m68k_rtsdtr(struct m68k_serial *ss, int set) +{ + if (set) { + /* set the RTS/CTS line */ + } else { + /* clear it */ + } + return; +} + +/* Utility routines */ +static inline int get_baud(struct m68k_serial *ss) +{ + unsigned long result = 115200; + unsigned short int baud = uart_addr[ss->line].ubaud; + if (GET_FIELD(baud, UBAUD_PRESCALER) == 0x38) result = 38400; + result >>= GET_FIELD(baud, UBAUD_DIVIDE); + + return result; +} + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_stop(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_stop")) + return; + + local_irq_save(flags); + uart->ustcnt &= ~USTCNT_TXEN; + local_irq_restore(flags); +} + +static int rs_put_char(char ch) +{ + int flags, loops = 0; + + local_irq_save(flags); + + while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) { + loops++; + udelay(5); + } + + UTX_TXDATA = ch; + udelay(5); + local_irq_restore(flags); + return 1; +} + +static void rs_start(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_start")) + return; + + local_irq_save(flags); + if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) { +#ifdef USE_INTS + uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK; +#else + uart->ustcnt |= USTCNT_TXEN; +#endif + } + local_irq_restore(flags); +} + +/* Drop into either the boot monitor or kadb upon receiving a break + * from keyboard/console input. + */ +static void batten_down_hatches(void) +{ + /* Drop into the debugger */ +} + +static void status_handle(struct m68k_serial *info, unsigned short status) +{ +#if 0 + if(status & DCD) { + if((info->port.tty->termios->c_cflag & CRTSCTS) && + ((info->curregs[3] & AUTO_ENAB)==0)) { + info->curregs[3] |= AUTO_ENAB; + info->pendregs[3] |= AUTO_ENAB; + write_zsreg(info->m68k_channel, 3, info->curregs[3]); + } + } else { + if((info->curregs[3] & AUTO_ENAB)) { + info->curregs[3] &= ~AUTO_ENAB; + info->pendregs[3] &= ~AUTO_ENAB; + write_zsreg(info->m68k_channel, 3, info->curregs[3]); + } + } +#endif + /* If this is console input and this is a + * 'break asserted' status change interrupt + * see if we can drop into the debugger + */ + if((status & URX_BREAK) && info->break_abort) + batten_down_hatches(); + return; +} + +static void receive_chars(struct m68k_serial *info, unsigned short rx) +{ + struct tty_struct *tty = info->port.tty; + m68328_uart *uart = &uart_addr[info->line]; + unsigned char ch, flag; + + /* + * This do { } while() loop will get ALL chars out of Rx FIFO + */ +#ifndef CONFIG_XCOPILOT_BUGS + do { +#endif + ch = GET_FIELD(rx, URX_RXDATA); + + if(info->is_cons) { + if(URX_BREAK & rx) { /* whee, break received */ + status_handle(info, rx); + return; +#ifdef CONFIG_MAGIC_SYSRQ + } else if (ch == 0x10) { /* ^P */ + show_state(); + show_free_areas(); + show_buffers(); +/* show_net_buffers(); */ + return; + } else if (ch == 0x12) { /* ^R */ + emergency_restart(); + return; +#endif /* CONFIG_MAGIC_SYSRQ */ + } + } + + if(!tty) + goto clear_and_exit; + + flag = TTY_NORMAL; + + if(rx & URX_PARITY_ERROR) { + flag = TTY_PARITY; + status_handle(info, rx); + } else if(rx & URX_OVRUN) { + flag = TTY_OVERRUN; + status_handle(info, rx); + } else if(rx & URX_FRAME_ERROR) { + flag = TTY_FRAME; + status_handle(info, rx); + } + tty_insert_flip_char(tty, ch, flag); +#ifndef CONFIG_XCOPILOT_BUGS + } while((rx = uart->urx.w) & URX_DATA_READY); +#endif + + tty_schedule_flip(tty); + +clear_and_exit: + return; +} + +static void transmit_chars(struct m68k_serial *info) +{ + m68328_uart *uart = &uart_addr[info->line]; + + if (info->x_char) { + /* Send next char */ + uart->utx.b.txdata = info->x_char; + info->x_char = 0; + goto clear_and_return; + } + + if((info->xmit_cnt <= 0) || info->port.tty->stopped) { + /* That's peculiar... TX ints off */ + uart->ustcnt &= ~USTCNT_TX_INTR_MASK; + goto clear_and_return; + } + + /* Send char */ + uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + + if (info->xmit_cnt < WAKEUP_CHARS) + schedule_work(&info->tqueue); + + if(info->xmit_cnt <= 0) { + /* All done for now... TX ints off */ + uart->ustcnt &= ~USTCNT_TX_INTR_MASK; + goto clear_and_return; + } + +clear_and_return: + /* Clear interrupt (should be auto)*/ + return; +} + +/* + * This is the serial driver's generic interrupt routine + */ +irqreturn_t rs_interrupt(int irq, void *dev_id) +{ + struct m68k_serial *info = dev_id; + m68328_uart *uart; + unsigned short rx; + unsigned short tx; + + uart = &uart_addr[info->line]; + rx = uart->urx.w; + +#ifdef USE_INTS + tx = uart->utx.w; + + if (rx & URX_DATA_READY) receive_chars(info, rx); + if (tx & UTX_TX_AVAIL) transmit_chars(info); +#else + receive_chars(info, rx); +#endif + return IRQ_HANDLED; +} + +static void do_softint(struct work_struct *work) +{ + struct m68k_serial *info = container_of(work, struct m68k_serial, tqueue); + struct tty_struct *tty; + + tty = info->port.tty; + if (!tty) + return; +#if 0 + if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + tty_wakeup(tty); + } +#endif +} + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_serial_hangup() -> tty->hangup() -> rs_hangup() + * + */ +static void do_serial_hangup(struct work_struct *work) +{ + struct m68k_serial *info = container_of(work, struct m68k_serial, tqueue_hangup); + struct tty_struct *tty; + + tty = info->port.tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static int startup(struct m68k_serial * info) +{ + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + if (info->flags & S_INITIALIZED) + return 0; + + if (!info->xmit_buf) { + info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL); + if (!info->xmit_buf) + return -ENOMEM; + } + + local_irq_save(flags); + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + + uart->ustcnt = USTCNT_UEN; + info->xmit_fifo_size = 1; + uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN; + (void)uart->urx.w; + + /* + * Finally, enable sequencing and interrupts + */ +#ifdef USE_INTS + uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | + USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK; +#else + uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK; +#endif + + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * and set the speed of the serial port + */ + + change_speed(info); + + info->flags |= S_INITIALIZED; + local_irq_restore(flags); + return 0; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(struct m68k_serial * info) +{ + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + uart->ustcnt = 0; /* All off! */ + if (!(info->flags & S_INITIALIZED)) + return; + + local_irq_save(flags); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->flags &= ~S_INITIALIZED; + local_irq_restore(flags); +} + +struct { + int divisor, prescale; +} +#ifndef CONFIG_M68VZ328 + hw_baud_table[18] = { + {0,0}, /* 0 */ + {0,0}, /* 50 */ + {0,0}, /* 75 */ + {0,0}, /* 110 */ + {0,0}, /* 134 */ + {0,0}, /* 150 */ + {0,0}, /* 200 */ + {7,0x26}, /* 300 */ + {6,0x26}, /* 600 */ + {5,0x26}, /* 1200 */ + {0,0}, /* 1800 */ + {4,0x26}, /* 2400 */ + {3,0x26}, /* 4800 */ + {2,0x26}, /* 9600 */ + {1,0x26}, /* 19200 */ + {0,0x26}, /* 38400 */ + {1,0x38}, /* 57600 */ + {0,0x38}, /* 115200 */ +}; +#else + hw_baud_table[18] = { + {0,0}, /* 0 */ + {0,0}, /* 50 */ + {0,0}, /* 75 */ + {0,0}, /* 110 */ + {0,0}, /* 134 */ + {0,0}, /* 150 */ + {0,0}, /* 200 */ + {0,0}, /* 300 */ + {7,0x26}, /* 600 */ + {6,0x26}, /* 1200 */ + {0,0}, /* 1800 */ + {5,0x26}, /* 2400 */ + {4,0x26}, /* 4800 */ + {3,0x26}, /* 9600 */ + {2,0x26}, /* 19200 */ + {1,0x26}, /* 38400 */ + {0,0x26}, /* 57600 */ + {1,0x38}, /* 115200 */ +}; +#endif +/* rate = 1036800 / ((65 - prescale) * (1<line]; + unsigned short port; + unsigned short ustcnt; + unsigned cflag; + int i; + + if (!info->port.tty || !info->port.tty->termios) + return; + cflag = info->port.tty->termios->c_cflag; + if (!(port = info->port)) + return; + + ustcnt = uart->ustcnt; + uart->ustcnt = ustcnt & ~USTCNT_TXEN; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i = (i & ~CBAUDEX) + B38400; + } + + info->baud = baud_table[i]; + uart->ubaud = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | + PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); + + ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); + + if ((cflag & CSIZE) == CS8) + ustcnt |= USTCNT_8_7; + + if (cflag & CSTOPB) + ustcnt |= USTCNT_STOP; + + if (cflag & PARENB) + ustcnt |= USTCNT_PARITYEN; + if (cflag & PARODD) + ustcnt |= USTCNT_ODD_EVEN; + +#ifdef CONFIG_SERIAL_68328_RTS_CTS + if (cflag & CRTSCTS) { + uart->utx.w &= ~ UTX_NOCTS; + } else { + uart->utx.w |= UTX_NOCTS; + } +#endif + + ustcnt |= USTCNT_TXEN; + + uart->ustcnt = ustcnt; + return; +} + +/* + * Fair output driver allows a process to speak. + */ +static void rs_fair_output(void) +{ + int left; /* Output no more than that */ + unsigned long flags; + struct m68k_serial *info = &m68k_soft[0]; + char c; + + if (info == 0) return; + if (info->xmit_buf == 0) return; + + local_irq_save(flags); + left = info->xmit_cnt; + while (left != 0) { + c = info->xmit_buf[info->xmit_tail]; + info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + local_irq_restore(flags); + + rs_put_char(c); + + local_irq_save(flags); + left = min(info->xmit_cnt, left-1); + } + + /* Last character is being transmitted now (hopefully). */ + udelay(5); + + local_irq_restore(flags); + return; +} + +/* + * m68k_console_print is registered for printk. + */ +void console_print_68328(const char *p) +{ + char c; + + while((c=*(p++)) != 0) { + if(c == '\n') + rs_put_char('\r'); + rs_put_char(c); + } + + /* Comment this if you want to have a strict interrupt-driven output */ + rs_fair_output(); + + return; +} + +static void rs_set_ldisc(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_set_ldisc")) + return; + + info->is_cons = (tty->termios->c_line == N_TTY); + + printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off"); +} + +static void rs_flush_chars(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_flush_chars")) + return; +#ifndef USE_INTS + for(;;) { +#endif + + /* Enable transmitter */ + local_irq_save(flags); + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) { + local_irq_restore(flags); + return; + } + +#ifdef USE_INTS + uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK; +#else + uart->ustcnt |= USTCNT_TXEN; +#endif + +#ifdef USE_INTS + if (uart->utx.w & UTX_TX_AVAIL) { +#else + if (1) { +#endif + /* Send char */ + uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } + +#ifndef USE_INTS + while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); + } +#endif + local_irq_restore(flags); +} + +extern void console_printn(const char * b, int count); + +static int rs_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + int c, total = 0; + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_write")) + return 0; + + if (!tty || !info->xmit_buf) + return 0; + + local_save_flags(flags); + while (1) { + local_irq_disable(); + c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + local_irq_restore(flags); + + if (c <= 0) + break; + + memcpy(info->xmit_buf + info->xmit_head, buf, c); + + local_irq_disable(); + info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt += c; + local_irq_restore(flags); + buf += c; + count -= c; + total += c; + } + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + /* Enable transmitter */ + local_irq_disable(); +#ifndef USE_INTS + while(info->xmit_cnt) { +#endif + + uart->ustcnt |= USTCNT_TXEN; +#ifdef USE_INTS + uart->ustcnt |= USTCNT_TX_INTR_MASK; +#else + while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5); +#endif + if (uart->utx.w & UTX_TX_AVAIL) { + uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } + +#ifndef USE_INTS + } +#endif + local_irq_restore(flags); + } + + return total; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->name, "rs_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + +static int rs_chars_in_buffer(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) + return 0; + return info->xmit_cnt; +} + +static void rs_flush_buffer(struct tty_struct *tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) + return; + local_irq_save(flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + local_irq_restore(flags); + tty_wakeup(tty); +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_throttle(struct tty_struct * tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + info->x_char = STOP_CHAR(tty); + + /* Turn off RTS line (do this atomic) */ +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + } + + /* Assert RTS line (do this atomic) */ +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int get_serial_info(struct m68k_serial * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = info->port; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + + return 0; +} + +static int set_serial_info(struct m68k_serial * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct m68k_serial old_info; + int retval = 0; + + if (!new_info) + return -EFAULT; + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + old_info = *info; + + if (!capable(CAP_SYS_ADMIN)) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.type != info->type) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~S_USR_MASK) != + (info->flags & ~S_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~S_USR_MASK) | + (new_serial.flags & S_USR_MASK)); + info->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud_base = new_serial.baud_base; + info->flags = ((info->flags & ~S_FLAGS) | + (new_serial.flags & S_FLAGS)); + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; + +check_and_exit: + retval = startup(info); + return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct m68k_serial * info, unsigned int *value) +{ +#ifdef CONFIG_SERIAL_68328_RTS_CTS + m68328_uart *uart = &uart_addr[info->line]; +#endif + unsigned char status; + unsigned long flags; + + local_irq_save(flags); +#ifdef CONFIG_SERIAL_68328_RTS_CTS + status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0; +#else + status = 0; +#endif + local_irq_restore(flags); + return put_user(status, value); +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break(struct m68k_serial * info, unsigned int duration) +{ + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + if (!info->port) + return; + local_irq_save(flags); +#ifdef USE_INTS + uart->utx.w |= UTX_SEND_BREAK; + msleep_interruptible(duration); + uart->utx.w &= ~UTX_SEND_BREAK; +#endif + local_irq_restore(flags); +} + +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; + int retval; + + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + send_break(info, 250); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + send_break(info, arg ? arg*(100) : 250); + return 0; + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + case TIOCSERGSTRUCT: + if (copy_to_user((struct m68k_serial *) arg, + info, sizeof(struct m68k_serial))) + return -EFAULT; + return 0; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct m68k_serial *info = (struct m68k_serial *)tty->driver_data; + + change_speed(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * S structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_close(struct tty_struct *tty, struct file * filp) +{ + struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; + m68328_uart *uart = &uart_addr[info->line]; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->name, "rs_close")) + return; + + local_irq_save(flags); + + if (tty_hung_up_p(filp)) { + local_irq_restore(flags); + return; + } + + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("rs_close: bad serial port count for ttyS%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + local_irq_restore(flags); + return; + } + info->flags |= S_CLOSING; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != S_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + + uart->ustcnt &= ~USTCNT_RXEN; + uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK); + + shutdown(info); + rs_flush_buffer(tty); + + tty_ldisc_flush(tty); + tty->closing = 0; + info->event = 0; + info->port.tty = NULL; +#warning "This is not and has never been valid so fix it" +#if 0 + if (tty->ldisc.num != ldiscs[N_TTY].num) { + if (tty->ldisc.close) + (tty->ldisc.close)(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if (tty->ldisc.open) + (tty->ldisc.open)(tty); + } +#endif + if (info->blocked_open) { + if (info->close_delay) { + msleep_interruptible(jiffies_to_msecs(info->close_delay)); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(S_NORMAL_ACTIVE|S_CLOSING); + wake_up_interruptible(&info->close_wait); + local_irq_restore(flags); +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void rs_hangup(struct tty_struct *tty) +{ + struct m68k_serial * info = (struct m68k_serial *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_hangup")) + return; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~S_NORMAL_ACTIVE; + info->port.tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct m68k_serial *info) +{ + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (info->flags & S_CLOSING) { + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & S_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + info->flags |= S_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); + + info->count--; + info->blocked_open++; + while (1) { + local_irq_disable(); + m68k_rtsdtr(info, 1); + local_irq_enable(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(info->flags & S_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & S_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & S_CLOSING) && do_clocal) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + tty_unlock(); + schedule(); + tty_lock(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + + if (retval) + return retval; + info->flags |= S_NORMAL_ACTIVE; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its S structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct m68k_serial *info; + int retval, line; + + line = tty->index; + + if (line >= NR_PORTS || line < 0) /* we have exactly one */ + return -ENODEV; + + info = &m68k_soft[line]; + + if (serial_paranoia_check(info, tty->name, "rs_open")) + return -ENODEV; + + info->count++; + tty->driver_data = info; + info->port.tty = tty; + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + return block_til_ready(tty, filp, info); +} + +/* Finally, routines used to initialize the serial driver. */ + +static void show_serial_version(void) +{ + printk("MC68328 serial driver version 1.00\n"); +} + +static const struct tty_operations rs_ops = { + .open = rs_open, + .close = rs_close, + .write = rs_write, + .flush_chars = rs_flush_chars, + .write_room = rs_write_room, + .chars_in_buffer = rs_chars_in_buffer, + .flush_buffer = rs_flush_buffer, + .ioctl = rs_ioctl, + .throttle = rs_throttle, + .unthrottle = rs_unthrottle, + .set_termios = rs_set_termios, + .stop = rs_stop, + .start = rs_start, + .hangup = rs_hangup, + .set_ldisc = rs_set_ldisc, +}; + +/* rs_init inits the driver */ +static int __init +rs68328_init(void) +{ + int flags, i; + struct m68k_serial *info; + + serial_driver = alloc_tty_driver(NR_PORTS); + if (!serial_driver) + return -ENOMEM; + + show_serial_version(); + + /* Initialize the tty_driver structure */ + /* SPARC: Not all of this is exactly right for us. */ + + serial_driver->name = "ttyS"; + serial_driver->major = TTY_MAJOR; + serial_driver->minor_start = 64; + serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + serial_driver->subtype = SERIAL_TYPE_NORMAL; + serial_driver->init_termios = tty_std_termios; + serial_driver->init_termios.c_cflag = + m68328_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(serial_driver, &rs_ops); + + if (tty_register_driver(serial_driver)) { + put_tty_driver(serial_driver); + printk(KERN_ERR "Couldn't register serial driver\n"); + return -ENOMEM; + } + + local_irq_save(flags); + + for(i=0;imagic = SERIAL_MAGIC; + info->port = (int) &uart_addr[i]; + info->port.tty = NULL; + info->irq = uart_irqs[i]; + info->custom_divisor = 16; + info->close_delay = 50; + info->closing_wait = 3000; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + INIT_WORK(&info->tqueue, do_softint); + INIT_WORK(&info->tqueue_hangup, do_serial_hangup); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + info->line = i; + info->is_cons = 1; /* Means shortcuts work */ + + printk("%s%d at 0x%08x (irq = %d)", serial_driver->name, info->line, + info->port, info->irq); + printk(" is a builtin MC68328 UART\n"); + +#ifdef CONFIG_M68VZ328 + if (i > 0 ) + PJSEL &= 0xCF; /* PSW enable second port output */ +#endif + + if (request_irq(uart_irqs[i], + rs_interrupt, + IRQF_DISABLED, + "M68328_UART", info)) + panic("Unable to attach 68328 serial interrupt\n"); + } + local_irq_restore(flags); + return 0; +} + +module_init(rs68328_init); + + + +static void m68328_set_baud(void) +{ + unsigned short ustcnt; + int i; + + ustcnt = USTCNT; + USTCNT = ustcnt & ~USTCNT_TXEN; + +again: + for (i = 0; i < ARRAY_SIZE(baud_table); i++) + if (baud_table[i] == m68328_console_baud) + break; + if (i >= ARRAY_SIZE(baud_table)) { + m68328_console_baud = 9600; + goto again; + } + + UBAUD = PUT_FIELD(UBAUD_DIVIDE, hw_baud_table[i].divisor) | + PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale); + ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7); + ustcnt |= USTCNT_8_7; + ustcnt |= USTCNT_TXEN; + USTCNT = ustcnt; + m68328_console_initted = 1; + return; +} + + +int m68328_console_setup(struct console *cp, char *arg) +{ + int i, n = CONSOLE_BAUD_RATE; + + if (!cp) + return(-1); + + if (arg) + n = simple_strtoul(arg,NULL,0); + + for (i = 0; i < ARRAY_SIZE(baud_table); i++) + if (baud_table[i] == n) + break; + if (i < ARRAY_SIZE(baud_table)) { + m68328_console_baud = n; + m68328_console_cbaud = 0; + if (i > 15) { + m68328_console_cbaud |= CBAUDEX; + i -= 15; + } + m68328_console_cbaud |= i; + } + + m68328_set_baud(); /* make sure baud rate changes */ + return(0); +} + + +static struct tty_driver *m68328_console_device(struct console *c, int *index) +{ + *index = c->index; + return serial_driver; +} + + +void m68328_console_write (struct console *co, const char *str, + unsigned int count) +{ + if (!m68328_console_initted) + m68328_set_baud(); + while (count--) { + if (*str == '\n') + rs_put_char('\r'); + rs_put_char( *str++ ); + } +} + + +static struct console m68328_driver = { + .name = "ttyS", + .write = m68328_console_write, + .device = m68328_console_device, + .setup = m68328_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + + +static int __init m68328_console_init(void) +{ + register_console(&m68328_driver); + return 0; +} + +console_initcall(m68328_console_init); diff --git a/drivers/tty/serial/68328serial.h b/drivers/tty/serial/68328serial.h new file mode 100644 index 000000000000..664ceb0a158c --- /dev/null +++ b/drivers/tty/serial/68328serial.h @@ -0,0 +1,188 @@ +/* 68328serial.h: Definitions for the mc68328 serial driver. + * + * Copyright (C) 1995 David S. Miller + * Copyright (C) 1998 Kenneth Albanowski + * Copyright (C) 1998, 1999 D. Jeff Dionne + * Copyright (C) 1999 Vladimir Gurevich + * + * VZ Support/Fixes Evan Stawnyczy + */ + +#ifndef _MC683XX_SERIAL_H +#define _MC683XX_SERIAL_H + + +struct serial_struct { + int type; + int line; + int port; + int irq; + int flags; + int xmit_fifo_size; + int custom_divisor; + int baud_base; + unsigned short close_delay; + char reserved_char[2]; + int hub6; /* FIXME: We don't have AT&T Hub6 boards! */ + unsigned short closing_wait; /* time to wait before closing */ + unsigned short closing_wait2; /* no longer used... */ + int reserved[4]; +}; + +/* + * For the close wait times, 0 means wait forever for serial port to + * flush its output. 65535 means don't wait at all. + */ +#define S_CLOSING_WAIT_INF 0 +#define S_CLOSING_WAIT_NONE 65535 + +/* + * Definitions for S_struct (and serial_struct) flags field + */ +#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes + on the callout port */ +#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */ +#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */ +#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ + +#define S_SPD_MASK 0x0030 +#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */ + +#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */ +#define S_SPD_CUST 0x0030 /* Use user-specified divisor */ + +#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */ +#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */ +#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ +#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ +#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */ + +#define S_FLAGS 0x0FFF /* Possible legal S flags */ +#define S_USR_MASK 0x0430 /* Legal flags that non-privileged + * users can set or reset */ + +/* Internal flags used only by kernel/chr_drv/serial.c */ +#define S_INITIALIZED 0x80000000 /* Serial port was initialized */ +#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ +#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ +#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ +#define S_CLOSING 0x08000000 /* Serial port is closing */ +#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */ +#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */ + +/* Software state per channel */ + +#ifdef __KERNEL__ + +/* + * I believe this is the optimal setting that reduces the number of interrupts. + * At high speeds the output might become a little "bursted" (use USTCNT_TXHE + * if that bothers you), but in most cases it will not, since we try to + * transmit characters every time rs_interrupt is called. Thus, quite often + * you'll see that a receive interrupt occures before the transmit one. + * -- Vladimir Gurevich + */ +#define USTCNT_TX_INTR_MASK (USTCNT_TXEE) + +/* + * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special + * "Old data interrupt" which occures whenever the data stay in the FIFO + * longer than 30 bits time. This allows us to use FIFO without compromising + * latency. '328 does not have this feature and without the real 328-based + * board I would assume that RXRE is the safest setting. + * + * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of + * interrupts. RXFE (receive queue full) causes the system to lose data + * at least at 115200 baud + * + * If your board is busy doing other stuff, you might consider to use + * RXRE (data ready intrrupt) instead. + * + * The other option is to make these INTR masks run-time configurable, so + * that people can dynamically adapt them according to the current usage. + * -- Vladimir Gurevich + */ + +/* (es) */ +#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328) +#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN) +#elif defined(CONFIG_M68328) +#define USTCNT_RX_INTR_MASK (USTCNT_RXRE) +#else +#error Please, define the Rx interrupt events for your CPU +#endif +/* (/es) */ + +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * For definitions of the flags field, see tty.h + */ + +struct m68k_serial { + char soft_carrier; /* Use soft carrier on this channel */ + char break_abort; /* Is serial console in, so process brk/abrt */ + char is_cons; /* Is this our console. */ + + /* We need to know the current clock divisor + * to read the bps rate the chip has currently + * loaded. + */ + unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */ + int baud; + int magic; + int baud_base; + int port; + int irq; + int flags; /* defined in tty.h */ + int type; /* UART type */ + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int xmit_fifo_size; + int custom_divisor; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int line; + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct work_struct tqueue; + struct work_struct tqueue_hangup; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; +}; + + +#define SERIAL_MAGIC 0x5301 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#define SERIAL_XMIT_SIZE 4096 + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define RS_EVENT_WRITE_WAKEUP 0 + +/* + * Define the number of ports supported and their irqs. + */ +#define NR_PORTS 1 +#define UART_IRQ_DEFNS {UART_IRQ_NUM} + +#endif /* __KERNEL__ */ +#endif /* !(_MC683XX_SERIAL_H) */ diff --git a/drivers/tty/serial/68360serial.c b/drivers/tty/serial/68360serial.c new file mode 100644 index 000000000000..88b13356ec10 --- /dev/null +++ b/drivers/tty/serial/68360serial.c @@ -0,0 +1,2978 @@ +/* + * UART driver for 68360 CPM SCC or SMC + * Copyright (c) 2000 D. Jeff Dionne , + * Copyright (c) 2000 Michael Leslie + * Copyright (c) 1997 Dan Malek + * + * I used the serial.c driver as the framework for this driver. + * Give credit to those guys. + * The original code was written for the MBX860 board. I tried to make + * it generic, but there may be some assumptions in the structures that + * have to be fixed later. + * To save porting time, I did not bother to change any object names + * that are not accessed outside of this file. + * It still needs lots of work........When it was easy, I included code + * to support the SCCs, but this has never been tested, nor is it complete. + * Only the SCCs support modem control, so that is not complete either. + * + * This module exports the following rs232 io functions: + * + * int rs_360_init(void); + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_KGDB +extern void breakpoint(void); +extern void set_debug_traps(void); +extern int kgdb_output_string (const char* s, unsigned int count); +#endif + + +/* #ifdef CONFIG_SERIAL_CONSOLE */ /* This seems to be a post 2.0 thing - mles */ +#include +#include + +/* this defines the index into rs_table for the port to use + */ +#ifndef CONFIG_SERIAL_CONSOLE_PORT +#define CONFIG_SERIAL_CONSOLE_PORT 1 /* ie SMC2 - note USE_SMC2 must be defined */ +#endif +/* #endif */ + +#if 0 +/* SCC2 for console + */ +#undef CONFIG_SERIAL_CONSOLE_PORT +#define CONFIG_SERIAL_CONSOLE_PORT 2 +#endif + + +#define TX_WAKEUP ASYNC_SHARE_IRQ + +static char *serial_name = "CPM UART driver"; +static char *serial_version = "0.03"; + +static struct tty_driver *serial_driver; +int serial_console_setup(struct console *co, char *options); + +/* + * Serial driver configuration section. Here are the various options: + */ +#define SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + +#define _INLINE_ inline + +#define DBG_CNT(s) + +/* We overload some of the items in the data structure to meet our + * needs. For example, the port address is the CPM parameter ram + * offset for the SCC or SMC. The maximum number of ports is 4 SCCs and + * 2 SMCs. The "hub6" field is used to indicate the channel number, with + * a flag indicating SCC or SMC, and the number is used as an index into + * the CPM parameter area for this device. + * The "type" field is currently set to 0, for PORT_UNKNOWN. It is + * not currently used. I should probably use it to indicate the port + * type of SMC or SCC. + * The SMCs do not support any modem control signals. + */ +#define smc_scc_num hub6 +#define NUM_IS_SCC ((int)0x00010000) +#define PORT_NUM(P) ((P) & 0x0000ffff) + + +#if defined (CONFIG_UCQUICC) + +volatile extern void *_periph_base; +/* sipex transceiver + * mode bits for are on pins + * + * SCC2 d16..19 + * SCC3 d20..23 + * SCC4 d24..27 + */ +#define SIPEX_MODE(n,m) ((m & 0x0f)<<(16+4*(n-1))) + +static uint sipex_mode_bits = 0x00000000; + +#endif + +/* There is no `serial_state' defined back here in 2.0. + * Try to get by with serial_struct + */ +/* #define serial_state serial_struct */ + +/* 2.4 -> 2.0 portability problem: async_icount in 2.4 has a few + * extras: */ + +#if 0 +struct async_icount_24 { + __u32 cts, dsr, rng, dcd, tx, rx; + __u32 frame, parity, overrun, brk; + __u32 buf_overrun; +} icount; +#endif + +#if 0 + +struct serial_state { + int magic; + int baud_base; + unsigned long port; + int irq; + int flags; + int hub6; + int type; + int line; + int revision; /* Chip revision (950) */ + int xmit_fifo_size; + int custom_divisor; + int count; + u8 *iomem_base; + u16 iomem_reg_shift; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + struct async_icount_24 icount; + int io_type; + struct async_struct *info; +}; +#endif + +#define SSTATE_MAGIC 0x5302 + + + +/* SMC2 is sometimes used for low performance TDM interfaces. Define + * this as 1 if you want SMC2 as a serial port UART managed by this driver. + * Define this as 0 if you wish to use SMC2 for something else. + */ +#define USE_SMC2 1 + +#if 0 +/* Define SCC to ttySx mapping. */ +#define SCC_NUM_BASE (USE_SMC2 + 1) /* SCC base tty "number" */ + +/* Define which SCC is the first one to use for a serial port. These + * are 0-based numbers, i.e. this assumes the first SCC (SCC1) is used + * for Ethernet, and the first available SCC for serial UART is SCC2. + * NOTE: IF YOU CHANGE THIS, you have to change the PROFF_xxx and + * interrupt vectors in the table below to match. + */ +#define SCC_IDX_BASE 1 /* table index */ +#endif + + +/* Processors other than the 860 only get SMCs configured by default. + * Either they don't have SCCs or they are allocated somewhere else. + * Of course, there are now 860s without some SCCs, so we will need to + * address that someday. + * The Embedded Planet Multimedia I/O cards use TDM interfaces to the + * stereo codec parts, and we use SMC2 to help support that. + */ +static struct serial_state rs_table[] = { +/* type line PORT IRQ FLAGS smc_scc_num (F.K.A. hub6) */ + { 0, 0, PRSLOT_SMC1, CPMVEC_SMC1, 0, 0 } /* SMC1 ttyS0 */ +#if USE_SMC2 + ,{ 0, 0, PRSLOT_SMC2, CPMVEC_SMC2, 0, 1 } /* SMC2 ttyS1 */ +#endif + +#if defined(CONFIG_SERIAL_68360_SCC) + ,{ 0, 0, PRSLOT_SCC2, CPMVEC_SCC2, 0, (NUM_IS_SCC | 1) } /* SCC2 ttyS2 */ + ,{ 0, 0, PRSLOT_SCC3, CPMVEC_SCC3, 0, (NUM_IS_SCC | 2) } /* SCC3 ttyS3 */ + ,{ 0, 0, PRSLOT_SCC4, CPMVEC_SCC4, 0, (NUM_IS_SCC | 3) } /* SCC4 ttyS4 */ +#endif +}; + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +/* The number of buffer descriptors and their sizes. + */ +#define RX_NUM_FIFO 4 +#define RX_BUF_SIZE 32 +#define TX_NUM_FIFO 4 +#define TX_BUF_SIZE 32 + +#define CONSOLE_NUM_FIFO 2 +#define CONSOLE_BUF_SIZE 4 + +char *console_fifos[CONSOLE_NUM_FIFO * CONSOLE_BUF_SIZE]; + +/* The async_struct in serial.h does not really give us what we + * need, so define our own here. + */ +typedef struct serial_info { + int magic; + int flags; + + struct serial_state *state; + /* struct serial_struct *state; */ + /* struct async_struct *state; */ + + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int timeout; + int line; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int blocked_open; /* # of blocked opens */ + struct work_struct tqueue; + struct work_struct tqueue_hangup; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + + +/* CPM Buffer Descriptor pointers. + */ + QUICC_BD *rx_bd_base; + QUICC_BD *rx_cur; + QUICC_BD *tx_bd_base; + QUICC_BD *tx_cur; +} ser_info_t; + + +/* since kmalloc_init() does not get called until much after this initialization: */ +static ser_info_t quicc_ser_info[NR_PORTS]; +static char rx_buf_pool[NR_PORTS * RX_NUM_FIFO * RX_BUF_SIZE]; +static char tx_buf_pool[NR_PORTS * TX_NUM_FIFO * TX_BUF_SIZE]; + +static void change_speed(ser_info_t *info); +static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout); + +static inline int serial_paranoia_check(ser_info_t *info, + char *name, const char *routine) +{ +#ifdef SERIAL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for serial struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null async_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, name, routine); + return 1; + } + if (info->magic != SERIAL_MAGIC) { + printk(badmagic, name, routine); + return 1; + } +#endif + return 0; +} + +/* + * This is used to figure out the divisor speeds and the timeouts, + * indexed by the termio value. The generic CPM functions are responsible + * for setting and assigning baud rate generators for us. + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; + +/* This sucks. There is a better way: */ +#if defined(CONFIG_CONSOLE_9600) + #define CONSOLE_BAUDRATE 9600 +#elif defined(CONFIG_CONSOLE_19200) + #define CONSOLE_BAUDRATE 19200 +#elif defined(CONFIG_CONSOLE_115200) + #define CONSOLE_BAUDRATE 115200 +#else + #warning "console baud rate undefined" + #define CONSOLE_BAUDRATE 9600 +#endif + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ +static void rs_360_stop(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int idx; + unsigned long flags; + volatile struct scc_regs *sccp; + volatile struct smc_regs *smcp; + + if (serial_paranoia_check(info, tty->name, "rs_stop")) + return; + + local_irq_save(flags); + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + sccp->scc_sccm &= ~UART_SCCM_TX; + } else { + /* smcp = &cpmp->cp_smc[idx]; */ + smcp = &pquicc->smc_regs[idx]; + smcp->smc_smcm &= ~SMCM_TX; + } + local_irq_restore(flags); +} + + +static void rs_360_start(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int idx; + unsigned long flags; + volatile struct scc_regs *sccp; + volatile struct smc_regs *smcp; + + if (serial_paranoia_check(info, tty->name, "rs_stop")) + return; + + local_irq_save(flags); + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + sccp->scc_sccm |= UART_SCCM_TX; + } else { + smcp = &pquicc->smc_regs[idx]; + smcp->smc_smcm |= SMCM_TX; + } + local_irq_restore(flags); +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +static _INLINE_ void receive_chars(ser_info_t *info) +{ + struct tty_struct *tty = info->port.tty; + unsigned char ch, flag, *cp; + /*int ignored = 0;*/ + int i; + ushort status; + struct async_icount *icount; + /* struct async_icount_24 *icount; */ + volatile QUICC_BD *bdp; + + icount = &info->state->icount; + + /* Just loop through the closed BDs and copy the characters into + * the buffer. + */ + bdp = info->rx_cur; + for (;;) { + if (bdp->status & BD_SC_EMPTY) /* If this one is empty */ + break; /* we are all done */ + + /* The read status mask tell us what we should do with + * incoming characters, especially if errors occur. + * One special case is the use of BD_SC_EMPTY. If + * this is not set, we are supposed to be ignoring + * inputs. In this case, just mark the buffer empty and + * continue. + */ + if (!(info->read_status_mask & BD_SC_EMPTY)) { + bdp->status |= BD_SC_EMPTY; + bdp->status &= + ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); + + if (bdp->status & BD_SC_WRAP) + bdp = info->rx_bd_base; + else + bdp++; + continue; + } + + /* Get the number of characters and the buffer pointer. + */ + i = bdp->length; + /* cp = (unsigned char *)__va(bdp->buf); */ + cp = (char *)bdp->buf; + status = bdp->status; + + while (i-- > 0) { + ch = *cp++; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, status); +#endif + flag = TTY_NORMAL; + + if (status & (BD_SC_BR | BD_SC_FR | + BD_SC_PR | BD_SC_OV)) { + /* + * For statistics only + */ + if (status & BD_SC_BR) + icount->brk++; + else if (status & BD_SC_PR) + icount->parity++; + else if (status & BD_SC_FR) + icount->frame++; + if (status & BD_SC_OV) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + if (status & info->ignore_status_mask) { + if (++ignored > 100) + break; + continue; + } + */ + status &= info->read_status_mask; + + if (status & (BD_SC_BR)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & BD_SC_PR) + flag = TTY_PARITY; + else if (status & BD_SC_FR) + flag = TTY_FRAME; + } + tty_insert_flip_char(tty, ch, flag); + if (status & BD_SC_OV) + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + /* This BD is ready to be used again. Clear status. + * Get next BD. + */ + bdp->status |= BD_SC_EMPTY; + bdp->status &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV); + + if (bdp->status & BD_SC_WRAP) + bdp = info->rx_bd_base; + else + bdp++; + } + + info->rx_cur = (QUICC_BD *)bdp; + + tty_schedule_flip(tty); +} + +static _INLINE_ void receive_break(ser_info_t *info) +{ + struct tty_struct *tty = info->port.tty; + + info->state->icount.brk++; + /* Check to see if there is room in the tty buffer for + * the break. If not, we exit now, losing the break. FIXME + */ + tty_insert_flip_char(tty, 0, TTY_BREAK); + tty_schedule_flip(tty); +} + +static _INLINE_ void transmit_chars(ser_info_t *info) +{ + + if ((info->flags & TX_WAKEUP) || + (info->port.tty->flags & (1 << TTY_DO_WRITE_WAKEUP))) { + schedule_work(&info->tqueue); + } + +#ifdef SERIAL_DEBUG_INTR + printk("THRE..."); +#endif +} + +#ifdef notdef + /* I need to do this for the SCCs, so it is left as a reminder. + */ +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + /* struct async_icount *icount; */ + struct async_icount_24 *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + /* update input line counters */ + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + icount->cts++; + wake_up_interruptible(&info->delta_msr_wait); + } + + if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { +#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) + printk("ttys%d CD now %s...", info->line, + (status & UART_MSR_DCD) ? "on" : "off"); +#endif + if (status & UART_MSR_DCD) + wake_up_interruptible(&info->open_wait); + else { +#ifdef SERIAL_DEBUG_OPEN + printk("scheduling hangup..."); +#endif + queue_task(&info->tqueue_hangup, + &tq_scheduler); + } + } + if (info->flags & ASYNC_CTS_FLOW) { + if (info->port.tty->hw_stopped) { + if (status & UART_MSR_CTS) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx start..."); +#endif + info->port.tty->hw_stopped = 0; + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & UART_MSR_CTS)) { +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) + printk("CTS tx stop..."); +#endif + info->port.tty->hw_stopped = 1; + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + } + } +} +#endif + +/* + * This is the serial driver's interrupt routine for a single port + */ +/* static void rs_360_interrupt(void *dev_id) */ /* until and if we start servicing irqs here */ +static void rs_360_interrupt(int vec, void *dev_id) +{ + u_char events; + int idx; + ser_info_t *info; + volatile struct smc_regs *smcp; + volatile struct scc_regs *sccp; + + info = dev_id; + + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + events = sccp->scc_scce; + if (events & SCCM_RX) + receive_chars(info); + if (events & SCCM_TX) + transmit_chars(info); + sccp->scc_scce = events; + } else { + smcp = &pquicc->smc_regs[idx]; + events = smcp->smc_smce; + if (events & SMCM_BRKE) + receive_break(info); + if (events & SMCM_RX) + receive_chars(info); + if (events & SMCM_TX) + transmit_chars(info); + smcp->smc_smce = events; + } + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d, %x)...", + info->state->smc_scc_num, events); +#endif +#ifdef modem_control + check_modem_status(info); +#endif + info->last_active = jiffies; +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + + +static void do_softint(void *private_) +{ + ser_info_t *info = (ser_info_t *) private_; + struct tty_struct *tty; + + tty = info->port.tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) + tty_wakeup(tty); +} + + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_serial_hangup() -> tty->hangup() -> rs_hangup() + * + */ +static void do_serial_hangup(void *private_) +{ + struct async_struct *info = (struct async_struct *) private_; + struct tty_struct *tty; + + tty = info->port.tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static int startup(ser_info_t *info) +{ + unsigned long flags; + int retval=0; + int idx; + /*struct serial_state *state = info->state;*/ + volatile struct smc_regs *smcp; + volatile struct scc_regs *sccp; + volatile struct smc_uart_pram *up; + volatile struct uart_pram *scup; + + + local_irq_save(flags); + + if (info->flags & ASYNC_INITIALIZED) { + goto errout; + } + +#ifdef maybe + if (!state->port || !state->type) { + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + goto errout; + } +#endif + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttys%d (irq %d)...", info->line, state->irq); +#endif + + +#ifdef modem_control + info->MCR = 0; + if (info->port.tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#endif + + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + + /* + * and set the speed of the serial port + */ + change_speed(info); + + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + scup = &pquicc->pram[info->state->port].scc.pscc.u; + + scup->mrblr = RX_BUF_SIZE; + scup->max_idl = RX_BUF_SIZE; + + sccp->scc_sccm |= (UART_SCCM_TX | UART_SCCM_RX); + sccp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + } else { + smcp = &pquicc->smc_regs[idx]; + + /* Enable interrupts and I/O. + */ + smcp->smc_smcm |= (SMCM_RX | SMCM_TX); + smcp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN); + + /* We can tune the buffer length and idle characters + * to take advantage of the entire incoming buffer size. + * If mrblr is something other than 1, maxidl has to be + * non-zero or we never get an interrupt. The maxidl + * is the number of character times we wait after reception + * of the last character before we decide no more characters + * are coming. + */ + /* up = (smc_uart_t *)&pquicc->cp_dparam[state->port]; */ + /* holy unionized structures, Batman: */ + up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; + + up->mrblr = RX_BUF_SIZE; + up->max_idl = RX_BUF_SIZE; + + up->brkcr = 1; /* number of break chars */ + } + + info->flags |= ASYNC_INITIALIZED; + local_irq_restore(flags); + return 0; + +errout: + local_irq_restore(flags); + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void shutdown(ser_info_t *info) +{ + unsigned long flags; + struct serial_state *state; + int idx; + volatile struct smc_regs *smcp; + volatile struct scc_regs *sccp; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + local_irq_save(flags); + + idx = PORT_NUM(state->smc_scc_num); + if (state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + sccp->scc_gsmr.w.low &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#ifdef CONFIG_SERIAL_CONSOLE + /* We can't disable the transmitter if this is the + * system console. + */ + if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT) +#endif + sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX); + } else { + smcp = &pquicc->smc_regs[idx]; + + /* Disable interrupts and I/O. + */ + smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); +#ifdef CONFIG_SERIAL_CONSOLE + /* We can't disable the transmitter if this is the + * system console. + */ + if ((state - rs_table) != CONFIG_SERIAL_CONSOLE_PORT) +#endif + smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + } + + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + local_irq_restore(flags); +} + +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static void change_speed(ser_info_t *info) +{ + int baud_rate; + unsigned cflag, cval, scval, prev_mode; + int i, bits, sbits, idx; + unsigned long flags; + struct serial_state *state; + volatile struct smc_regs *smcp; + volatile struct scc_regs *sccp; + + if (!info->port.tty || !info->port.tty->termios) + return; + cflag = info->port.tty->termios->c_cflag; + + state = info->state; + + /* Character length programmed into the mode register is the + * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, + * 1 or 2 stop bits, minus 1. + * The value 'bits' counts this for us. + */ + cval = 0; + scval = 0; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: bits = 5; break; + case CS6: bits = 6; break; + case CS7: bits = 7; break; + case CS8: bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: bits = 8; break; + } + sbits = bits - 5; + + if (cflag & CSTOPB) { + cval |= SMCMR_SL; /* Two stops */ + scval |= SCU_PMSR_SL; + bits++; + } + if (cflag & PARENB) { + cval |= SMCMR_PEN; + scval |= SCU_PMSR_PEN; + bits++; + } + if (!(cflag & PARODD)) { + cval |= SMCMR_PM_EVEN; + scval |= (SCU_PMSR_REVP | SCU_PMSR_TEVP); + } + + /* Determine divisor based on baud rate */ + i = cflag & CBAUD; + if (i >= (sizeof(baud_table)/sizeof(int))) + baud_rate = 9600; + else + baud_rate = baud_table[i]; + + info->timeout = (TX_BUF_SIZE*HZ*bits); + info->timeout += HZ/50; /* Add .02 seconds of slop */ + +#ifdef modem_control + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + } else + info->flags &= ~ASYNC_CTS_FLOW; + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + serial_out(info, UART_IER, info->IER); +#endif + + /* + * Set up parity check flag + */ + info->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); + if (I_INPCK(info->port.tty)) + info->read_status_mask |= BD_SC_FR | BD_SC_PR; + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + info->read_status_mask |= BD_SC_BR; + + /* + * Characters to ignore + */ + info->ignore_status_mask = 0; + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask |= BD_SC_PR | BD_SC_FR; + if (I_IGNBRK(info->port.tty)) { + info->ignore_status_mask |= BD_SC_BR; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->port.tty)) + info->ignore_status_mask |= BD_SC_OV; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + info->read_status_mask &= ~BD_SC_EMPTY; + local_irq_save(flags); + + /* Start bit has not been added (so don't, because we would just + * subtract it later), and we need to add one for the number of + * stops bits (there is always at least one). + */ + bits++; + idx = PORT_NUM(state->smc_scc_num); + if (state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + sccp->scc_psmr = (sbits << 12) | scval; + } else { + smcp = &pquicc->smc_regs[idx]; + + /* Set the mode register. We want to keep a copy of the + * enables, because we want to put them back if they were + * present. + */ + prev_mode = smcp->smc_smcmr; + smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART; + smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN)); + } + + m360_cpm_setbrg((state - rs_table), baud_rate); + + local_irq_restore(flags); +} + +static void rs_360_put_char(struct tty_struct *tty, unsigned char ch) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + volatile QUICC_BD *bdp; + + if (serial_paranoia_check(info, tty->name, "rs_put_char")) + return 0; + + if (!tty) + return 0; + + bdp = info->tx_cur; + while (bdp->status & BD_SC_READY); + + /* *((char *)__va(bdp->buf)) = ch; */ + *((char *)bdp->buf) = ch; + bdp->length = 1; + bdp->status |= BD_SC_READY; + + /* Get next BD. + */ + if (bdp->status & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + + info->tx_cur = (QUICC_BD *)bdp; + return 1; + +} + +static int rs_360_write(struct tty_struct * tty, + const unsigned char *buf, int count) +{ + int c, ret = 0; + ser_info_t *info = (ser_info_t *)tty->driver_data; + volatile QUICC_BD *bdp; + +#ifdef CONFIG_KGDB + /* Try to let stub handle output. Returns true if it did. */ + if (kgdb_output_string(buf, count)) + return ret; +#endif + + if (serial_paranoia_check(info, tty->name, "rs_write")) + return 0; + + if (!tty) + return 0; + + bdp = info->tx_cur; + + while (1) { + c = min(count, TX_BUF_SIZE); + + if (c <= 0) + break; + + if (bdp->status & BD_SC_READY) { + info->flags |= TX_WAKEUP; + break; + } + + /* memcpy(__va(bdp->buf), buf, c); */ + memcpy((void *)bdp->buf, buf, c); + + bdp->length = c; + bdp->status |= BD_SC_READY; + + buf += c; + count -= c; + ret += c; + + /* Get next BD. + */ + if (bdp->status & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + info->tx_cur = (QUICC_BD *)bdp; + } + return ret; +} + +static int rs_360_write_room(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + int ret; + + if (serial_paranoia_check(info, tty->name, "rs_write_room")) + return 0; + + if ((info->tx_cur->status & BD_SC_READY) == 0) { + info->flags &= ~TX_WAKEUP; + ret = TX_BUF_SIZE; + } + else { + info->flags |= TX_WAKEUP; + ret = 0; + } + return ret; +} + +/* I could track this with transmit counters....maybe later. +*/ +static int rs_360_chars_in_buffer(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) + return 0; + return 0; +} + +static void rs_360_flush_buffer(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) + return; + + /* There is nothing to "flush", whatever we gave the CPM + * is on its way out. + */ + tty_wakeup(tty); + info->flags &= ~TX_WAKEUP; +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_360_send_xchar(struct tty_struct *tty, char ch) +{ + volatile QUICC_BD *bdp; + + ser_info_t *info = (ser_info_t *)tty->driver_data; + + if (serial_paranoia_check(info, tty->name, "rs_send_char")) + return; + + bdp = info->tx_cur; + while (bdp->status & BD_SC_READY); + + /* *((char *)__va(bdp->buf)) = ch; */ + *((char *)bdp->buf) = ch; + bdp->length = 1; + bdp->status |= BD_SC_READY; + + /* Get next BD. + */ + if (bdp->status & BD_SC_WRAP) + bdp = info->tx_bd_base; + else + bdp++; + + info->tx_cur = (QUICC_BD *)bdp; +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void rs_360_throttle(struct tty_struct * tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->name, "rs_throttle")) + return; + + if (I_IXOFF(tty)) + rs_360_send_xchar(tty, STOP_CHAR(tty)); + +#ifdef modem_control + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + local_irq_disable(); + serial_out(info, UART_MCR, info->MCR); + local_irq_enable(); +#endif +} + +static void rs_360_unthrottle(struct tty_struct * tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_360_send_xchar(tty, START_CHAR(tty)); + } +#ifdef modem_control + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + local_irq_disable(); + serial_out(info, UART_MCR, info->MCR); + local_irq_enable(); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +#ifdef maybe +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int get_lsr_info(struct async_struct * info, unsigned int *value) +{ + unsigned char status; + unsigned int result; + + local_irq_disable(); + status = serial_in(info, UART_LSR); + local_irq_enable(); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + return put_user(result,value); +} +#endif + +static int rs_360_tiocmget(struct tty_struct *tty, struct file *file) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + unsigned int result = 0; +#ifdef modem_control + unsigned char control, status; + + if (serial_paranoia_check(info, tty->name, __func__)) + return -ENODEV; + + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + + control = info->MCR; + local_irq_disable(); + status = serial_in(info, UART_MSR); + local_irq_enable(); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) + | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) + | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) + | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); +#endif + return result; +} + +static int rs_360_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ +#ifdef modem_control + ser_info_t *info = (ser_info_t *)tty->driver_data; + unsigned int arg; + + if (serial_paranoia_check(info, tty->name, __func__)) + return -ENODEV; + + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + /* FIXME: locking on info->mcr */ + if (set & TIOCM_RTS) + info->mcr |= UART_MCR_RTS; + if (set & TIOCM_DTR) + info->mcr |= UART_MCR_DTR; + if (clear & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (clear & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; + +#ifdef TIOCM_OUT1 + if (set & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (set & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; + if (clear & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (clear & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + + local_irq_disable(); + serial_out(info, UART_MCR, info->MCR); + local_irq_enable(); +#endif + return 0; +} + +/* Sending a break is a two step process on the SMC/SCC. It is accomplished + * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT + * command. We take advantage of the begin/end functions to make this + * happen. + */ +static ushort smc_chan_map[] = { + CPM_CR_CH_SMC1, + CPM_CR_CH_SMC2 +}; + +static ushort scc_chan_map[] = { + CPM_CR_CH_SCC1, + CPM_CR_CH_SCC2, + CPM_CR_CH_SCC3, + CPM_CR_CH_SCC4 +}; + +static void begin_break(ser_info_t *info) +{ + volatile QUICC *cp; + ushort chan; + int idx; + + cp = pquicc; + + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) + chan = scc_chan_map[idx]; + else + chan = smc_chan_map[idx]; + + cp->cp_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; + while (cp->cp_cr & CPM_CR_FLG); +} + +static void end_break(ser_info_t *info) +{ + volatile QUICC *cp; + ushort chan; + int idx; + + cp = pquicc; + + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) + chan = scc_chan_map[idx]; + else + chan = smc_chan_map[idx]; + + cp->cp_cr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; + while (cp->cp_cr & CPM_CR_FLG); +} + +/* + * This routine sends a break character out the serial port. + */ +static void send_break(ser_info_t *info, unsigned int duration) +{ +#ifdef SERIAL_DEBUG_SEND_BREAK + printk("rs_send_break(%d) jiff=%lu...", duration, jiffies); +#endif + begin_break(info); + msleep_interruptible(duration); + end_break(info); +#ifdef SERIAL_DEBUG_SEND_BREAK + printk("done jiffies=%lu\n", jiffies); +#endif +} + + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int rs_360_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct async_icount cnow; + + local_irq_disable(); + cnow = info->state->icount; + local_irq_enable(); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + + return 0; +} + +static int rs_360_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + ser_info_t *info = (ser_info_t *)tty->driver_data; + int retval; + struct async_icount cnow; + /* struct async_icount_24 cnow;*/ /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + + if (serial_paranoia_check(info, tty->name, "rs_ioctl")) + return -ENODEV; + + if (cmd != TIOCMIWAIT) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, 250); /* 1/4 second */ + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*100 : 250); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCSBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + begin_break(info); + return 0; + case TIOCCBRK: + retval = tty_check_change(tty); + if (retval) + return retval; + end_break(info); + return 0; +#ifdef maybe + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); +#endif + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: +#ifdef modem_control + local_irq_disable(); + /* note the counters on entry */ + cprev = info->state->icount; + local_irq_enable(); + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + local_irq_disable(); + cnow = info->state->icount; /* atomic copy */ + local_irq_enable(); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + 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)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ +#else + return 0; +#endif + + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* FIX UP modem control here someday...... +*/ +static void rs_360_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + + change_speed(info); + +#ifdef modem_control + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && + !(tty->termios->c_cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + local_irq_disable(); + serial_out(info, UART_MCR, info->MCR); + local_irq_enable(); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + (tty->termios->c_cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!tty->hw_stopped || + !(tty->termios->c_cflag & CRTSCTS)) { + info->MCR |= UART_MCR_RTS; + } + local_irq_disable(); + serial_out(info, UART_MCR, info->MCR); + local_irq_enable(); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_360_start(tty); + } +#endif + +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); +#endif +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void rs_360_close(struct tty_struct *tty, struct file * filp) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + /* struct async_state *state; */ + struct serial_state *state; + unsigned long flags; + int idx; + volatile struct smc_regs *smcp; + volatile struct scc_regs *sccp; + + if (!info || serial_paranoia_check(info, tty->name, "rs_close")) + return; + + state = info->state; + + local_irq_save(flags); + + if (tty_hung_up_p(filp)) { + DBG_CNT("before DEC-hung"); + local_irq_restore(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close ttys%d, count = %d\n", info->line, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. state->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + local_irq_restore(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + info->read_status_mask &= ~BD_SC_EMPTY; + if (info->flags & ASYNC_INITIALIZED) { + + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) { + sccp = &pquicc->scc_regs[idx]; + sccp->scc_sccm &= ~UART_SCCM_RX; + sccp->scc_gsmr.w.low &= ~SCC_GSMRL_ENR; + } else { + smcp = &pquicc->smc_regs[idx]; + smcp->smc_smcm &= ~SMCM_RX; + smcp->smc_smcmr &= ~SMCMR_REN; + } + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + rs_360_wait_until_sent(tty, info->timeout); + } + shutdown(info); + rs_360_flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; + info->event = 0; + info->port.tty = NULL; + if (info->blocked_open) { + if (info->close_delay) { + msleep_interruptible(jiffies_to_msecs(info->close_delay)); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + local_irq_restore(flags); +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + unsigned long orig_jiffies, char_time; + /*int lsr;*/ + volatile QUICC_BD *bdp; + + if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) + return; + +#ifdef maybe + if (info->state->type == PORT_UNKNOWN) + return; +#endif + + orig_jiffies = jiffies; + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + char_time = 1; + if (timeout) + char_time = min(char_time, (unsigned long)timeout); +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + + /* We go through the loop at least once because we can't tell + * exactly when the last character exits the shifter. There can + * be at least two characters waiting to be sent after the buffers + * are empty. + */ + do { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif +/* current->counter = 0; make us low-priority */ + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (timeout && (time_after(jiffies, orig_jiffies + timeout))) + break; + /* The 'tx_cur' is really the next buffer to send. We + * have to back up to the previous BD and wait for it + * to go. This isn't perfect, because all this indicates + * is the buffer is available. There are still characters + * in the CPM FIFO. + */ + bdp = info->tx_cur; + if (bdp == info->tx_bd_base) + bdp += (TX_NUM_FIFO-1); + else + bdp--; + } while (bdp->status & BD_SC_READY); + current->state = TASK_RUNNING; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_360_hangup(struct tty_struct *tty) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->name, "rs_hangup")) + return; + + state = info->state; + + rs_360_flush_buffer(tty); + shutdown(info); + info->event = 0; + state->count = 0; + info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + ser_info_t *info) +{ +#ifdef DO_THIS_LATER + DECLARE_WAITQUEUE(wait, current); +#endif + struct serial_state *state = info->state; + int retval; + int do_clocal = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + * If this is an SMC port, we don't have modem control to wait + * for, so just get out here. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR)) || + !(info->state->smc_scc_num & NUM_IS_SCC)) { + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, state->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; +#ifdef DO_THIS_LATER + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttys%d, count = %d\n", + state->line, state->count); +#endif + local_irq_disable(); + if (!tty_hung_up_p(filp)) + state->count--; + local_irq_enable(); + info->blocked_open++; + while (1) { + local_irq_disable(); + if (tty->termios->c_cflag & CBAUD) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); + local_irq_enable(); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CLOSING) && + (do_clocal || (serial_in(info, UART_MSR) & + UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + tty_unlock(); + schedule(); + tty_lock(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + if (!tty_hung_up_p(filp)) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif +#endif /* DO_THIS_LATER */ + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, ser_info_t **ret_info) +{ + struct serial_state *sstate; + + sstate = rs_table + line; + if (sstate->info) { + sstate->count++; + *ret_info = (ser_info_t *)sstate->info; + return 0; + } + else { + return -ENOMEM; + } +} + +/* + * This routine is called whenever a serial port is opened. It + * enables interrupts for a serial port, linking in its async structure into + * the IRQ chain. It also performs the serial-specific + * initialization for the tty structure. + */ +static int rs_360_open(struct tty_struct *tty, struct file * filp) +{ + ser_info_t *info; + int retval, line; + + line = tty->index; + if ((line < 0) || (line >= NR_PORTS)) + return -ENODEV; + retval = get_async_struct(line, &info); + if (retval) + return retval; + if (serial_paranoia_check(info, tty->name, "rs_open")) + return -ENODEV; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s, count = %d\n", tty->name, info->state->count); +#endif + tty->driver_data = info; + info->port.tty = tty; + + /* + * Start up serial port + */ + retval = startup(info); + if (retval) + return retval; + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s successful...", tty->name); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static inline int line_info(char *buf, struct serial_state *state) +{ +#ifdef notdef + struct async_struct *info = state->info, scr_info; + char stat_buf[30], control, status; +#endif + int ret; + + ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", + state->line, + (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC", + (unsigned int)(state->port), state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + +#ifdef notdef + /* + * Figure out the current RS-232 lines + */ + if (!info) { + info = &scr_info; /* This is just for serial_{in,out} */ + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->quot = 0; + info->port.tty = NULL; + } + local_irq_disable(); + status = serial_in(info, UART_MSR); + control = info ? info->MCR : serial_in(info, UART_MCR); + local_irq_enable(); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); + + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); +#endif + return ret; +} + +int rs_360_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + len += line_info(page + len, &rs_table[i]); + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * rs_init() and friends + * + * rs_init() is called at boot-time to initialize the serial driver. + * --------------------------------------------------------------------- + */ + +/* + * This routine prints out the appropriate serial driver version + * number, and identifies which options were configured into this + * driver. + */ +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s\n", serial_name, serial_version); +} + + +/* + * The serial console driver used during boot. Note that these names + * clash with those found in "serial.c", so we currently can't support + * the 16xxx uarts and these at the same time. I will fix this to become + * an indirect function call from tty_io.c (or something). + */ + +#ifdef CONFIG_SERIAL_CONSOLE + +/* + * Print a string to the serial port trying not to disturb any possible + * real use of the port... + */ +static void my_console_write(int idx, const char *s, + unsigned count) +{ + struct serial_state *ser; + ser_info_t *info; + unsigned i; + QUICC_BD *bdp, *bdbase; + volatile struct smc_uart_pram *up; + volatile u_char *cp; + + ser = rs_table + idx; + + + /* If the port has been initialized for general use, we have + * to use the buffer descriptors allocated there. Otherwise, + * we simply use the single buffer allocated. + */ + if ((info = (ser_info_t *)ser->info) != NULL) { + bdp = info->tx_cur; + bdbase = info->tx_bd_base; + } + else { + /* Pointer to UART in parameter ram. + */ + /* up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; */ + up = &pquicc->pram[ser->port].scc.pothers.idma_smc.psmc.u; + + /* Get the address of the host memory buffer. + */ + bdp = bdbase = (QUICC_BD *)((uint)pquicc + (uint)up->tbase); + } + + /* + * We need to gracefully shut down the transmitter, disable + * interrupts, then send our bytes out. + */ + + /* + * Now, do each character. This is not as bad as it looks + * since this is a holding FIFO and not a transmitting FIFO. + * We could add the complexity of filling the entire transmit + * buffer, but we would just wait longer between accesses...... + */ + for (i = 0; i < count; i++, s++) { + /* Wait for transmitter fifo to empty. + * Ready indicates output is ready, and xmt is doing + * that, not that it is ready for us to send. + */ + while (bdp->status & BD_SC_READY); + + /* Send the character out. + */ + cp = bdp->buf; + *cp = *s; + + bdp->length = 1; + bdp->status |= BD_SC_READY; + + if (bdp->status & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + + /* if a LF, also do CR... */ + if (*s == 10) { + while (bdp->status & BD_SC_READY); + /* cp = __va(bdp->buf); */ + cp = bdp->buf; + *cp = 13; + bdp->length = 1; + bdp->status |= BD_SC_READY; + + if (bdp->status & BD_SC_WRAP) { + bdp = bdbase; + } + else { + bdp++; + } + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + while (bdp->status & BD_SC_READY); + + if (info) + info->tx_cur = (QUICC_BD *)bdp; +} + +static void serial_console_write(struct console *c, const char *s, + unsigned count) +{ +#ifdef CONFIG_KGDB + /* Try to let stub handle output. Returns true if it did. */ + if (kgdb_output_string(s, count)) + return; +#endif + my_console_write(c->index, s, count); +} + + + +/*void console_print_68360(const char *p) +{ + const char *cp = p; + int i; + + for (i=0;cp[i]!=0;i++); + + serial_console_write (p, i); + + //Comment this if you want to have a strict interrupt-driven output + //rs_fair_output(); + + return; +}*/ + + + + + + +#ifdef CONFIG_XMON +int +xmon_360_write(const char *s, unsigned count) +{ + my_console_write(0, s, count); + return(count); +} +#endif + +#ifdef CONFIG_KGDB +void +putDebugChar(char ch) +{ + my_console_write(0, &ch, 1); +} +#endif + +/* + * Receive character from the serial port. This only works well + * before the port is initialized for real use. + */ +static int my_console_wait_key(int idx, int xmon, char *obuf) +{ + struct serial_state *ser; + u_char c, *cp; + ser_info_t *info; + QUICC_BD *bdp; + volatile struct smc_uart_pram *up; + int i; + + ser = rs_table + idx; + + /* Get the address of the host memory buffer. + * If the port has been initialized for general use, we must + * use information from the port structure. + */ + if ((info = (ser_info_t *)ser->info)) + bdp = info->rx_cur; + else + /* bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_rbase]; */ + bdp = (QUICC_BD *)((uint)pquicc + (uint)up->tbase); + + /* Pointer to UART in parameter ram. + */ + /* up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; */ + up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; + + /* + * We need to gracefully shut down the receiver, disable + * interrupts, then read the input. + * XMON just wants a poll. If no character, return -1, else + * return the character. + */ + if (!xmon) { + while (bdp->status & BD_SC_EMPTY); + } + else { + if (bdp->status & BD_SC_EMPTY) + return -1; + } + + cp = (char *)bdp->buf; + + if (obuf) { + i = c = bdp->length; + while (i-- > 0) + *obuf++ = *cp++; + } + else { + c = *cp; + } + bdp->status |= BD_SC_EMPTY; + + if (info) { + if (bdp->status & BD_SC_WRAP) { + bdp = info->rx_bd_base; + } + else { + bdp++; + } + info->rx_cur = (QUICC_BD *)bdp; + } + + return((int)c); +} + +static int serial_console_wait_key(struct console *co) +{ + return(my_console_wait_key(co->index, 0, NULL)); +} + +#ifdef CONFIG_XMON +int +xmon_360_read_poll(void) +{ + return(my_console_wait_key(0, 1, NULL)); +} + +int +xmon_360_read_char(void) +{ + return(my_console_wait_key(0, 0, NULL)); +} +#endif + +#ifdef CONFIG_KGDB +static char kgdb_buf[RX_BUF_SIZE], *kgdp; +static int kgdb_chars; + +unsigned char +getDebugChar(void) +{ + if (kgdb_chars <= 0) { + kgdb_chars = my_console_wait_key(0, 0, kgdb_buf); + kgdp = kgdb_buf; + } + kgdb_chars--; + + return(*kgdp++); +} + +void kgdb_interruptible(int state) +{ +} +void kgdb_map_scc(void) +{ + struct serial_state *ser; + uint mem_addr; + volatile QUICC_BD *bdp; + volatile smc_uart_t *up; + + cpmp = (cpm360_t *)&(((immap_t *)IMAP_ADDR)->im_cpm); + + /* To avoid data cache CPM DMA coherency problems, allocate a + * buffer in the CPM DPRAM. This will work until the CPM and + * serial ports are initialized. At that time a memory buffer + * will be allocated. + * The port is already initialized from the boot procedure, all + * we do here is give it a different buffer and make it a FIFO. + */ + + ser = rs_table; + + /* Right now, assume we are using SMCs. + */ + up = (smc_uart_t *)&cpmp->cp_dparam[ser->port]; + + /* Allocate space for an input FIFO, plus a few bytes for output. + * Allocate bytes to maintain word alignment. + */ + mem_addr = (uint)(&cpmp->cp_dpmem[0x1000]); + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_rbase]; + bdp->buf = mem_addr; + + bdp = (QUICC_BD *)&cpmp->cp_dpmem[up->smc_tbase]; + bdp->buf = mem_addr+RX_BUF_SIZE; + + up->smc_mrblr = RX_BUF_SIZE; /* receive buffer length */ + up->smc_maxidl = RX_BUF_SIZE; +} +#endif + +static struct tty_struct *serial_console_device(struct console *c, int *index) +{ + *index = c->index; + return serial_driver; +} + + +struct console sercons = { + .name = "ttyS", + .write = serial_console_write, + .device = serial_console_device, + .wait_key = serial_console_wait_key, + .setup = serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = CONFIG_SERIAL_CONSOLE_PORT, +}; + + + +/* + * Register console. + */ +long console_360_init(long kmem_start, long kmem_end) +{ + register_console(&sercons); + /*register_console (console_print_68360); - 2.0.38 only required a write + function pointer. */ + return kmem_start; +} + +#endif + +/* Index in baud rate table of the default console baud rate. +*/ +static int baud_idx; + +static const struct tty_operations rs_360_ops = { + .owner = THIS_MODULE, + .open = rs_360_open, + .close = rs_360_close, + .write = rs_360_write, + .put_char = rs_360_put_char, + .write_room = rs_360_write_room, + .chars_in_buffer = rs_360_chars_in_buffer, + .flush_buffer = rs_360_flush_buffer, + .ioctl = rs_360_ioctl, + .throttle = rs_360_throttle, + .unthrottle = rs_360_unthrottle, + /* .send_xchar = rs_360_send_xchar, */ + .set_termios = rs_360_set_termios, + .stop = rs_360_stop, + .start = rs_360_start, + .hangup = rs_360_hangup, + /* .wait_until_sent = rs_360_wait_until_sent, */ + /* .read_proc = rs_360_read_proc, */ + .tiocmget = rs_360_tiocmget, + .tiocmset = rs_360_tiocmset, +}; + +static int __init rs_360_init(void) +{ + struct serial_state * state; + ser_info_t *info; + void *mem_addr; + uint dp_addr, iobits; + int i, j, idx; + ushort chan; + QUICC_BD *bdp; + volatile QUICC *cp; + volatile struct smc_regs *sp; + volatile struct smc_uart_pram *up; + volatile struct scc_regs *scp; + volatile struct uart_pram *sup; + /* volatile immap_t *immap; */ + + serial_driver = alloc_tty_driver(NR_PORTS); + if (!serial_driver) + return -1; + + show_serial_version(); + + serial_driver->name = "ttyS"; + serial_driver->major = TTY_MAJOR; + serial_driver->minor_start = 64; + serial_driver->type = TTY_DRIVER_TYPE_SERIAL; + serial_driver->subtype = SERIAL_TYPE_NORMAL; + serial_driver->init_termios = tty_std_termios; + serial_driver->init_termios.c_cflag = + baud_idx | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(serial_driver, &rs_360_ops); + + if (tty_register_driver(serial_driver)) + panic("Couldn't register serial driver\n"); + + cp = pquicc; /* Get pointer to Communication Processor */ + /* immap = (immap_t *)IMAP_ADDR; */ /* and to internal registers */ + + + /* Configure SCC2, SCC3, and SCC4 instead of port A parallel I/O. + */ + /* The "standard" configuration through the 860. + */ +/* immap->im_ioport.iop_papar |= 0x00fc; */ +/* immap->im_ioport.iop_padir &= ~0x00fc; */ +/* immap->im_ioport.iop_paodr &= ~0x00fc; */ + cp->pio_papar |= 0x00fc; + cp->pio_padir &= ~0x00fc; + /* cp->pio_paodr &= ~0x00fc; */ + + + /* Since we don't yet do modem control, connect the port C pins + * as general purpose I/O. This will assert CTS and CD for the + * SCC ports. + */ + /* FIXME: see 360um p.7-365 and 860um p.34-12 + * I can't make sense of these bits - mleslie*/ +/* immap->im_ioport.iop_pcdir |= 0x03c6; */ +/* immap->im_ioport.iop_pcpar &= ~0x03c6; */ + +/* cp->pio_pcdir |= 0x03c6; */ +/* cp->pio_pcpar &= ~0x03c6; */ + + + + /* Connect SCC2 and SCC3 to NMSI. Connect BRG3 to SCC2 and + * BRG4 to SCC3. + */ + cp->si_sicr &= ~0x00ffff00; + cp->si_sicr |= 0x001b1200; + +#ifdef CONFIG_PP04 + /* Frequentis PP04 forced to RS-232 until we know better. + * Port C 12 and 13 low enables RS-232 on SCC3 and SCC4. + */ + immap->im_ioport.iop_pcdir |= 0x000c; + immap->im_ioport.iop_pcpar &= ~0x000c; + immap->im_ioport.iop_pcdat &= ~0x000c; + + /* This enables the TX driver. + */ + cp->cp_pbpar &= ~0x6000; + cp->cp_pbdat &= ~0x6000; +#endif + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + printk(KERN_INFO "ttyS%d at irq 0x%02x is an %s\n", + i, (unsigned int)(state->irq), + (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC"); + +#ifdef CONFIG_SERIAL_CONSOLE + /* If we just printed the message on the console port, and + * we are about to initialize it for general use, we have + * to wait a couple of character times for the CR/NL to + * make it out of the transmit buffer. + */ + if (i == CONFIG_SERIAL_CONSOLE_PORT) + mdelay(8); + + +/* idx = PORT_NUM(info->state->smc_scc_num); */ +/* if (info->state->smc_scc_num & NUM_IS_SCC) */ +/* chan = scc_chan_map[idx]; */ +/* else */ +/* chan = smc_chan_map[idx]; */ + +/* cp->cp_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; */ +/* while (cp->cp_cr & CPM_CR_FLG); */ + +#endif + /* info = kmalloc(sizeof(ser_info_t), GFP_KERNEL); */ + info = &quicc_ser_info[i]; + if (info) { + memset (info, 0, sizeof(ser_info_t)); + info->magic = SERIAL_MAGIC; + info->line = i; + info->flags = state->flags; + INIT_WORK(&info->tqueue, do_softint, info); + INIT_WORK(&info->tqueue_hangup, do_serial_hangup, info); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + info->state = state; + state->info = (struct async_struct *)info; + + /* We need to allocate a transmit and receive buffer + * descriptors from dual port ram, and a character + * buffer area from host mem. + */ + dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * RX_NUM_FIFO); + + /* Allocate space for FIFOs in the host memory. + * (for now this is from a static array of buffers :( + */ + /* mem_addr = m360_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); */ + /* mem_addr = kmalloc (RX_NUM_FIFO * RX_BUF_SIZE, GFP_BUFFER); */ + mem_addr = &rx_buf_pool[i * RX_NUM_FIFO * RX_BUF_SIZE]; + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bdp = (QUICC_BD *)((uint)pquicc + dp_addr); + info->rx_cur = info->rx_bd_base = bdp; + + /* initialize rx buffer descriptors */ + for (j=0; j<(RX_NUM_FIFO-1); j++) { + bdp->buf = &rx_buf_pool[(i * RX_NUM_FIFO + j ) * RX_BUF_SIZE]; + bdp->status = BD_SC_EMPTY | BD_SC_INTRPT; + mem_addr += RX_BUF_SIZE; + bdp++; + } + bdp->buf = &rx_buf_pool[(i * RX_NUM_FIFO + j ) * RX_BUF_SIZE]; + bdp->status = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; + + + idx = PORT_NUM(info->state->smc_scc_num); + if (info->state->smc_scc_num & NUM_IS_SCC) { + +#if defined (CONFIG_UCQUICC) && 1 + /* set the transceiver mode to RS232 */ + sipex_mode_bits &= ~(uint)SIPEX_MODE(idx,0x0f); /* clear current mode */ + sipex_mode_bits |= (uint)SIPEX_MODE(idx,0x02); + *(uint *)_periph_base = sipex_mode_bits; + /* printk ("sipex bits = 0x%08x\n", sipex_mode_bits); */ +#endif + } + + dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * TX_NUM_FIFO); + + /* Allocate space for FIFOs in the host memory. + */ + /* mem_addr = m360_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); */ + /* mem_addr = kmalloc (TX_NUM_FIFO * TX_BUF_SIZE, GFP_BUFFER); */ + mem_addr = &tx_buf_pool[i * TX_NUM_FIFO * TX_BUF_SIZE]; + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + /* bdp = (QUICC_BD *)&cp->cp_dpmem[dp_addr]; */ + bdp = (QUICC_BD *)((uint)pquicc + dp_addr); + info->tx_cur = info->tx_bd_base = (QUICC_BD *)bdp; + + /* initialize tx buffer descriptors */ + for (j=0; j<(TX_NUM_FIFO-1); j++) { + bdp->buf = &tx_buf_pool[(i * TX_NUM_FIFO + j ) * TX_BUF_SIZE]; + bdp->status = BD_SC_INTRPT; + mem_addr += TX_BUF_SIZE; + bdp++; + } + bdp->buf = &tx_buf_pool[(i * TX_NUM_FIFO + j ) * TX_BUF_SIZE]; + bdp->status = (BD_SC_WRAP | BD_SC_INTRPT); + + if (info->state->smc_scc_num & NUM_IS_SCC) { + scp = &pquicc->scc_regs[idx]; + sup = &pquicc->pram[info->state->port].scc.pscc.u; + sup->rbase = dp_addr; + sup->tbase = dp_addr; + + /* Set up the uart parameters in the + * parameter ram. + */ + sup->rfcr = SMC_EB; + sup->tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single + * character interrupts. Using idle character + * time requires some additional tuning. + */ + sup->mrblr = 1; + sup->max_idl = 0; + sup->brkcr = 1; + sup->parec = 0; + sup->frmer = 0; + sup->nosec = 0; + sup->brkec = 0; + sup->uaddr1 = 0; + sup->uaddr2 = 0; + sup->toseq = 0; + { + int i; + for (i=0;i<8;i++) + sup->cc[i] = 0x8000; + } + sup->rccm = 0xc0ff; + + /* Send the CPM an initialize command. + */ + chan = scc_chan_map[idx]; + + /* execute the INIT RX & TX PARAMS command for this channel. */ + cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + scp->scc_gsmr.w.high = 0; + scp->scc_gsmr.w.low = + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Disable all interrupts and clear all pending + * events. + */ + scp->scc_sccm = 0; + scp->scc_scce = 0xffff; + scp->scc_dsr = 0x7e7e; + scp->scc_psmr = 0x3000; + + /* If the port is the console, enable Rx and Tx. + */ +#ifdef CONFIG_SERIAL_CONSOLE + if (i == CONFIG_SERIAL_CONSOLE_PORT) + scp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); +#endif + } + else { + /* Configure SMCs Tx/Rx instead of port B + * parallel I/O. + */ + up = &pquicc->pram[info->state->port].scc.pothers.idma_smc.psmc.u; + up->rbase = dp_addr; + + iobits = 0xc0 << (idx * 4); + cp->pip_pbpar |= iobits; + cp->pip_pbdir &= ~iobits; + cp->pip_pbodr &= ~iobits; + + + /* Connect the baud rate generator to the + * SMC based upon index in rs_table. Also + * make sure it is connected to NMSI. + */ + cp->si_simode &= ~(0xffff << (idx * 16)); + cp->si_simode |= (i << ((idx * 16) + 12)); + + up->tbase = dp_addr; + + /* Set up the uart parameters in the + * parameter ram. + */ + up->rfcr = SMC_EB; + up->tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single + * character interrupts. Using idle character + * time requires some additional tuning. + */ + up->mrblr = 1; + up->max_idl = 0; + up->brkcr = 1; + + /* Send the CPM an initialize command. + */ + chan = smc_chan_map[idx]; + + cp->cp_cr = mk_cr_cmd(chan, + CPM_CR_INIT_TRX) | CPM_CR_FLG; +#ifdef CONFIG_SERIAL_CONSOLE + if (i == CONFIG_SERIAL_CONSOLE_PORT) + printk(""); +#endif + while (cp->cp_cr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp = &cp->smc_regs[idx]; + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* Disable all interrupts and clear all pending + * events. + */ + sp->smc_smcm = 0; + sp->smc_smce = 0xff; + + /* If the port is the console, enable Rx and Tx. + */ +#ifdef CONFIG_SERIAL_CONSOLE + if (i == CONFIG_SERIAL_CONSOLE_PORT) + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; +#endif + } + + /* Install interrupt handler. + */ + /* cpm_install_handler(IRQ_MACHSPEC | state->irq, rs_360_interrupt, info); */ + /*request_irq(IRQ_MACHSPEC | state->irq, rs_360_interrupt, */ + request_irq(state->irq, rs_360_interrupt, + IRQ_FLG_LOCK, "ttyS", (void *)info); + + /* Set up the baud rate generator. + */ + m360_cpm_setbrg(i, baud_table[baud_idx]); + + } + } + + return 0; +} +module_init(rs_360_init); + +/* This must always be called before the rs_360_init() function, otherwise + * it blows away the port control information. + */ +//static int __init serial_console_setup( struct console *co, char *options) +int serial_console_setup( struct console *co, char *options) +{ + struct serial_state *ser; + uint mem_addr, dp_addr, bidx, idx, iobits; + ushort chan; + QUICC_BD *bdp; + volatile QUICC *cp; + volatile struct smc_regs *sp; + volatile struct scc_regs *scp; + volatile struct smc_uart_pram *up; + volatile struct uart_pram *sup; + +/* mleslie TODO: + * add something to the 68k bootloader to store a desired initial console baud rate */ + +/* bd_t *bd; */ /* a board info struct used by EPPC-bug */ +/* bd = (bd_t *)__res; */ + + for (bidx = 0; bidx < (sizeof(baud_table) / sizeof(int)); bidx++) + /* if (bd->bi_baudrate == baud_table[bidx]) */ + if (CONSOLE_BAUDRATE == baud_table[bidx]) + break; + + /* co->cflag = CREAD|CLOCAL|bidx|CS8; */ + baud_idx = bidx; + + ser = rs_table + CONFIG_SERIAL_CONSOLE_PORT; + + cp = pquicc; /* Get pointer to Communication Processor */ + + idx = PORT_NUM(ser->smc_scc_num); + if (ser->smc_scc_num & NUM_IS_SCC) { + + /* TODO: need to set up SCC pin assignment etc. here */ + + } + else { + iobits = 0xc0 << (idx * 4); + cp->pip_pbpar |= iobits; + cp->pip_pbdir &= ~iobits; + cp->pip_pbodr &= ~iobits; + + /* Connect the baud rate generator to the + * SMC based upon index in rs_table. Also + * make sure it is connected to NMSI. + */ + cp->si_simode &= ~(0xffff << (idx * 16)); + cp->si_simode |= (idx << ((idx * 16) + 12)); + } + + /* When we get here, the CPM has been reset, so we need + * to configure the port. + * We need to allocate a transmit and receive buffer descriptor + * from dual port ram, and a character buffer area from host mem. + */ + + /* Allocate space for two buffer descriptors in the DP ram. + */ + dp_addr = m360_cpm_dpalloc(sizeof(QUICC_BD) * CONSOLE_NUM_FIFO); + + /* Allocate space for two 2 byte FIFOs in the host memory. + */ + /* mem_addr = m360_cpm_hostalloc(8); */ + mem_addr = (uint)console_fifos; + + + /* Set the physical address of the host memory buffers in + * the buffer descriptors. + */ + /* bdp = (QUICC_BD *)&cp->cp_dpmem[dp_addr]; */ + bdp = (QUICC_BD *)((uint)pquicc + dp_addr); + bdp->buf = (char *)mem_addr; + (bdp+1)->buf = (char *)(mem_addr+4); + + /* For the receive, set empty and wrap. + * For transmit, set wrap. + */ + bdp->status = BD_SC_EMPTY | BD_SC_WRAP; + (bdp+1)->status = BD_SC_WRAP; + + /* Set up the uart parameters in the parameter ram. + */ + if (ser->smc_scc_num & NUM_IS_SCC) { + scp = &cp->scc_regs[idx]; + /* sup = (scc_uart_t *)&cp->cp_dparam[ser->port]; */ + sup = &pquicc->pram[ser->port].scc.pscc.u; + + sup->rbase = dp_addr; + sup->tbase = dp_addr + sizeof(QUICC_BD); + + /* Set up the uart parameters in the + * parameter ram. + */ + sup->rfcr = SMC_EB; + sup->tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single + * character interrupts. Using idle character + * time requires some additional tuning. + */ + sup->mrblr = 1; + sup->max_idl = 0; + sup->brkcr = 1; + sup->parec = 0; + sup->frmer = 0; + sup->nosec = 0; + sup->brkec = 0; + sup->uaddr1 = 0; + sup->uaddr2 = 0; + sup->toseq = 0; + { + int i; + for (i=0;i<8;i++) + sup->cc[i] = 0x8000; + } + sup->rccm = 0xc0ff; + + /* Send the CPM an initialize command. + */ + chan = scc_chan_map[idx]; + + cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + scp->scc_gsmr.w.high = 0; + scp->scc_gsmr.w.low = + (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Disable all interrupts and clear all pending + * events. + */ + scp->scc_sccm = 0; + scp->scc_scce = 0xffff; + scp->scc_dsr = 0x7e7e; + scp->scc_psmr = 0x3000; + + scp->scc_gsmr.w.low |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + + } + else { + /* up = (smc_uart_t *)&cp->cp_dparam[ser->port]; */ + up = &pquicc->pram[ser->port].scc.pothers.idma_smc.psmc.u; + + up->rbase = dp_addr; /* Base of receive buffer desc. */ + up->tbase = dp_addr+sizeof(QUICC_BD); /* Base of xmt buffer desc. */ + up->rfcr = SMC_EB; + up->tfcr = SMC_EB; + + /* Set this to 1 for now, so we get single character interrupts. + */ + up->mrblr = 1; /* receive buffer length */ + up->max_idl = 0; /* wait forever for next char */ + + /* Send the CPM an initialize command. + */ + chan = smc_chan_map[idx]; + cp->cp_cr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG; + while (cp->cp_cr & CPM_CR_FLG); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + sp = &cp->smc_regs[idx]; + sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART; + + /* And finally, enable Rx and Tx. + */ + sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + } + + /* Set up the baud rate generator. + */ + /* m360_cpm_setbrg((ser - rs_table), bd->bi_baudrate); */ + m360_cpm_setbrg((ser - rs_table), CONSOLE_BAUDRATE); + + return 0; +} + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c new file mode 100644 index 000000000000..b25e6e490530 --- /dev/null +++ b/drivers/tty/serial/8250.c @@ -0,0 +1,3377 @@ +/* + * linux/drivers/char/8250.c + * + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * 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. + * + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. + * membase is an 'ioremapped' cookie. + */ + +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8250.h" + +#ifdef CONFIG_SPARC +#include "suncore.h" +#endif + +/* + * Configuration: + * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option + * is unsafe when used on edge-triggered interrupts. + */ +static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; + +static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; + +static struct uart_driver serial8250_reg; + +static int serial_index(struct uart_port *port) +{ + return (serial8250_reg.minor - 64) + port->line; +} + +static unsigned int skip_txen_test; /* force skip of txen test at init time */ + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 256 + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define CONFIG_SERIAL_DETECT_IRQ 1 +#endif +#ifdef CONFIG_SERIAL_8250_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif + +/* + * HUB6 is always on. This will be removed once the header + * files have been cleaned. + */ +#define CONFIG_HUB6 1 + +#include +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#ifndef SERIAL_PORT_DFNS +#define SERIAL_PORT_DFNS +#endif + +static const struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS /* defined in asm/serial.h */ +}; + +#define UART_NR CONFIG_SERIAL_8250_NR_UARTS + +#ifdef CONFIG_SERIAL_8250_RSA + +#define PORT_RSA_MAX 4 +static unsigned long probe_rsa[PORT_RSA_MAX]; +static unsigned int probe_rsa_count; +#endif /* CONFIG_SERIAL_8250_RSA */ + +struct uart_8250_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short capabilities; /* port capabilities */ + unsigned short bugs; /* port bugs */ + unsigned int tx_loadsz; /* transmit fifo load size */ + 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 cur_iotype; /* Running I/O type */ + + /* + * Some bits in registers are cleared on a read, so they must + * be saved whenever the register is read but the bits will not + * be immediately processed. + */ +#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS + unsigned char lsr_saved_flags; +#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA + unsigned char msr_saved_flags; +}; + +struct irq_info { + struct hlist_node node; + int irq; + spinlock_t lock; /* Protects list not the hash */ + struct list_head *head; +}; + +#define NR_IRQ_HASH 32 /* Can be adjusted later */ +static struct hlist_head irq_lists[NR_IRQ_HASH]; +static DEFINE_MUTEX(hash_mutex); /* Used to walk the hash */ + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial8250_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_8250] = { + .name = "8250", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16450] = { + .name = "16450", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16550] = { + .name = "16550", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16550A] = { + .name = "16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_CIRRUS] = { + .name = "Cirrus", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16650] = { + .name = "ST16650", + .fifo_size = 1, + .tx_loadsz = 1, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16650V2] = { + .name = "ST16650V2", + .fifo_size = 32, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_00, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16750] = { + .name = "TI16750", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | + UART_FCR7_64BYTE, + .flags = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE, + }, + [PORT_STARTECH] = { + .name = "Startech", + .fifo_size = 1, + .tx_loadsz = 1, + }, + [PORT_16C950] = { + .name = "16C950/954", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16654] = { + .name = "ST16654", + .fifo_size = 64, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 | + UART_FCR_T_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_16850] = { + .name = "XR16850", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, + }, + [PORT_RSA] = { + .name = "RSA", + .fifo_size = 2048, + .tx_loadsz = 2048, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11, + .flags = UART_CAP_FIFO, + }, + [PORT_NS16550A] = { + .name = "NS16550A", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_NATSEMI, + }, + [PORT_XSCALE] = { + .name = "XScale", + .fifo_size = 32, + .tx_loadsz = 32, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_UUE, + }, + [PORT_RM9000] = { + .name = "RM9000", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_OCTEON] = { + .name = "OCTEON", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO, + }, + [PORT_AR7] = { + .name = "AR7", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, + [PORT_U6_16550A] = { + .name = "U6_16550A", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, +}; + +#if defined(CONFIG_MIPS_ALCHEMY) + +/* Au1x00 UART hardware has a weird register layout */ +static const u8 au_io_in_map[] = { + [UART_RX] = 0, + [UART_IER] = 2, + [UART_IIR] = 3, + [UART_LCR] = 5, + [UART_MCR] = 6, + [UART_LSR] = 7, + [UART_MSR] = 8, +}; + +static const u8 au_io_out_map[] = { + [UART_TX] = 1, + [UART_IER] = 2, + [UART_FCR] = 4, + [UART_LCR] = 5, + [UART_MCR] = 6, +}; + +/* sane hardware needs no mapping */ +static inline int map_8250_in_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_AU) + return offset; + return au_io_in_map[offset]; +} + +static inline int map_8250_out_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_AU) + return offset; + return au_io_out_map[offset]; +} + +#elif defined(CONFIG_SERIAL_8250_RM9K) + +static const u8 + regmap_in[8] = { + [UART_RX] = 0x00, + [UART_IER] = 0x0c, + [UART_IIR] = 0x14, + [UART_LCR] = 0x1c, + [UART_MCR] = 0x20, + [UART_LSR] = 0x24, + [UART_MSR] = 0x28, + [UART_SCR] = 0x2c + }, + regmap_out[8] = { + [UART_TX] = 0x04, + [UART_IER] = 0x0c, + [UART_FCR] = 0x18, + [UART_LCR] = 0x1c, + [UART_MCR] = 0x20, + [UART_LSR] = 0x24, + [UART_MSR] = 0x28, + [UART_SCR] = 0x2c + }; + +static inline int map_8250_in_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_RM9000) + return offset; + return regmap_in[offset]; +} + +static inline int map_8250_out_reg(struct uart_port *p, int offset) +{ + if (p->iotype != UPIO_RM9000) + return offset; + return regmap_out[offset]; +} + +#else + +/* sane hardware needs no mapping */ +#define map_8250_in_reg(up, offset) (offset) +#define map_8250_out_reg(up, offset) (offset) + +#endif + +static unsigned int hub6_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + return inb(p->iobase + 1); +} + +static void hub6_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + outb(p->hub6 - 1 + offset, p->iobase); + outb(value, p->iobase + 1); +} + +static unsigned int mem_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return readb(p->membase + offset); +} + +static void mem_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + writeb(value, p->membase + offset); +} + +static void mem32_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + writel(value, p->membase + offset); +} + +static unsigned int mem32_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return readl(p->membase + offset); +} + +static unsigned int au_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return __raw_readl(p->membase + offset); +} + +static void au_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + __raw_writel(value, p->membase + offset); +} + +static unsigned int tsi_serial_in(struct uart_port *p, int offset) +{ + unsigned int tmp; + offset = map_8250_in_reg(p, offset) << p->regshift; + if (offset == UART_IIR) { + tmp = readl(p->membase + (UART_IIR & ~3)); + return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ + } else + return readb(p->membase + offset); +} + +static void tsi_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + if (!((offset == UART_IER) && (value & UART_IER_UUE))) + writeb(value, p->membase + offset); +} + +/* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ +static inline void dwapb_save_out_value(struct uart_port *p, int offset, + int value) +{ + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); + + if (offset == UART_LCR) + up->lcr = value; +} + +/* Read the IER to ensure any interrupt is cleared before returning from ISR. */ +static inline void dwapb_check_clear_ier(struct uart_port *p, int offset) +{ + if (offset == UART_TX || offset == UART_IER) + p->serial_in(p, UART_IER); +} + +static void dwapb_serial_out(struct uart_port *p, int offset, int value) +{ + int save_offset = offset; + offset = map_8250_out_reg(p, offset) << p->regshift; + dwapb_save_out_value(p, save_offset, value); + writeb(value, p->membase + offset); + dwapb_check_clear_ier(p, save_offset); +} + +static void dwapb32_serial_out(struct uart_port *p, int offset, int value) +{ + int save_offset = offset; + offset = map_8250_out_reg(p, offset) << p->regshift; + dwapb_save_out_value(p, save_offset, value); + writel(value, p->membase + offset); + dwapb_check_clear_ier(p, save_offset); +} + +static unsigned int io_serial_in(struct uart_port *p, int offset) +{ + offset = map_8250_in_reg(p, offset) << p->regshift; + return inb(p->iobase + offset); +} + +static void io_serial_out(struct uart_port *p, int offset, int value) +{ + offset = map_8250_out_reg(p, offset) << p->regshift; + outb(value, p->iobase + offset); +} + +static void set_io_from_upio(struct uart_port *p) +{ + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); + switch (p->iotype) { + case UPIO_HUB6: + p->serial_in = hub6_serial_in; + p->serial_out = hub6_serial_out; + break; + + case UPIO_MEM: + p->serial_in = mem_serial_in; + p->serial_out = mem_serial_out; + break; + + case UPIO_RM9000: + case UPIO_MEM32: + p->serial_in = mem32_serial_in; + p->serial_out = mem32_serial_out; + break; + + case UPIO_AU: + p->serial_in = au_serial_in; + p->serial_out = au_serial_out; + break; + + case UPIO_TSI: + p->serial_in = tsi_serial_in; + p->serial_out = tsi_serial_out; + break; + + case UPIO_DWAPB: + p->serial_in = mem_serial_in; + p->serial_out = dwapb_serial_out; + break; + + case UPIO_DWAPB32: + p->serial_in = mem32_serial_in; + p->serial_out = dwapb32_serial_out; + break; + + default: + p->serial_in = io_serial_in; + p->serial_out = io_serial_out; + break; + } + /* Remember loaded iotype */ + up->cur_iotype = p->iotype; +} + +static void +serial_out_sync(struct uart_8250_port *up, int offset, int value) +{ + struct uart_port *p = &up->port; + switch (p->iotype) { + case UPIO_MEM: + case UPIO_MEM32: + case UPIO_AU: + case UPIO_DWAPB: + case UPIO_DWAPB32: + p->serial_out(p, offset, value); + p->serial_in(p, UART_LCR); /* safe, no side-effects */ + break; + default: + p->serial_out(p, offset, value); + } +} + +#define serial_in(up, offset) \ + (up->port.serial_in(&(up)->port, (offset))) +#define serial_out(up, offset, value) \ + (up->port.serial_out(&(up)->port, (offset), (value))) +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + +/* Uart divisor latch read */ +static inline int _serial_dl_read(struct uart_8250_port *up) +{ + return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8; +} + +/* Uart divisor latch write */ +static inline void _serial_dl_write(struct uart_8250_port *up, int value) +{ + serial_outp(up, UART_DLL, value & 0xff); + serial_outp(up, UART_DLM, value >> 8 & 0xff); +} + +#if defined(CONFIG_MIPS_ALCHEMY) +/* Au1x00 haven't got a standard divisor latch */ +static int serial_dl_read(struct uart_8250_port *up) +{ + if (up->port.iotype == UPIO_AU) + return __raw_readl(up->port.membase + 0x28); + else + return _serial_dl_read(up); +} + +static void serial_dl_write(struct uart_8250_port *up, int value) +{ + if (up->port.iotype == UPIO_AU) + __raw_writel(value, up->port.membase + 0x28); + else + _serial_dl_write(up, value); +} +#elif defined(CONFIG_SERIAL_8250_RM9K) +static int serial_dl_read(struct uart_8250_port *up) +{ + return (up->port.iotype == UPIO_RM9000) ? + (((__raw_readl(up->port.membase + 0x10) << 8) | + (__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) : + _serial_dl_read(up); +} + +static void serial_dl_write(struct uart_8250_port *up, int value) +{ + if (up->port.iotype == UPIO_RM9000) { + __raw_writel(value, up->port.membase + 0x08); + __raw_writel(value >> 8, up->port.membase + 0x10); + } else { + _serial_dl_write(up, value); + } +} +#else +#define serial_dl_read(up) _serial_dl_read(up) +#define serial_dl_write(up, value) _serial_dl_write(up, value) +#endif + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_8250_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} + +/* + * FIFO support. + */ +static void serial8250_clear_fifos(struct uart_8250_port *p) +{ + if (p->capabilities & UART_CAP_FIFO) { + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(p, UART_FCR, 0); + } +} + +/* + * IER sleep support. UARTs which have EFRs need the "extended + * capability" bit enabled. Note that on XR16C850s, we need to + * reset LCR to write to IER. + */ +static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) +{ + if (p->capabilities & UART_CAP_SLEEP) { + if (p->capabilities & UART_CAP_EFR) { + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(p, UART_EFR, UART_EFR_ECB); + serial_outp(p, UART_LCR, 0); + } + serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); + if (p->capabilities & UART_CAP_EFR) { + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(p, UART_EFR, 0); + serial_outp(p, UART_LCR, 0); + } + } +} + +#ifdef CONFIG_SERIAL_8250_RSA +/* + * Attempts to turn on the RSA FIFO. Returns zero on failure. + * We set the port uart clock rate if we succeed. + */ +static int __enable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; + + return result; +} + +static void enable_rsa(struct uart_8250_port *up) +{ + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + __enable_rsa(up); + spin_unlock_irq(&up->port.lock); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(up, UART_RSA_FRR, 0); + } +} + +/* + * Attempts to turn off the RSA FIFO. Returns zero on failure. + * It is unknown why interrupts were disabled in here. However, + * the caller is expected to preserve this behaviour by grabbing + * the spinlock before calling this function. + */ +static void disable_rsa(struct uart_8250_port *up) +{ + unsigned char mode; + int result; + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; + spin_unlock_irq(&up->port.lock); + } +} +#endif /* CONFIG_SERIAL_8250_RSA */ + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +static int size_fifo(struct uart_8250_port *up) +{ + unsigned char old_fcr, old_mcr, old_lcr; + unsigned short old_dl; + int count; + + old_lcr = serial_inp(up, UART_LCR); + serial_outp(up, UART_LCR, 0); + old_fcr = serial_inp(up, UART_FCR); + old_mcr = serial_inp(up, UART_MCR); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_MCR, UART_MCR_LOOP); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + old_dl = serial_dl_read(up); + serial_dl_write(up, 0x0001); + serial_outp(up, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(up, UART_TX, count); + mdelay(20);/* FIXME - schedule_timeout */ + for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(up, UART_RX); + serial_outp(up, UART_FCR, old_fcr); + serial_outp(up, UART_MCR, old_mcr); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_dl_write(up, old_dl); + serial_outp(up, UART_LCR, old_lcr); + + return count; +} + +/* + * Read UART ID using the divisor method - set DLL and DLM to zero + * and the revision will be in DLL and device type in DLM. We + * preserve the device state across this. + */ +static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) +{ + unsigned char old_dll, old_dlm, old_lcr; + unsigned int id; + + old_lcr = serial_inp(p, UART_LCR); + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_A); + + old_dll = serial_inp(p, UART_DLL); + old_dlm = serial_inp(p, UART_DLM); + + serial_outp(p, UART_DLL, 0); + serial_outp(p, UART_DLM, 0); + + id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8; + + serial_outp(p, UART_DLL, old_dll); + serial_outp(p, UART_DLM, old_dlm); + serial_outp(p, UART_LCR, old_lcr); + + return id; +} + +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +static void autoconfig_has_efr(struct uart_8250_port *up) +{ + unsigned int id1, id2, id3, rev; + + /* + * Everything with an EFR has SLEEP + */ + up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; + + /* + * First we check to see if it's an Oxford Semiconductor UART. + * + * If we have to do this here because some non-National + * Semiconductor clone chips lock up if you try writing to the + * LSR register (which serial_icr_read does) + */ + + /* + * Check for Oxford Semiconductor 16C950. + * + * EFR [4] must be set else this test fails. + * + * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca) + * claims that it's needed for 952 dual UART's (which are not + * recommended for new designs). + */ + up->acr = 0; + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, 0x00); + id1 = serial_icr_read(up, UART_ID1); + id2 = serial_icr_read(up, UART_ID2); + id3 = serial_icr_read(up, UART_ID3); + rev = serial_icr_read(up, UART_REV); + + DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev); + + if (id1 == 0x16 && id2 == 0xC9 && + (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) { + up->port.type = PORT_16C950; + + /* + * Enable work around for the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if (id3 == 0x52 && rev == 0x01) + up->bugs |= UART_BUG_QUOT; + return; + } + + /* + * We check for a XR16C850 by setting DLL and DLM to 0, and then + * reading back DLL and DLM. The chip type depends on the DLM + * value read back: + * 0x10 - XR16C850 and the DLL contains the chip revision. + * 0x12 - XR16C2850. + * 0x14 - XR16C854. + */ + id1 = autoconfig_read_divisor_id(up); + DEBUG_AUTOCONF("850id=%04x ", id1); + + id2 = id1 >> 8; + if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) { + up->port.type = PORT_16850; + return; + } + + /* + * It wasn't an XR16C850. + * + * We distinguish between the '654 and the '650 by counting + * how many bytes are in the FIFO. I'm using this for now, + * since that's the technique that was sent to me in the + * serial driver update, but I'm not convinced this works. + * I've had problems doing this in the past. -TYT + */ + if (size_fifo(up) == 64) + up->port.type = PORT_16654; + else + up->port.type = PORT_16650V2; +} + +/* + * We detected a chip without a FIFO. Only two fall into + * this category - the original 8250 and the 16450. The + * 16450 has a scratch register (accessible with LCR=0) + */ +static void autoconfig_8250(struct uart_8250_port *up) +{ + unsigned char scratch, status1, status2; + + up->port.type = PORT_8250; + + scratch = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0xa5); + status1 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0x5a); + status2 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, scratch); + + if (status1 == 0xa5 && status2 == 0x5a) + up->port.type = PORT_16450; +} + +static int broken_efr(struct uart_8250_port *up) +{ + /* + * Exar ST16C2550 "A2" devices incorrectly detect as + * having an EFR, and report an ID of 0x0201. See + * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html + */ + if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16) + return 1; + + return 0; +} + +/* + * We know that the chip has FIFOs. Does it have an EFR? The + * EFR is located in the same register position as the IIR and + * we know the top two bits of the IIR are currently set. The + * EFR should contain zero. Try to read the EFR. + */ +static void autoconfig_16550a(struct uart_8250_port *up) +{ + unsigned char status1, status2; + unsigned int iersave; + + up->port.type = PORT_16550A; + up->capabilities |= UART_CAP_FIFO; + + /* + * Check for presence of the EFR when DLAB is set. + * Only ST16C650V1 UARTs pass this test. + */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + if (serial_in(up, UART_EFR) == 0) { + serial_outp(up, UART_EFR, 0xA8); + if (serial_in(up, UART_EFR) != 0) { + DEBUG_AUTOCONF("EFRv1 "); + up->port.type = PORT_16650; + up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP; + } else { + DEBUG_AUTOCONF("Motorola 8xxx DUART "); + } + serial_outp(up, UART_EFR, 0); + return; + } + + /* + * Maybe it requires 0xbf to be written to the LCR. + * (other ST16C650V2 UARTs, TI16C752A, etc) + */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) { + DEBUG_AUTOCONF("EFRv2 "); + autoconfig_has_efr(up); + return; + } + + /* + * Check for a National Semiconductor SuperIO chip. + * Attempt to switch to bank 2, read the value of the LOOP bit + * from EXCR1. Switch back to bank 0, change it in MCR. Then + * switch back to bank 2, read it from EXCR1 again and check + * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2 + */ + serial_outp(up, UART_LCR, 0); + status1 = serial_in(up, UART_MCR); + serial_outp(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + + if (!((status2 ^ status1) & UART_MCR_LOOP)) { + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_MCR, status1 ^ UART_MCR_LOOP); + serial_outp(up, UART_LCR, 0xE0); + status2 = serial_in(up, 0x02); /* EXCR1 */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_MCR, status1); + + if ((status2 ^ status1) & UART_MCR_LOOP) { + unsigned short quot; + + serial_outp(up, UART_LCR, 0xE0); + + quot = serial_dl_read(up); + quot <<= 3; + + status1 = serial_in(up, 0x04); /* EXCR2 */ + status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_outp(up, 0x04, status1); + + serial_dl_write(up, quot); + + serial_outp(up, UART_LCR, 0); + + up->port.uartclk = 921600*16; + up->port.type = PORT_NS16550A; + up->capabilities |= UART_NATSEMI; + return; + } + } + + /* + * No EFR. Try to detect a TI16750, which only sets bit 5 of + * the IIR when 64 byte FIFO mode is enabled when DLAB is set. + * Try setting it with and without DLAB set. Cheap clones + * set bit 5 without DLAB set. + */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status1 = serial_in(up, UART_IIR) >> 5; + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + status2 = serial_in(up, UART_IIR) >> 5; + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, 0); + + DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2); + + if (status1 == 6 && status2 == 7) { + up->port.type = PORT_16750; + up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP; + return; + } + + /* + * Try writing and reading the UART_IER_UUE bit (b6). + * If it works, this is probably one of the Xscale platform's + * internal UARTs. + * We're going to explicitly set the UUE bit to 0 before + * trying to write and read a 1 just to make sure it's not + * already a 1 and maybe locked there before we even start start. + */ + iersave = serial_in(up, UART_IER); + serial_outp(up, UART_IER, iersave & ~UART_IER_UUE); + if (!(serial_in(up, UART_IER) & UART_IER_UUE)) { + /* + * OK it's in a known zero state, try writing and reading + * without disturbing the current state of the other bits. + */ + serial_outp(up, UART_IER, iersave | UART_IER_UUE); + if (serial_in(up, UART_IER) & UART_IER_UUE) { + /* + * It's an Xscale. + * We'll leave the UART_IER_UUE bit set to 1 (enabled). + */ + DEBUG_AUTOCONF("Xscale "); + up->port.type = PORT_XSCALE; + up->capabilities |= UART_CAP_UUE; + return; + } + } else { + /* + * If we got here we couldn't force the IER_UUE bit to 0. + * Log it and continue. + */ + DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 "); + } + serial_outp(up, UART_IER, iersave); + + /* + * We distinguish between 16550A and U6 16550A by counting + * how many bytes are in the FIFO. + */ + if (up->port.type == PORT_16550A && size_fifo(up) == 64) { + up->port.type = PORT_U6_16550A; + up->capabilities |= UART_CAP_AFE; + } +} + +/* + * This routine is called by rs_init() to initialize a specific serial + * port. It determines what type of UART chip this serial port is + * using: 8250, 16450, 16550, 16550A. The important question is + * whether or not this UART is a 16550A or not, since this will + * determine whether or not we can use its FIFO features or not. + */ +static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + if (!up->port.iobase && !up->port.mapbase && !up->port.membase) + return; + + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", + serial_index(&up->port), up->port.iobase, up->port.membase); + + /* + * We really do need global IRQs disabled here - we're going to + * be frobbing the chips IRQ enable register to see if it exists. + */ + spin_lock_irqsave(&up->port.lock, flags); + + up->capabilities = 0; + up->bugs = 0; + + if (!(up->port.flags & UPF_BUGGY_UART)) { + /* + * Do a simple existence test first; if we fail this, + * there's no point trying anything else. + * + * 0x80 is used as a nonsense port to prevent against + * false positives due to ISA bus float. The + * assumption is that 0x80 is a non-existent port; + * which should be safe since include/asm/io.h also + * makes this assumption. + * + * Note: this is safe as long as MCR bit 4 is clear + * and the device is in "PC" mode. + */ + scratch = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + /* + * Mask out IER[7:4] bits for test as some UARTs (e.g. TL + * 16C754B) allow only to modify them if an EFR bit is set. + */ + scratch2 = serial_inp(up, UART_IER) & 0x0f; + serial_outp(up, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(up, UART_IER) & 0x0f; + serial_outp(up, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + /* + * We failed; there's nothing here + */ + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(up, UART_MCR); + save_lcr = serial_in(up, UART_LCR); + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufacturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(up->port.flags & UPF_SKIP_TEST)) { + serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(up, UART_MSR) & 0xF0; + serial_outp(up, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; + } + } + + /* + * We're pretty sure there's a port here. Lets find out what + * type of port it is. The IIR top two bits allows us to find + * out if it's 8250 or 16450, 16550, 16550A or later. This + * determines what we test for next. + * + * We also initialise the EFR (if any) to zero for later. The + * EFR occupies the same register location as the FCR and IIR. + */ + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(up, UART_EFR, 0); + serial_outp(up, UART_LCR, 0); + + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(up, UART_IIR) >> 6; + + DEBUG_AUTOCONF("iir=%d ", scratch); + + switch (scratch) { + case 0: + autoconfig_8250(up); + break; + case 1: + up->port.type = PORT_UNKNOWN; + break; + case 2: + up->port.type = PORT_16550; + break; + case 3: + autoconfig_16550a(up); + break; + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Only probe for RSA ports if we got the region. + */ + if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) { + int i; + + for (i = 0 ; i < probe_rsa_count; ++i) { + if (probe_rsa[i] == up->port.iobase && + __enable_rsa(up)) { + up->port.type = PORT_RSA; + break; + } + } + } +#endif + + serial_outp(up, UART_LCR, save_lcr); + + if (up->capabilities != uart_config[up->port.type].flags) { + printk(KERN_WARNING + "ttyS%d: detected caps %08x should be %08x\n", + serial_index(&up->port), up->capabilities, + uart_config[up->port.type].flags); + } + + up->port.fifosize = uart_config[up->port.type].fifo_size; + up->capabilities = uart_config[up->port.type].flags; + up->tx_loadsz = uart_config[up->port.type].tx_loadsz; + + if (up->port.type == PORT_UNKNOWN) + goto out; + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.type == PORT_RSA) + serial_outp(up, UART_RSA_FRR, 0); +#endif + serial_outp(up, UART_MCR, save_mcr); + serial8250_clear_fifos(up); + serial_in(up, UART_RX); + if (up->capabilities & UART_CAP_UUE) + serial_outp(up, UART_IER, UART_IER_UUE); + else + serial_outp(up, UART_IER, 0); + + out: + spin_unlock_irqrestore(&up->port.lock, flags); + DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name); +} + +static void autoconfig_irq(struct uart_8250_port *up) +{ + unsigned char save_mcr, save_ier; + unsigned char save_ICP = 0; + unsigned int ICP = 0; + unsigned long irqs; + int irq; + + if (up->port.flags & UPF_FOURPORT) { + ICP = (up->port.iobase & 0xfe0) | 0x1f; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(up, UART_MCR); + save_ier = serial_inp(up, UART_IER); + serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(up, UART_MCR, 0); + udelay(10); + if (up->port.flags & UPF_FOURPORT) { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(up, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(up, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(up, UART_LSR); + (void)serial_inp(up, UART_RX); + (void)serial_inp(up, UART_IIR); + (void)serial_inp(up, UART_MSR); + serial_outp(up, UART_TX, 0xFF); + udelay(20); + irq = probe_irq_off(irqs); + + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_IER, save_ier); + + if (up->port.flags & UPF_FOURPORT) + outb_p(save_ICP, ICP); + + up->port.irq = (irq > 0) ? irq : 0; +} + +static inline void __stop_tx(struct uart_8250_port *p) +{ + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } +} + +static void serial8250_stop_tx(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + __stop_tx(up); + + /* + * We really want to stop the transmitter from sending. + */ + if (up->port.type == PORT_16C950) { + up->acr |= UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void transmit_chars(struct uart_8250_port *up); + +static void serial8250_start_tx(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + + if (up->bugs & UART_BUG_TXEN) { + unsigned char lsr; + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + if ((up->port.type == PORT_RM9000) ? + (lsr & UART_LSR_THRE) : + (lsr & UART_LSR_TEMT)) + transmit_chars(up); + } + } + + /* + * Re-enable the transmitter if we disabled it. + */ + if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { + up->acr &= ~UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void serial8250_stop_rx(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void serial8250_enable_ms(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + /* no MSR capabilities */ + if (up->bugs & UART_BUG_NOMSR) + return; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void +receive_chars(struct uart_8250_port *up, unsigned int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch, lsr = *status; + int max_count = 256; + char flag; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_inp(up, UART_RX); + else + /* + * Intel 82571 has a Serial Over Lan device that will + * set UART_LSR_BI without setting UART_LSR_DR when + * it receives a break. To avoid reading from the + * receive buffer without UART_LSR_DR bit set, we + * just force the read character to be 0 + */ + ch = 0; + + flag = TTY_NORMAL; + up->port.icount.rx++; + + lsr |= up->lsr_saved_flags; + up->lsr_saved_flags = 0; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) + up->port.icount.parity++; + else if (lsr & UART_LSR_FE) + up->port.icount.frame++; + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + + if (lsr & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); + +ignore_char: + lsr = serial_inp(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); + *status = lsr; +} + +static void transmit_chars(struct uart_8250_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_outp(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_tx_stopped(&up->port)) { + serial8250_stop_tx(&up->port); + return; + } + if (uart_circ_empty(xmit)) { + __stop_tx(up); + return; + } + + count = up->tx_loadsz; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + __stop_tx(up); +} + +static unsigned int check_modem_status(struct uart_8250_port *up) +{ + unsigned int status = serial_in(up, UART_MSR); + + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + return status; +} + +/* + * This handles the interrupt from one port. + */ +static void serial8250_handle_port(struct uart_8250_port *up) +{ + unsigned int status; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + status = serial_inp(up, UART_LSR); + + DEBUG_INTR("status = %x...", status); + + if (status & (UART_LSR_DR | UART_LSR_BI)) + receive_chars(up, &status); + check_modem_status(up); + if (status & UART_LSR_THRE) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t serial8250_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0, handled = 0; + + DEBUG_INTR("serial8250_interrupt(%d)...", irq); + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_8250_port *up; + unsigned int iir; + + up = list_entry(l, struct uart_8250_port, list); + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) { + serial8250_handle_port(up); + + handled = 1; + + end = NULL; + } else if ((up->port.iotype == UPIO_DWAPB || + up->port.iotype == UPIO_DWAPB32) && + (iir & UART_IIR_BUSY) == UART_IIR_BUSY) { + /* The DesignWare APB UART has an Busy Detect (0x07) + * interrupt meaning an LCR write attempt occured while the + * UART was busy. The interrupt must be cleared by reading + * the UART status register (USR) and the LCR re-written. */ + unsigned int status; + status = *(volatile u32 *)up->port.private_data; + serial_out(up, UART_LCR, up->lcr); + + handled = 1; + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + /* If we hit this, we're dead. */ + printk_ratelimited(KERN_ERR + "serial8250: too much work for irq%d\n", irq); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_RETVAL(handled); +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + spin_unlock_irq(&i->lock); + /* List empty so throw away the hash node */ + if (i->head == NULL) { + hlist_del(&i->node); + kfree(i); + } +} + +static int serial_link_irq_chain(struct uart_8250_port *up) +{ + struct hlist_head *h; + struct hlist_node *n; + struct irq_info *i; + int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; + + mutex_lock(&hash_mutex); + + h = &irq_lists[up->port.irq % NR_IRQ_HASH]; + + hlist_for_each(n, h) { + i = hlist_entry(n, struct irq_info, node); + if (i->irq == up->port.irq) + break; + } + + if (n == NULL) { + i = kzalloc(sizeof(struct irq_info), GFP_KERNEL); + if (i == NULL) { + mutex_unlock(&hash_mutex); + return -ENOMEM; + } + spin_lock_init(&i->lock); + i->irq = up->port.irq; + hlist_add_head(&i->node, h); + } + mutex_unlock(&hash_mutex); + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + irq_flags |= up->port.irqflags; + ret = request_irq(up->port.irq, serial8250_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_8250_port *up) +{ + struct irq_info *i; + struct hlist_node *n; + struct hlist_head *h; + + mutex_lock(&hash_mutex); + + h = &irq_lists[up->port.irq % NR_IRQ_HASH]; + + hlist_for_each(n, h) { + i = hlist_entry(n, struct irq_info, node); + if (i->irq == up->port.irq) + break; + } + + BUG_ON(n == NULL); + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) + free_irq(up->port.irq, i); + + serial_do_unlink(i, up); + mutex_unlock(&hash_mutex); +} + +/* + * This function is used to handle ports that do not have an + * interrupt. This doesn't work very well for 16450's, but gives + * barely passable results for a 16550A. (Although at the expense + * of much CPU overhead). + */ +static void serial8250_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + unsigned int iir; + + iir = serial_in(up, UART_IIR); + if (!(iir & UART_IIR_NO_INT)) + serial8250_handle_port(up); + mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); +} + +static void serial8250_backup_timeout(unsigned long data) +{ + struct uart_8250_port *up = (struct uart_8250_port *)data; + unsigned int iir, ier = 0, lsr; + unsigned long flags; + + /* + * Must disable interrupts or else we risk racing with the interrupt + * based handler. + */ + if (is_real_interrupt(up->port.irq)) { + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + } + + iir = serial_in(up, UART_IIR); + + /* + * This should be a safe test for anyone who doesn't trust the + * IIR bits on their UART, but it's specifically designed for + * the "Diva" UART used on the management processor on many HP + * ia64 and parisc boxes. + */ + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + spin_unlock_irqrestore(&up->port.lock, flags); + if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && + (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && + (lsr & UART_LSR_THRE)) { + iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir |= UART_IIR_THRI; + } + + if (!(iir & UART_IIR_NO_INT)) + serial8250_handle_port(up); + + if (is_real_interrupt(up->port.irq)) + serial_out(up, UART_IER, ier); + + /* Standard timer interval plus 0.2s to keep the port running */ + mod_timer(&up->timer, + jiffies + uart_poll_timeout(&up->port) + HZ / 5); +} + +static unsigned int serial8250_tx_empty(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + unsigned int lsr; + + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; + spin_unlock_irqrestore(&up->port.lock, flags); + + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; +} + +static unsigned int serial8250_get_mctrl(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned int status; + unsigned int ret; + + status = check_modem_status(up); + + 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 serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned char mcr = 0; + + 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; + + mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial8250_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_8250_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + for (;;) { + status = serial_in(up, UART_LSR); + + up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; + + if ((status & bits) == bits) + break; + if (--tmout == 0) + break; + udelay(1); + } + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + unsigned int tmout; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) + break; + udelay(1); + touch_nmi_watchdog(); + } + } +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial8250_get_poll_char(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned char lsr = serial_inp(up, UART_LSR); + + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; + + return serial_inp(up, UART_RX); +} + + +static void serial8250_put_poll_char(struct uart_port *port, + unsigned char c) +{ + unsigned int ier; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + if (up->capabilities & UART_CAP_UUE) + serial_out(up, UART_IER, UART_IER_UUE); + else + serial_out(up, UART_IER, 0); + + wait_for_xmitr(up, BOTH_EMPTY); + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(up, UART_TX, c); + if (c == 10) { + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_TX, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_IER, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static int serial8250_startup(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + unsigned char lsr, iir; + int retval; + + up->port.fifosize = uart_config[up->port.type].fifo_size; + up->tx_loadsz = uart_config[up->port.type].tx_loadsz; + up->capabilities = uart_config[up->port.type].flags; + up->mcr = 0; + + if (up->port.iotype != up->cur_iotype) + set_io_from_upio(port); + + if (up->port.type == PORT_16C950) { + /* Wake up and initialize UART */ + up->acr = 0; + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_IER, 0); + serial_outp(up, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + enable_rsa(up); +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial8250_clear_fifos(up); + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + /* + * At this point, there's no way the LSR could still be 0xff; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (!(up->port.flags & UPF_BUGGY_UART) && + (serial_inp(up, UART_LSR) == 0xff)) { + printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n", + serial_index(&up->port)); + return -ENODEV; + } + + /* + * For a XR16C850, we need to set the trigger levels + */ + if (up->port.type == PORT_16850) { + unsigned char fctr; + + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + + fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); + serial_outp(up, UART_TRG, UART_TRG_96); + serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX); + serial_outp(up, UART_TRG, UART_TRG_96); + + serial_outp(up, UART_LCR, 0); + } + + if (is_real_interrupt(up->port.irq)) { + unsigned char iir1; + /* + * Test for UARTs that do not reassert THRE when the + * transmitter is idle and the interrupt has already + * been cleared. Real 16550s should always reassert + * this interrupt whenever the transmitter is idle and + * the interrupt is enabled. Delays are necessary to + * allow register changes to become visible. + */ + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.irqflags & IRQF_SHARED) + disable_irq_nosync(up->port.irq); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_out_sync(up, UART_IER, UART_IER_THRI); + udelay(1); /* allow THRE to set */ + iir1 = serial_in(up, UART_IIR); + serial_out(up, UART_IER, 0); + serial_out_sync(up, UART_IER, UART_IER_THRI); + udelay(1); /* allow a working UART time to re-assert THRE */ + iir = serial_in(up, UART_IIR); + serial_out(up, UART_IER, 0); + + if (up->port.irqflags & IRQF_SHARED) + enable_irq(up->port.irq); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * If the interrupt is not reasserted, setup a timer to + * kick the UART on a regular basis. + */ + if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { + up->bugs |= UART_BUG_THRE; + pr_debug("ttyS%d - using backup timer\n", + serial_index(port)); + } + } + + /* + * The above check will only give an accurate result the first time + * the port is opened so this value needs to be preserved. + */ + if (up->bugs & UART_BUG_THRE) { + up->timer.function = serial8250_backup_timeout; + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + + uart_poll_timeout(port) + HZ / 5); + } + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Now, initialize the UART + */ + serial_outp(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + if (!is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT1; + } else + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + if (is_real_interrupt(up->port.irq)) + up->port.mctrl |= TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + + /* Serial over Lan (SoL) hack: + Intel 8257x Gigabit ethernet chips have a + 16550 emulation, to be used for Serial Over Lan. + Those chips take a longer time than a normal + serial device to signalize that a transmission + data was queued. Due to that, the above test generally + fails. One solution would be to delay the reading of + iir. However, this is not reliable, since the timeout + is variable. So, let's just don't test if we receive + TX irq. This way, we'll never enable UART_BUG_TXEN. + */ + if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST) + goto dont_test_tx_en; + + /* + * Do a quick test to see if we receive an + * interrupt when we enable the TX irq. + */ + serial_outp(up, UART_IER, UART_IER_THRI); + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + serial_outp(up, UART_IER, 0); + + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + if (!(up->bugs & UART_BUG_TXEN)) { + up->bugs |= UART_BUG_TXEN; + pr_debug("ttyS%d - enabling bad tx status workarounds\n", + serial_index(port)); + } + } else { + up->bugs &= ~UART_BUG_TXEN; + } + +dont_test_tx_en: + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Clear the interrupt registers again for luck, and clear the + * saved flags to avoid getting false values from polling + * routines or the previous session. + */ + serial_inp(up, UART_LSR); + serial_inp(up, UART_RX); + serial_inp(up, UART_IIR); + serial_inp(up, UART_MSR); + up->lsr_saved_flags = 0; + up->msr_saved_flags = 0; + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(up, UART_IER, up->ier); + + if (up->port.flags & UPF_FOURPORT) { + unsigned int icp; + /* + * Enable interrupts on the AST Fourport board + */ + icp = (up->port.iobase & 0xfe0) | 0x01f; + outb_p(0x80, icp); + (void) inb_p(icp); + } + + return 0; +} + +static void serial8250_shutdown(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned long flags; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_outp(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((up->port.iobase & 0xfe0) | 0x1f); + up->port.mctrl |= TIOCM_OUT1; + } else + up->port.mctrl &= ~TIOCM_OUT2; + + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); + serial8250_clear_fifos(up); + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + disable_rsa(up); +#endif + + /* + * Read data port to reset things, and then unlink from + * the IRQ chain. + */ + (void) serial_in(up, UART_RX); + + del_timer_sync(&up->timer); + up->timer.function = serial8250_timeout; + if (is_real_interrupt(up->port.irq)) + serial_unlink_irq_chain(up); +} + +static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int quot; + + /* + * Handle magic divisors for baud rates above baud_base on + * SMSC SuperIO chips. + */ + 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 = uart_get_divisor(port, baud); + + return quot; +} + +void +serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + 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 + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + quot = serial8250_get_divisor(port, baud); + + /* + * Oxford Semi 952 rev B workaround + */ + if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0) + quot++; + + if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) { + if (baud < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + else + fcr = uart_config[up->port.type].fcr; + } + + /* + * MCR-based auto flow control. When AFE is enabled, RTS will be + * deasserted when the receive FIFO contains more characters than + * the trigger, or the MCR RTS bit is cleared. In the case where + * the remote UART is not using CTS auto flow control, we must + * have sufficient FIFO entries for the latency of the remote + * UART to respond. IOW, at least 32 bytes of FIFO. + */ + if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) { + up->mcr &= ~UART_MCR_AFE; + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + } + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (!(up->bugs & UART_BUG_NOMSR) && + UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + if (up->capabilities & UART_CAP_UUE) + up->ier |= UART_IER_UUE | UART_IER_RTOIE; + + serial_out(up, UART_IER, up->ier); + + if (up->capabilities & UART_CAP_EFR) { + unsigned char efr = 0; + /* + * TI16C752/Startech hardware flow control. FIXME: + * - TI16C752 requires control thresholds to be set. + * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled. + */ + if (termios->c_cflag & CRTSCTS) + efr |= UART_EFR_CTS; + + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_outp(up, UART_EFR, efr); + } + +#ifdef CONFIG_ARCH_OMAP + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (cpu_is_omap1510() && is_omap_port(up)) { + if (baud == 115200) { + quot = 1; + serial_out(up, UART_OMAP_OSC_12M_SEL, 1); + } else + serial_out(up, UART_OMAP_OSC_12M_SEL, 0); + } +#endif + + if (up->capabilities & UART_NATSEMI) { + /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ + serial_outp(up, UART_LCR, 0xe0); + } else { + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + } + + serial_dl_write(up, quot); + + /* + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR + * is written without DLAB set, this mode will be disabled. + */ + if (up->port.type == PORT_16750) + serial_outp(up, UART_FCR, fcr); + + serial_outp(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + if (up->port.type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(up, UART_FCR, fcr); /* set fcr */ + } + serial8250_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} +EXPORT_SYMBOL(serial8250_do_set_termios); + +static void +serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + if (port->set_termios) + port->set_termios(port, termios, old); + else + serial8250_do_set_termios(port, termios, old); +} + +static void +serial8250_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + serial8250_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + + +void serial8250_do_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *p = + container_of(port, struct uart_8250_port, port); + + serial8250_set_sleep(p, state != 0); +} +EXPORT_SYMBOL(serial8250_do_pm); + +static void +serial8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + if (port->pm) + port->pm(port, state, oldstate); + else + serial8250_do_pm(port, state, oldstate); +} + +static unsigned int serial8250_port_size(struct uart_8250_port *pt) +{ + if (pt->port.iotype == UPIO_AU) + return 0x1000; +#ifdef CONFIG_ARCH_OMAP + if (is_omap_port(pt)) + return 0x16 << pt->port.regshift; +#endif + return 8 << pt->port.regshift; +} + +/* + * Resource handling. + */ +static int serial8250_request_std_resource(struct uart_8250_port *up) +{ + unsigned int size = serial8250_port_size(up); + int ret = 0; + + switch (up->port.iotype) { + case UPIO_AU: + case UPIO_TSI: + case UPIO_MEM32: + case UPIO_MEM: + case UPIO_DWAPB: + case UPIO_DWAPB32: + if (!up->port.mapbase) + break; + + if (!request_mem_region(up->port.mapbase, size, "serial")) { + ret = -EBUSY; + break; + } + + if (up->port.flags & UPF_IOREMAP) { + up->port.membase = ioremap_nocache(up->port.mapbase, + size); + if (!up->port.membase) { + release_mem_region(up->port.mapbase, size); + ret = -ENOMEM; + } + } + break; + + case UPIO_HUB6: + case UPIO_PORT: + if (!request_region(up->port.iobase, size, "serial")) + ret = -EBUSY; + break; + } + return ret; +} + +static void serial8250_release_std_resource(struct uart_8250_port *up) +{ + unsigned int size = serial8250_port_size(up); + + switch (up->port.iotype) { + case UPIO_AU: + case UPIO_TSI: + case UPIO_MEM32: + case UPIO_MEM: + case UPIO_DWAPB: + case UPIO_DWAPB32: + if (!up->port.mapbase) + break; + + if (up->port.flags & UPF_IOREMAP) { + iounmap(up->port.membase); + up->port.membase = NULL; + } + + release_mem_region(up->port.mapbase, size); + break; + + case UPIO_HUB6: + case UPIO_PORT: + release_region(up->port.iobase, size); + break; + } +} + +static int serial8250_request_rsa_resource(struct uart_8250_port *up) +{ + unsigned long start = UART_RSA_BASE << up->port.regshift; + unsigned int size = 8 << up->port.regshift; + int ret = -EINVAL; + + switch (up->port.iotype) { + case UPIO_HUB6: + case UPIO_PORT: + start += up->port.iobase; + if (request_region(start, size, "serial-rsa")) + ret = 0; + else + ret = -EBUSY; + break; + } + + return ret; +} + +static void serial8250_release_rsa_resource(struct uart_8250_port *up) +{ + unsigned long offset = UART_RSA_BASE << up->port.regshift; + unsigned int size = 8 << up->port.regshift; + + switch (up->port.iotype) { + case UPIO_HUB6: + case UPIO_PORT: + release_region(up->port.iobase + offset, size); + break; + } +} + +static void serial8250_release_port(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + serial8250_release_std_resource(up); + if (up->port.type == PORT_RSA) + serial8250_release_rsa_resource(up); +} + +static int serial8250_request_port(struct uart_port *port) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + int ret = 0; + + ret = serial8250_request_std_resource(up); + if (ret == 0 && up->port.type == PORT_RSA) { + ret = serial8250_request_rsa_resource(up); + if (ret < 0) + serial8250_release_std_resource(up); + } + + return ret; +} + +static void serial8250_config_port(struct uart_port *port, int flags) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + int probeflags = PROBE_ANY; + int ret; + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + ret = serial8250_request_std_resource(up); + if (ret < 0) + return; + + ret = serial8250_request_rsa_resource(up); + if (ret < 0) + probeflags &= ~PROBE_RSA; + + if (up->port.iotype != up->cur_iotype) + set_io_from_upio(port); + + if (flags & UART_CONFIG_TYPE) + autoconfig(up, probeflags); + + /* if access method is AU, it is a 16550 with a quirk */ + if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) + up->bugs |= UART_BUG_NOMSR; + + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(up); + + if (up->port.type != PORT_RSA && probeflags & PROBE_RSA) + serial8250_release_rsa_resource(up); + if (up->port.type == PORT_UNKNOWN) + serial8250_release_std_resource(up); +} + +static int +serial8250_verify_port(struct 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 >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char * +serial8250_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops serial8250_pops = { + .tx_empty = serial8250_tx_empty, + .set_mctrl = serial8250_set_mctrl, + .get_mctrl = serial8250_get_mctrl, + .stop_tx = serial8250_stop_tx, + .start_tx = serial8250_start_tx, + .stop_rx = serial8250_stop_rx, + .enable_ms = serial8250_enable_ms, + .break_ctl = serial8250_break_ctl, + .startup = serial8250_startup, + .shutdown = serial8250_shutdown, + .set_termios = serial8250_set_termios, + .set_ldisc = serial8250_set_ldisc, + .pm = serial8250_pm, + .type = serial8250_type, + .release_port = serial8250_release_port, + .request_port = serial8250_request_port, + .config_port = serial8250_config_port, + .verify_port = serial8250_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial8250_get_poll_char, + .poll_put_char = serial8250_put_poll_char, +#endif +}; + +static struct uart_8250_port serial8250_ports[UART_NR]; + +static void (*serial8250_isa_config)(int port, struct uart_port *up, + unsigned short *capabilities); + +void serial8250_set_isa_configurator( + void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) +{ + serial8250_isa_config = v; +} +EXPORT_SYMBOL(serial8250_set_isa_configurator); + +static void __init serial8250_isa_init_ports(void) +{ + struct uart_8250_port *up; + static int first = 1; + int i, irqflag = 0; + + if (!first) + return; + first = 0; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + up->port.line = i; + spin_lock_init(&up->port.lock); + + init_timer(&up->timer); + up->timer.function = serial8250_timeout; + + /* + * ALPHA_KLUDGE_MCR needs to be killed. + */ + up->mcr_mask = ~ALPHA_KLUDGE_MCR; + up->mcr_force = ALPHA_KLUDGE_MCR; + + up->port.ops = &serial8250_pops; + } + + if (share_irqs) + irqflag = IRQF_SHARED; + + for (i = 0, up = serial8250_ports; + i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.irqflags = old_serial_port[i].irqflags; + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags; + up->port.hub6 = old_serial_port[i].hub6; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + set_io_from_upio(&up->port); + up->port.irqflags |= irqflag; + if (serial8250_isa_config != NULL) + serial8250_isa_config(i, &up->port, &up->capabilities); + + } +} + +static void +serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) +{ + up->port.type = type; + up->port.fifosize = uart_config[type].fifo_size; + up->capabilities = uart_config[type].flags; + up->tx_loadsz = uart_config[type].tx_loadsz; +} + +static void __init +serial8250_register_ports(struct uart_driver *drv, struct device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + up->cur_iotype = 0xFF; + } + + serial8250_isa_init_ports(); + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + up->port.dev = dev; + + if (up->port.flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(up, up->port.type); + + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_8250_CONSOLE + +static void serial8250_console_putchar(struct uart_port *port, int ch) +{ + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); + + wait_for_xmitr(up, UART_LSR_THRE); + serial_out(up, UART_TX, ch); +} + +/* + * 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. + */ +static void +serial8250_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_8250_port *up = &serial8250_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + touch_nmi_watchdog(); + + local_irq_save(flags); + if (up->port.sysrq) { + /* serial8250_handle_port() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + + if (up->capabilities & UART_CAP_UUE) + serial_out(up, UART_IER, UART_IER_UUE); + else + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial8250_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up, BOTH_EMPTY); + serial_out(up, UART_IER, ier); + + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + check_modem_status(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init serial8250_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + 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. + */ + if (co->index >= nr_uarts) + co->index = 0; + port = &serial8250_ports[co->index].port; + if (!port->iobase && !port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static int serial8250_console_early_setup(void) +{ + return serial8250_find_port_for_earlycon(); +} + +static struct console serial8250_console = { + .name = "ttyS", + .write = serial8250_console_write, + .device = uart_console_device, + .setup = serial8250_console_setup, + .early_setup = serial8250_console_early_setup, + .flags = CON_PRINTBUFFER | CON_ANYTIME, + .index = -1, + .data = &serial8250_reg, +}; + +static int __init serial8250_console_init(void) +{ + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + + serial8250_isa_init_ports(); + register_console(&serial8250_console); + return 0; +} +console_initcall(serial8250_console_init); + +int serial8250_find_port(struct uart_port *p) +{ + int line; + struct uart_port *port; + + for (line = 0; line < nr_uarts; line++) { + port = &serial8250_ports[line].port; + if (uart_match_port(p, port)) + return line; + } + return -ENODEV; +} + +#define SERIAL8250_CONSOLE &serial8250_console +#else +#define SERIAL8250_CONSOLE NULL +#endif + +static struct uart_driver serial8250_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .cons = SERIAL8250_CONSOLE, +}; + +/* + * early_serial_setup - early registration for 8250 ports + * + * Setup an 8250 port structure prior to console initialisation. Use + * after console initialisation will cause undefined behaviour. + */ +int __init early_serial_setup(struct uart_port *port) +{ + struct uart_port *p; + + if (port->line >= ARRAY_SIZE(serial8250_ports)) + return -ENODEV; + + serial8250_isa_init_ports(); + p = &serial8250_ports[port->line].port; + p->iobase = port->iobase; + p->membase = port->membase; + p->irq = port->irq; + p->irqflags = port->irqflags; + p->uartclk = port->uartclk; + p->fifosize = port->fifosize; + p->regshift = port->regshift; + p->iotype = port->iotype; + p->flags = port->flags; + p->mapbase = port->mapbase; + p->private_data = port->private_data; + p->type = port->type; + p->line = port->line; + + set_io_from_upio(p); + if (port->serial_in) + p->serial_in = port->serial_in; + if (port->serial_out) + p->serial_out = port->serial_out; + + return 0; +} + +/** + * serial8250_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void serial8250_suspend_port(int line) +{ + uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port); +} + +/** + * serial8250_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void serial8250_resume_port(int line) +{ + struct uart_8250_port *up = &serial8250_ports[line]; + + if (up->capabilities & UART_NATSEMI) { + unsigned char tmp; + + /* Ensure it's still in high speed mode */ + serial_outp(up, UART_LCR, 0xE0); + + tmp = serial_in(up, 0x04); /* EXCR2 */ + tmp &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */ + tmp |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ + serial_outp(up, 0x04, tmp); + + serial_outp(up, UART_LCR, 0); + } + uart_resume_port(&serial8250_reg, &up->port); +} + +/* + * Register a set of serial devices attached to a platform device. The + * 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) +{ + struct plat_serial8250_port *p = dev->dev.platform_data; + struct uart_port port; + int ret, i, irqflag = 0; + + memset(&port, 0, sizeof(struct uart_port)); + + if (share_irqs) + irqflag = IRQF_SHARED; + + for (i = 0; p && p->flags != 0; p++, i++) { + port.iobase = p->iobase; + port.membase = p->membase; + port.irq = p->irq; + port.irqflags = p->irqflags; + port.uartclk = p->uartclk; + port.regshift = p->regshift; + port.iotype = p->iotype; + port.flags = p->flags; + port.mapbase = p->mapbase; + port.hub6 = p->hub6; + port.private_data = p->private_data; + port.type = p->type; + port.serial_in = p->serial_in; + port.serial_out = p->serial_out; + port.set_termios = p->set_termios; + port.pm = p->pm; + port.dev = &dev->dev; + port.irqflags |= irqflag; + ret = serial8250_register_port(&port); + if (ret < 0) { + dev_err(&dev->dev, "unable to register port at index %d " + "(IO%lx MEM%llx IRQ%d): %d\n", i, + p->iobase, (unsigned long long)p->mapbase, + p->irq, ret); + } + } + return 0; +} + +/* + * Remove serial ports registered against a platform device. + */ +static int __devexit serial8250_remove(struct platform_device *dev) +{ + int i; + + for (i = 0; i < nr_uarts; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.dev == &dev->dev) + serial8250_unregister_port(i); + } + return 0; +} + +static int serial8250_suspend(struct platform_device *dev, pm_message_t state) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_suspend_port(&serial8250_reg, &up->port); + } + + return 0; +} + +static int serial8250_resume(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_8250_port *up = &serial8250_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + serial8250_resume_port(i); + } + + return 0; +} + +static struct platform_driver serial8250_isa_driver = { + .probe = serial8250_probe, + .remove = __devexit_p(serial8250_remove), + .suspend = serial8250_suspend, + .resume = serial8250_resume, + .driver = { + .name = "serial8250", + .owner = THIS_MODULE, + }, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices listed + * in the table in include/asm/serial.h + */ +static struct platform_device *serial8250_isa_devs; + +/* + * serial8250_register_port and serial8250_unregister_port allows for + * 16x50 serial ports to be configured at run-time, to support PCMCIA + * modems and PCI multiport cards. + */ +static DEFINE_MUTEX(serial_mutex); + +static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *port) +{ + int i; + + /* + * First, find a port entry which matches. + */ + for (i = 0; i < nr_uarts; i++) + if (uart_match_port(&serial8250_ports[i].port, port)) + return &serial8250_ports[i]; + + /* + * We didn't find a matching entry, so look for the first + * free entry. We look for one which hasn't been previously + * used (indicated by zero iobase). + */ + for (i = 0; i < nr_uarts; i++) + if (serial8250_ports[i].port.type == PORT_UNKNOWN && + serial8250_ports[i].port.iobase == 0) + return &serial8250_ports[i]; + + /* + * That also failed. Last resort is to find any entry which + * doesn't have a real port associated with it. + */ + for (i = 0; i < nr_uarts; i++) + if (serial8250_ports[i].port.type == PORT_UNKNOWN) + return &serial8250_ports[i]; + + return NULL; +} + +/** + * serial8250_register_port - register a serial port + * @port: serial port template + * + * Configure the serial port specified by the request. If the + * port exists and is in use, it is hung up and unregistered + * first. + * + * The port is then probed and if necessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +int serial8250_register_port(struct uart_port *port) +{ + struct uart_8250_port *uart; + int ret = -ENOSPC; + + if (port->uartclk == 0) + return -EINVAL; + + mutex_lock(&serial_mutex); + + uart = serial8250_find_match_or_unused(port); + if (uart) { + uart_remove_one_port(&serial8250_reg, &uart->port); + + uart->port.iobase = port->iobase; + uart->port.membase = port->membase; + uart->port.irq = port->irq; + uart->port.irqflags = port->irqflags; + uart->port.uartclk = port->uartclk; + uart->port.fifosize = port->fifosize; + uart->port.regshift = port->regshift; + uart->port.iotype = port->iotype; + uart->port.flags = port->flags | UPF_BOOT_AUTOCONF; + uart->port.mapbase = port->mapbase; + uart->port.private_data = port->private_data; + if (port->dev) + uart->port.dev = port->dev; + + if (port->flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(uart, port->type); + + set_io_from_upio(&uart->port); + /* Possibly override default I/O functions. */ + if (port->serial_in) + uart->port.serial_in = port->serial_in; + if (port->serial_out) + uart->port.serial_out = port->serial_out; + /* Possibly override set_termios call */ + if (port->set_termios) + uart->port.set_termios = port->set_termios; + if (port->pm) + uart->port.pm = port->pm; + + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); + + ret = uart_add_one_port(&serial8250_reg, &uart->port); + if (ret == 0) + ret = uart->port.line; + } + mutex_unlock(&serial_mutex); + + return ret; +} +EXPORT_SYMBOL(serial8250_register_port); + +/** + * serial8250_unregister_port - remove a 16x50 serial port at runtime + * @line: serial line number + * + * Remove one serial port. This may not be called from interrupt + * context. We hand the port back to the our control. + */ +void serial8250_unregister_port(int line) +{ + struct uart_8250_port *uart = &serial8250_ports[line]; + + mutex_lock(&serial_mutex); + uart_remove_one_port(&serial8250_reg, &uart->port); + if (serial8250_isa_devs) { + uart->port.flags &= ~UPF_BOOT_AUTOCONF; + uart->port.type = PORT_UNKNOWN; + uart->port.dev = &serial8250_isa_devs->dev; + uart_add_one_port(&serial8250_reg, &uart->port); + } else { + uart->port.dev = NULL; + } + mutex_unlock(&serial_mutex); +} +EXPORT_SYMBOL(serial8250_unregister_port); + +static int __init serial8250_init(void) +{ + int ret; + + if (nr_uarts > UART_NR) + nr_uarts = UART_NR; + + printk(KERN_INFO "Serial: 8250/16550 driver, " + "%d ports, IRQ sharing %sabled\n", nr_uarts, + share_irqs ? "en" : "dis"); + +#ifdef CONFIG_SPARC + ret = sunserial_register_minors(&serial8250_reg, UART_NR); +#else + serial8250_reg.nr = UART_NR; + ret = uart_register_driver(&serial8250_reg); +#endif + if (ret) + goto out; + + serial8250_isa_devs = platform_device_alloc("serial8250", + PLAT8250_DEV_LEGACY); + if (!serial8250_isa_devs) { + ret = -ENOMEM; + goto unreg_uart_drv; + } + + ret = platform_device_add(serial8250_isa_devs); + if (ret) + goto put_dev; + + serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev); + + ret = platform_driver_register(&serial8250_isa_driver); + if (ret == 0) + goto out; + + platform_device_del(serial8250_isa_devs); +put_dev: + platform_device_put(serial8250_isa_devs); +unreg_uart_drv: +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +out: + return ret; +} + +static void __exit serial8250_exit(void) +{ + struct platform_device *isa_dev = serial8250_isa_devs; + + /* + * This tells serial8250_unregister_port() not to re-register + * the ports (thereby making serial8250_isa_driver permanently + * in use.) + */ + serial8250_isa_devs = NULL; + + platform_driver_unregister(&serial8250_isa_driver); + platform_device_unregister(isa_dev); + +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else + uart_unregister_driver(&serial8250_reg); +#endif +} + +module_init(serial8250_init); +module_exit(serial8250_exit); + +EXPORT_SYMBOL(serial8250_suspend_port); +EXPORT_SYMBOL(serial8250_resume_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 serial driver"); + +module_param(share_irqs, uint, 0644); +MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" + " (unsafe)"); + +module_param(nr_uarts, uint, 0644); +MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); + +module_param(skip_txen_test, uint, 0644); +MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); + +#ifdef CONFIG_SERIAL_8250_RSA +module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +#endif +MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR); diff --git a/drivers/tty/serial/8250.h b/drivers/tty/serial/8250.h new file mode 100644 index 000000000000..6e19ea3e48d5 --- /dev/null +++ b/drivers/tty/serial/8250.h @@ -0,0 +1,80 @@ +/* + * linux/drivers/char/8250.h + * + * Driver for 8250/16550-type serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char hub6; + unsigned char io_type; + unsigned char *iomem_base; + unsigned short iomem_reg_shift; + unsigned long irqflags; +}; + +/* + * This replaces serial_uart_config in include/linux/serial.h + */ +struct serial8250_config { + const char *name; + unsigned short fifo_size; + unsigned short tx_loadsz; + unsigned char fcr; + unsigned int flags; +}; + +#define UART_CAP_FIFO (1 << 8) /* UART has FIFO */ +#define UART_CAP_EFR (1 << 9) /* UART has EFR */ +#define UART_CAP_SLEEP (1 << 10) /* UART has IER sleep */ +#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */ +#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ + +#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ +#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ +#define UART_BUG_NOMSR (1 << 2) /* UART has buggy MSR status bits (Au1x00) */ +#define UART_BUG_THRE (1 << 3) /* UART has buggy THRE reassertion */ + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#ifdef CONFIG_SERIAL_8250_SHARE_IRQ +#define SERIAL8250_SHARE_IRQS 1 +#else +#define SERIAL8250_SHARE_IRQS 0 +#endif + +#if defined(__alpha__) && !defined(CONFIG_PCI) +/* + * Digital did something really horribly wrong with the OUT1 and OUT2 + * lines on at least some ALPHA's. The failure mode is that if either + * is cleared, the machine locks up with endless interrupts. + */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) +#elif defined(CONFIG_SBC8560) +/* + * WindRiver did something similarly broken on their SBC8560 board. The + * UART tristates its IRQ output while OUT2 is clear, but they pulled + * the interrupt line _up_ instead of down, so if we register the IRQ + * while the UART is in that state, we die in an IRQ storm. */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) +#else +#define ALPHA_KLUDGE_MCR 0 +#endif diff --git a/drivers/tty/serial/8250_accent.c b/drivers/tty/serial/8250_accent.c new file mode 100644 index 000000000000..9c10262f2469 --- /dev/null +++ b/drivers/tty/serial/8250_accent.c @@ -0,0 +1,47 @@ +/* + * linux/drivers/serial/8250_accent.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port accent_data[] = { + PORT(0x330, 4), + PORT(0x338, 4), + { }, +}; + +static struct platform_device accent_device = { + .name = "serial8250", + .id = PLAT8250_DEV_ACCENT, + .dev = { + .platform_data = accent_data, + }, +}; + +static int __init accent_init(void) +{ + return platform_device_register(&accent_device); +} + +module_init(accent_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_acorn.c b/drivers/tty/serial/8250_acorn.c new file mode 100644 index 000000000000..b0ce8c56f1a4 --- /dev/null +++ b/drivers/tty/serial/8250_acorn.c @@ -0,0 +1,141 @@ +/* + * linux/drivers/serial/acorn.c + * + * Copyright (C) 1996-2003 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "8250.h" + +#define MAX_PORTS 3 + +struct serial_card_type { + unsigned int num_ports; + unsigned int uartclk; + unsigned int type; + unsigned int offset[MAX_PORTS]; +}; + +struct serial_card_info { + unsigned int num_ports; + int ports[MAX_PORTS]; + void __iomem *vaddr; +}; + +static int __devinit +serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct serial_card_info *info; + struct serial_card_type *type = id->data; + struct uart_port port; + unsigned long bus_addr; + unsigned int i; + + info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->num_ports = type->num_ports; + + bus_addr = ecard_resource_start(ec, type->type); + info->vaddr = ecardm_iomap(ec, type->type, 0, 0); + if (!info->vaddr) { + kfree(info); + return -ENOMEM; + } + + ecard_set_drvdata(ec, info); + + memset(&port, 0, sizeof(struct uart_port)); + port.irq = ec->irq; + port.flags = UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + port.uartclk = type->uartclk; + port.iotype = UPIO_MEM; + port.regshift = 2; + port.dev = &ec->dev; + + for (i = 0; i < info->num_ports; i ++) { + port.membase = info->vaddr + type->offset[i]; + port.mapbase = bus_addr + type->offset[i]; + + info->ports[i] = serial8250_register_port(&port); + } + + return 0; +} + +static void __devexit serial_card_remove(struct expansion_card *ec) +{ + struct serial_card_info *info = ecard_get_drvdata(ec); + int i; + + ecard_set_drvdata(ec, NULL); + + for (i = 0; i < info->num_ports; i++) + if (info->ports[i] > 0) + serial8250_unregister_port(info->ports[i]); + + kfree(info); +} + +static struct serial_card_type atomwide_type = { + .num_ports = 3, + .uartclk = 7372800, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2800, 0x2400, 0x2000 }, +}; + +static struct serial_card_type serport_type = { + .num_ports = 2, + .uartclk = 3686400, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2000, 0x2020 }, +}; + +static const struct ecard_id serial_cids[] = { + { MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL, &atomwide_type }, + { MANU_SERPORT, PROD_SERPORT_DSPORT, &serport_type }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver serial_card_driver = { + .probe = serial_card_probe, + .remove = __devexit_p(serial_card_remove), + .id_table = serial_cids, + .drv = { + .name = "8250_acorn", + }, +}; + +static int __init serial_card_init(void) +{ + return ecard_register_driver(&serial_card_driver); +} + +static void __exit serial_card_exit(void) +{ + ecard_remove_driver(&serial_card_driver); +} + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); +MODULE_LICENSE("GPL"); + +module_init(serial_card_init); +module_exit(serial_card_exit); diff --git a/drivers/tty/serial/8250_boca.c b/drivers/tty/serial/8250_boca.c new file mode 100644 index 000000000000..3bfe0f7b26fb --- /dev/null +++ b/drivers/tty/serial/8250_boca.c @@ -0,0 +1,61 @@ +/* + * linux/drivers/serial/8250_boca.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port boca_data[] = { + PORT(0x100, 12), + PORT(0x108, 12), + PORT(0x110, 12), + PORT(0x118, 12), + PORT(0x120, 12), + PORT(0x128, 12), + PORT(0x130, 12), + PORT(0x138, 12), + PORT(0x140, 12), + PORT(0x148, 12), + PORT(0x150, 12), + PORT(0x158, 12), + PORT(0x160, 12), + PORT(0x168, 12), + PORT(0x170, 12), + PORT(0x178, 12), + { }, +}; + +static struct platform_device boca_device = { + .name = "serial8250", + .id = PLAT8250_DEV_BOCA, + .dev = { + .platform_data = boca_data, + }, +}; + +static int __init boca_init(void) +{ + return platform_device_register(&boca_device); +} + +module_init(boca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Boca cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_early.c b/drivers/tty/serial/8250_early.c new file mode 100644 index 000000000000..eaafb98debed --- /dev/null +++ b/drivers/tty/serial/8250_early.c @@ -0,0 +1,287 @@ +/* + * Early serial console for 8250/16550 devices + * + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + * + * 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. + * + * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, + * and on early_printk.c by Andi Kleen. + * + * This is for use before the serial driver has initialized, in + * particular, before the UARTs have been discovered and named. + * Instead of specifying the console device as, e.g., "ttyS0", + * we locate the device directly by its MMIO or I/O port address. + * + * The user can specify the device directly, e.g., + * earlycon=uart8250,io,0x3f8,9600n8 + * earlycon=uart8250,mmio,0xff5e0000,115200n8 + * earlycon=uart8250,mmio32,0xff5e0000,115200n8 + * or + * console=uart8250,io,0x3f8,9600n8 + * console=uart8250,mmio,0xff5e0000,115200n8 + * console=uart8250,mmio32,0xff5e0000,115200n8 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FIX_EARLYCON_MEM +#include +#include +#endif + +struct early_serial8250_device { + struct uart_port port; + char options[16]; /* e.g., 115200n8 */ + unsigned int baud; +}; + +static struct early_serial8250_device early_device; + +static unsigned int __init serial_in(struct uart_port *port, int offset) +{ + switch (port->iotype) { + case UPIO_MEM: + return readb(port->membase + offset); + case UPIO_MEM32: + return readl(port->membase + (offset << 2)); + case UPIO_PORT: + return inb(port->iobase + offset); + default: + return 0; + } +} + +static void __init serial_out(struct uart_port *port, int offset, int value) +{ + switch (port->iotype) { + case UPIO_MEM: + writeb(value, port->membase + offset); + break; + case UPIO_MEM32: + writel(value, port->membase + (offset << 2)); + break; + case UPIO_PORT: + outb(value, port->iobase + offset); + break; + } +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static void __init wait_for_xmitr(struct uart_port *port) +{ + unsigned int status; + + for (;;) { + status = serial_in(port, UART_LSR); + if ((status & BOTH_EMPTY) == BOTH_EMPTY) + return; + cpu_relax(); + } +} + +static void __init serial_putc(struct uart_port *port, int c) +{ + wait_for_xmitr(port); + serial_out(port, UART_TX, c); +} + +static void __init early_serial8250_write(struct console *console, + const char *s, unsigned int count) +{ + struct uart_port *port = &early_device.port; + unsigned int ier; + + /* Save the IER and disable interrupts */ + ier = serial_in(port, UART_IER); + serial_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); +} + +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); + + quot = (dlm << 8) | dll; + return (port->uartclk / 16) / quot; +} + +static void __init init_port(struct early_serial8250_device *device) +{ + struct uart_port *port = &device->port; + 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 */ + + divisor = 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); +} + +static int __init parse_options(struct early_serial8250_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length; + + if (!options) + return -ENODEV; + + port->uartclk = BASE_BAUD * 16; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), + &options, 0); + if (mmio32) + port->regshift = 2; +#ifdef CONFIG_FIX_EARLYCON_MEM + set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, + port->mapbase & PAGE_MASK); + port->membase = + (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + port->membase += port->mapbase & ~PAGE_MASK; +#else + port->membase = ioremap_nocache(port->mapbase, 64); + if (!port->membase) { + printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", + __func__, + (unsigned long long) port->mapbase); + return -ENOMEM; + } +#endif + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + port->iobase = simple_strtoul(options + 3, &options, 0); + mmio = 0; + } else + return -EINVAL; + + options = strchr(options, ','); + if (options) { + options++; + device->baud = simple_strtoul(options, NULL, 0); + length = min(strcspn(options, " "), sizeof(device->options)); + strncpy(device->options, options, length); + } else { + device->baud = probe_baud(port); + snprintf(device->options, sizeof(device->options), "%u", + device->baud); + } + + if (mmio || mmio32) + printk(KERN_INFO + "Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (unsigned long long)port->mapbase, + device->options); + else + printk(KERN_INFO + "Early serial console at I/O port 0x%lx (options '%s')\n", + port->iobase, + device->options); + + return 0; +} + +static struct console early_serial8250_console __initdata = { + .name = "uart", + .write = early_serial8250_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static int __init early_serial8250_setup(char *options) +{ + struct early_serial8250_device *device = &early_device; + int err; + + if (device->port.membase || device->port.iobase) + return 0; + + err = parse_options(device, options); + if (err < 0) + return err; + + init_port(device); + return 0; +} + +int __init setup_early_serial8250_console(char *cmdline) +{ + char *options; + int err; + + options = strstr(cmdline, "uart8250,"); + if (!options) { + options = strstr(cmdline, "uart,"); + if (!options) + return 0; + } + + options = strchr(cmdline, ',') + 1; + err = early_serial8250_setup(options); + if (err < 0) + return err; + + register_console(&early_serial8250_console); + + return 0; +} + +int serial8250_find_port_for_earlycon(void) +{ + struct early_serial8250_device *device = &early_device; + struct uart_port *port = &device->port; + int line; + int ret; + + if (!device->port.membase && !device->port.iobase) + return -ENODEV; + + line = serial8250_find_port(port); + if (line < 0) + return -ENODEV; + + ret = update_console_cmdline("uart", 8250, + "ttyS", line, device->options); + if (ret < 0) + ret = update_console_cmdline("uart", 0, + "ttyS", line, device->options); + + return ret; +} + +early_param("earlycon", setup_early_serial8250_console); diff --git a/drivers/tty/serial/8250_exar_st16c554.c b/drivers/tty/serial/8250_exar_st16c554.c new file mode 100644 index 000000000000..567143ace159 --- /dev/null +++ b/drivers/tty/serial/8250_exar_st16c554.c @@ -0,0 +1,52 @@ +/* + * linux/drivers/serial/8250_exar.c + * + * Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com > + * Based on 8250_boca. + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF, \ + } + +static struct plat_serial8250_port exar_data[] = { + PORT(0x100, 5), + PORT(0x108, 5), + PORT(0x110, 5), + PORT(0x118, 5), + { }, +}; + +static struct platform_device exar_device = { + .name = "serial8250", + .id = PLAT8250_DEV_EXAR_ST16C554, + .dev = { + .platform_data = exar_data, + }, +}; + +static int __init exar_init(void) +{ + return platform_device_register(&exar_device); +} + +module_init(exar_init); + +MODULE_AUTHOR("Paul B Schroeder"); +MODULE_DESCRIPTION("8250 serial probe module for Exar cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_fourport.c b/drivers/tty/serial/8250_fourport.c new file mode 100644 index 000000000000..6375d68b7913 --- /dev/null +++ b/drivers/tty/serial/8250_fourport.c @@ -0,0 +1,53 @@ +/* + * linux/drivers/serial/8250_fourport.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = UPF_BOOT_AUTOCONF | UPF_FOURPORT, \ + } + +static struct plat_serial8250_port fourport_data[] = { + PORT(0x1a0, 9), + PORT(0x1a8, 9), + PORT(0x1b0, 9), + PORT(0x1b8, 9), + PORT(0x2a0, 5), + PORT(0x2a8, 5), + PORT(0x2b0, 5), + PORT(0x2b8, 5), + { }, +}; + +static struct platform_device fourport_device = { + .name = "serial8250", + .id = PLAT8250_DEV_FOURPORT, + .dev = { + .platform_data = fourport_data, + }, +}; + +static int __init fourport_init(void) +{ + return platform_device_register(&fourport_device); +} + +module_init(fourport_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_gsc.c b/drivers/tty/serial/8250_gsc.c new file mode 100644 index 000000000000..d8c0ffbfa6e3 --- /dev/null +++ b/drivers/tty/serial/8250_gsc.c @@ -0,0 +1,122 @@ +/* + * Serial Device Initialisation for Lasi/Asp/Wax/Dino + * + * (c) Copyright Matthew Wilcox 2001-2002 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "8250.h" + +static int __init serial_init_chip(struct parisc_device *dev) +{ + struct uart_port port; + unsigned long address; + int err; + + if (!dev->irq) { + /* We find some unattached serial ports by walking native + * busses. These should be silently ignored. Otherwise, + * what we have here is a missing parent device, so tell + * the user what they're missing. + */ + if (parisc_parent(dev)->id.hw_type != HPHW_IOA) + printk(KERN_INFO + "Serial: device 0x%llx not configured.\n" + "Enable support for Wax, Lasi, Asp or Dino.\n", + (unsigned long long)dev->hpa.start); + return -ENODEV; + } + + address = dev->hpa.start; + if (dev->id.sversion != 0x8d) + address += 0x800; + + memset(&port, 0, sizeof(port)); + port.iotype = UPIO_MEM; + /* 7.272727MHz on Lasi. Assumed the same for Dino, Wax and Timi. */ + port.uartclk = 7272727; + port.mapbase = address; + port.membase = ioremap_nocache(address, 16); + port.irq = dev->irq; + port.flags = UPF_BOOT_AUTOCONF; + port.dev = &dev->dev; + + err = serial8250_register_port(&port); + if (err < 0) { + printk(KERN_WARNING + "serial8250_register_port returned error %d\n", err); + iounmap(port.membase); + return err; + } + + return 0; +} + +static struct parisc_device_id serial_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c }, + { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d }, + { 0 } +}; + +/* Hack. Some machines have SERIAL_0 attached to Lasi and SERIAL_1 + * attached to Dino. Unfortunately, Dino appears before Lasi in the device + * tree. To ensure that ttyS0 == SERIAL_0, we register two drivers; one + * which only knows about Lasi and then a second which will find all the + * other serial ports. HPUX ignores this problem. + */ +static struct parisc_device_id lasi_tbl[] = { + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */ + { HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */ + { 0 } +}; + + +MODULE_DEVICE_TABLE(parisc, serial_tbl); + +static struct parisc_driver lasi_driver = { + .name = "serial_1", + .id_table = lasi_tbl, + .probe = serial_init_chip, +}; + +static struct parisc_driver serial_driver = { + .name = "serial", + .id_table = serial_tbl, + .probe = serial_init_chip, +}; + +static int __init probe_serial_gsc(void) +{ + register_parisc_driver(&lasi_driver); + register_parisc_driver(&serial_driver); + return 0; +} + +module_init(probe_serial_gsc); + +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_hp300.c b/drivers/tty/serial/8250_hp300.c new file mode 100644 index 000000000000..c13438c93012 --- /dev/null +++ b/drivers/tty/serial/8250_hp300.c @@ -0,0 +1,327 @@ +/* + * Driver for the 98626/98644/internal serial interface on hp300/hp400 + * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs) + * + * Ported from 2.2 and modified to use the normal 8250 driver + * by Kars de Jong , May 2004. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "8250.h" + +#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI) +#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure? +#endif + +#ifdef CONFIG_HPAPCI +struct hp300_port +{ + struct hp300_port *next; /* next port */ + int line; /* line (tty) number */ +}; + +static struct hp300_port *hp300_ports; +#endif + +#ifdef CONFIG_HPDCA + +static int __devinit hpdca_init_one(struct dio_dev *d, + const struct dio_device_id *ent); +static void __devexit hpdca_remove_one(struct dio_dev *d); + +static struct dio_device_id hpdca_dio_tbl[] = { + { DIO_ID_DCA0 }, + { DIO_ID_DCA0REM }, + { DIO_ID_DCA1 }, + { DIO_ID_DCA1REM }, + { 0 } +}; + +static struct dio_driver hpdca_driver = { + .name = "hpdca", + .id_table = hpdca_dio_tbl, + .probe = hpdca_init_one, + .remove = __devexit_p(hpdca_remove_one), +}; + +#endif + +static unsigned int num_ports; + +extern int hp300_uart_scode; + +/* Offset to UART registers from base of DCA */ +#define UART_OFFSET 17 + +#define DCA_ID 0x01 /* ID (read), reset (write) */ +#define DCA_IC 0x03 /* Interrupt control */ + +/* Interrupt control */ +#define DCA_IC_IE 0x80 /* Master interrupt enable */ + +#define HPDCA_BAUD_BASE 153600 + +/* Base address of the Frodo part */ +#define FRODO_BASE (0x41c000) + +/* + * Where we find the 8250-like APCI ports, and how far apart they are. + */ +#define FRODO_APCIBASE 0x0 +#define FRODO_APCISPACE 0x20 +#define FRODO_APCI_OFFSET(x) (FRODO_APCIBASE + ((x) * FRODO_APCISPACE)) + +#define HPAPCI_BAUD_BASE 500400 + +#ifdef CONFIG_SERIAL_8250_CONSOLE +/* + * Parse the bootinfo to find descriptions for headless console and + * debug serial ports and register them with the 8250 driver. + * This function should be called before serial_console_init() is called + * to make sure the serial console will be available for use. IA-64 kernel + * calls this function from setup_arch() after the EFI and ACPI tables have + * been parsed. + */ +int __init hp300_setup_serial_console(void) +{ + int scode; + struct uart_port port; + + memset(&port, 0, sizeof(port)); + + if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX) + return 0; + + if (DIO_SCINHOLE(hp300_uart_scode)) + return 0; + + scode = hp300_uart_scode; + + /* Memory mapped I/O */ + port.iotype = UPIO_MEM; + port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; + port.type = PORT_UNKNOWN; + + /* Check for APCI console */ + if (scode == 256) { +#ifdef CONFIG_HPAPCI + printk(KERN_INFO "Serial console is HP APCI 1\n"); + + port.uartclk = HPAPCI_BAUD_BASE * 16; + port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1)); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 2; + add_preferred_console("ttyS", port.line, "9600n8"); +#else + printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n"); + return 0; +#endif + } else { +#ifdef CONFIG_HPDCA + unsigned long pa = dio_scodetophysaddr(scode); + if (!pa) + return 0; + + printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode); + + port.uartclk = HPDCA_BAUD_BASE * 16; + port.mapbase = (pa + UART_OFFSET); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 1; + port.irq = DIO_IPL(pa + DIO_VIRADDRBASE); + + /* Enable board-interrupts */ + out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); + + if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80) + add_preferred_console("ttyS", port.line, "9600n8"); +#else + printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n"); + return 0; +#endif + } + + if (early_serial_setup(&port) < 0) + printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n"); + return 0; +} +#endif /* CONFIG_SERIAL_8250_CONSOLE */ + +#ifdef CONFIG_HPDCA +static int __devinit hpdca_init_one(struct dio_dev *d, + const struct dio_device_id *ent) +{ + struct uart_port port; + int line; + +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (hp300_uart_scode == d->scode) { + /* Already got it. */ + return 0; + } +#endif + memset(&port, 0, sizeof(struct uart_port)); + + /* Memory mapped I/O */ + port.iotype = UPIO_MEM; + port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF; + port.irq = d->ipl; + port.uartclk = HPDCA_BAUD_BASE * 16; + port.mapbase = (d->resource.start + UART_OFFSET); + port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE); + port.regshift = 1; + port.dev = &d->dev; + line = serial8250_register_port(&port); + + if (line < 0) { + printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d" + " irq %d failed\n", d->scode, port.irq); + return -ENOMEM; + } + + /* Enable board-interrupts */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE); + dio_set_drvdata(d, (void *)line); + + /* Reset the DCA */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff); + udelay(100); + + num_ports++; + + return 0; +} +#endif + +static int __init hp300_8250_init(void) +{ + static int called; +#ifdef CONFIG_HPAPCI + int line; + unsigned long base; + struct uart_port uport; + struct hp300_port *port; + int i; +#endif + if (called) + return -ENODEV; + called = 1; + + if (!MACH_IS_HP300) + return -ENODEV; + +#ifdef CONFIG_HPDCA + dio_register_driver(&hpdca_driver); +#endif +#ifdef CONFIG_HPAPCI + if (hp300_model < HP_400) { + if (!num_ports) + return -ENODEV; + return 0; + } + /* These models have the Frodo chip. + * Port 0 is reserved for the Apollo Domain keyboard. + * Port 1 is either the console or the DCA. + */ + for (i = 1; i < 4; i++) { + /* Port 1 is the console on a 425e, on other machines it's + * mapped to DCA. + */ +#ifdef CONFIG_SERIAL_8250_CONSOLE + if (i == 1) + continue; +#endif + + /* Create new serial device */ + port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + memset(&uport, 0, sizeof(struct uart_port)); + + base = (FRODO_BASE + FRODO_APCI_OFFSET(i)); + + /* Memory mapped I/O */ + uport.iotype = UPIO_MEM; + uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \ + | UPF_BOOT_AUTOCONF; + /* XXX - no interrupt support yet */ + uport.irq = 0; + uport.uartclk = HPAPCI_BAUD_BASE * 16; + uport.mapbase = base; + uport.membase = (char *)(base + DIO_VIRADDRBASE); + uport.regshift = 2; + + line = serial8250_register_port(&uport); + + if (line < 0) { + printk(KERN_NOTICE "8250_hp300: register_serial() APCI" + " %d irq %d failed\n", i, uport.irq); + kfree(port); + continue; + } + + port->line = line; + port->next = hp300_ports; + hp300_ports = port; + + num_ports++; + } +#endif + + /* Any boards found? */ + if (!num_ports) + return -ENODEV; + + return 0; +} + +#ifdef CONFIG_HPDCA +static void __devexit hpdca_remove_one(struct dio_dev *d) +{ + int line; + + line = (int) dio_get_drvdata(d); + if (d->resource.start) { + /* Disable board-interrupts */ + out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0); + } + serial8250_unregister_port(line); +} +#endif + +static void __exit hp300_8250_exit(void) +{ +#ifdef CONFIG_HPAPCI + struct hp300_port *port, *to_free; + + for (port = hp300_ports; port; ) { + serial8250_unregister_port(port->line); + to_free = port; + port = port->next; + kfree(to_free); + } + + hp300_ports = NULL; +#endif +#ifdef CONFIG_HPDCA + dio_unregister_driver(&hpdca_driver); +#endif +} + +module_init(hp300_8250_init); +module_exit(hp300_8250_exit); +MODULE_DESCRIPTION("HP DCA/APCI serial driver"); +MODULE_AUTHOR("Kars de Jong "); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_hub6.c b/drivers/tty/serial/8250_hub6.c new file mode 100644 index 000000000000..7609150e7d5e --- /dev/null +++ b/drivers/tty/serial/8250_hub6.c @@ -0,0 +1,58 @@ +/* + * linux/drivers/serial/8250_hub6.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include + +#define HUB6(card,port) \ + { \ + .iobase = 0x302, \ + .irq = 3, \ + .uartclk = 1843200, \ + .iotype = UPIO_HUB6, \ + .flags = UPF_BOOT_AUTOCONF, \ + .hub6 = (card) << 6 | (port) << 3 | 1, \ + } + +static struct plat_serial8250_port hub6_data[] = { + HUB6(0, 0), + HUB6(0, 1), + HUB6(0, 2), + HUB6(0, 3), + HUB6(0, 4), + HUB6(0, 5), + HUB6(1, 0), + HUB6(1, 1), + HUB6(1, 2), + HUB6(1, 3), + HUB6(1, 4), + HUB6(1, 5), + { }, +}; + +static struct platform_device hub6_device = { + .name = "serial8250", + .id = PLAT8250_DEV_HUB6, + .dev = { + .platform_data = hub6_data, + }, +}; + +static int __init hub6_init(void) +{ + return platform_device_register(&hub6_device); +} + +module_init(hub6_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_mca.c b/drivers/tty/serial/8250_mca.c new file mode 100644 index 000000000000..d10be944ad44 --- /dev/null +++ b/drivers/tty/serial/8250_mca.c @@ -0,0 +1,63 @@ +/* + * linux/drivers/serial/8250_mca.c + * + * Copyright (C) 2005 Russell King. + * Data taken from include/asm-i386/serial.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +/* + * FIXME: Should we be doing AUTO_IRQ here? + */ +#ifdef CONFIG_SERIAL_8250_DETECT_IRQ +#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ +#else +#define MCA_FLAGS UPF_BOOT_AUTOCONF | UPF_SKIP_TEST +#endif + +#define PORT(_base,_irq) \ + { \ + .iobase = _base, \ + .irq = _irq, \ + .uartclk = 1843200, \ + .iotype = UPIO_PORT, \ + .flags = MCA_FLAGS, \ + } + +static struct plat_serial8250_port mca_data[] = { + PORT(0x3220, 3), + PORT(0x3228, 3), + PORT(0x4220, 3), + PORT(0x4228, 3), + PORT(0x5220, 3), + PORT(0x5228, 3), + { }, +}; + +static struct platform_device mca_device = { + .name = "serial8250", + .id = PLAT8250_DEV_MCA, + .dev = { + .platform_data = mca_data, + }, +}; + +static int __init mca_init(void) +{ + if (!MCA_bus) + return -ENODEV; + return platform_device_register(&mca_device); +} + +module_init(mca_init); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("8250 serial probe module for MCA ports"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/8250_pci.c b/drivers/tty/serial/8250_pci.c new file mode 100644 index 000000000000..8b8930f700b5 --- /dev/null +++ b/drivers/tty/serial/8250_pci.c @@ -0,0 +1,3850 @@ +/* + * linux/drivers/char/8250_pci.c + * + * Probe module for 8250/16550-type PCI serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "8250.h" + +#undef SERIAL_DEBUG_PCI + +/* + * init function returns: + * > 0 - number of ports + * = 0 - use board->num_ports + * < 0 - error + */ +struct pci_serial_quirk { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; + int (*init)(struct pci_dev *dev); + int (*setup)(struct serial_private *, + const struct pciserial_board *, + struct uart_port *, int); + void (*exit)(struct pci_dev *dev); +}; + +#define PCI_NUM_BAR_RESOURCES 6 + +struct serial_private { + struct pci_dev *dev; + unsigned int nr; + void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES]; + struct pci_serial_quirk *quirk; + int line[0]; +}; + +static void moan_device(const char *str, struct pci_dev *dev) +{ + printk(KERN_WARNING + "%s: %s\n" + "Please send the output of lspci -vv, this\n" + "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + "manufacturer and name of serial board or\n" + "modem board to rmk+serial@arm.linux.org.uk.\n", + pci_name(dev), str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + +static int +setup_port(struct serial_private *priv, struct uart_port *port, + int bar, int offset, int regshift) +{ + struct pci_dev *dev = priv->dev; + unsigned long base, len; + + if (bar >= PCI_NUM_BAR_RESOURCES) + return -EINVAL; + + base = pci_resource_start(dev, bar); + + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + len = pci_resource_len(dev, bar); + + if (!priv->remapped_bar[bar]) + priv->remapped_bar[bar] = ioremap_nocache(base, len); + if (!priv->remapped_bar[bar]) + return -ENOMEM; + + port->iotype = UPIO_MEM; + port->iobase = 0; + port->mapbase = base + offset; + port->membase = priv->remapped_bar[bar] + offset; + port->regshift = regshift; + } else { + port->iotype = UPIO_PORT; + port->iobase = base + offset; + port->mapbase = 0; + port->membase = NULL; + port->regshift = 0; + } + return 0; +} + +/* + * ADDI-DATA GmbH communication cards + */ +static int addidata_apci7800_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar = 0, offset = board->first_offset; + bar = FL_GET_BASE(board->flags); + + if (idx < 2) { + offset += idx * board->uart_offset; + } else if ((idx >= 2) && (idx < 4)) { + bar += 1; + offset += ((idx - 2) * board->uart_offset); + } else if ((idx >= 4) && (idx < 6)) { + bar += 2; + offset += ((idx - 4) * board->uart_offset); + } else if (idx >= 6) { + bar += 3; + offset += ((idx - 6) * board->uart_offset); + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * AFAVLAB uses a different mixture of BARs and offsets + * Not that ugly ;) -- HW + */ +static int +afavlab_setup(struct serial_private *priv, const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = FL_GET_BASE(board->flags); + if (idx < 4) + bar += idx; + else { + bar = 4; + offset += (idx - 4) * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * HP's Remote Management Console. The Diva chip came in several + * different versions. N-class, L2000 and A500 have two Diva chips, each + * with 3 UARTs (the third UART on the second chip is unused). Superdome + * and Keystone have one Diva chip with 3 UARTs. Some later machines have + * one Diva chip, but it has been expanded to 5 UARTs. + */ +static int pci_hp_diva_init(struct pci_dev *dev) +{ + int rc = 0; + + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + rc = 3; + break; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + rc = 2; + break; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + rc = 4; + break; + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + case PCI_DEVICE_ID_HP_DIVA_HURRICANE: + rc = 1; + break; + } + + return rc; +} + +/* + * HP's Diva chip puts the 4th/5th serial port further out, and + * some serial ports are supposed to be hidden on certain models. + */ +static int +pci_hp_diva_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int offset = board->first_offset; + unsigned int bar = FL_GET_BASE(board->flags); + + switch (priv->dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + if (idx == 3) + idx++; + break; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + if (idx > 0) + idx++; + if (idx > 2) + idx++; + break; + } + if (idx > 2) + offset = 0x18; + + offset += idx * board->uart_offset; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * Added for EKF Intel i960 serial boards + */ +static int pci_inteli960ni_init(struct pci_dev *dev) +{ + unsigned long oldval; + + if (!(dev->subsystem_device & 0x1000)) + return -ENODEV; + + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void *)&oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return -ENODEV; + } + return 0; +} + +/* + * Some PCI serial cards using the PLX 9050 PCI interface chip require + * that the card interrupt be explicitly enabled or disabled. This + * seems to be mainly needed on card using the PLX which also use I/O + * mapped memory. + */ +static int pci_plx9050_init(struct pci_dev *dev) +{ + u8 irq_config; + void __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM || + dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS) + irq_config = 0x43; + + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; + /* + * enable/disable interrupts + */ + p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + writel(irq_config, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + + return 0; +} + +static void __devexit pci_plx9050_exit(struct pci_dev *dev) +{ + u8 __iomem *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) + return; + + /* + * disable interrupts + */ + p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); + if (p != NULL) { + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + } +} + +#define NI8420_INT_ENABLE_REG 0x38 +#define NI8420_INT_ENABLE_BIT 0x2000 + +static void __devexit pci_ni8420_exit(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return; + + /* Disable the CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT), + p + NI8420_INT_ENABLE_REG); + iounmap(p); +} + + +/* MITE registers */ +#define MITE_IOWBSR1 0xc4 +#define MITE_IOWCR1 0xf4 +#define MITE_LCIMR1 0x08 +#define MITE_LCIMR2 0x10 + +#define MITE_LCIMR2_CLR_CPU_IE (1 << 30) + +static void __devexit pci_ni8430_exit(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return; + + /* Disable the CPU Interrupt */ + writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2); + iounmap(p); +} + +/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */ +static int +sbs_setup(struct serial_private *priv, const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = 0; + + if (idx < 4) { + /* first four channels map to 0, 0x100, 0x200, 0x300 */ + offset += idx * board->uart_offset; + } else if (idx < 8) { + /* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */ + offset += idx * board->uart_offset + 0xC00; + } else /* we have only 8 ports on PMC-OCTALPRO */ + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* +* This does initialization for PMC OCTALPRO cards: +* maps the device memory, resets the UARTs (needed, bc +* if the module is removed and inserted again, the card +* is in the sleep mode) and enables global interrupt. +*/ + +/* global control register offset for SBS PMC-OctalPro */ +#define OCT_REG_CR_OFF 0x500 + +static int sbs_init(struct pci_dev *dev) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(dev, 0); + + if (p == NULL) + return -ENOMEM; + /* Set bit-4 Control Register (UART RESET) in to reset the uarts */ + writeb(0x10, p + OCT_REG_CR_OFF); + udelay(50); + writeb(0x0, p + OCT_REG_CR_OFF); + + /* Set bit-2 (INTENABLE) of Control Register */ + writeb(0x4, p + OCT_REG_CR_OFF); + iounmap(p); + + return 0; +} + +/* + * Disables the global interrupt of PMC-OctalPro + */ + +static void __devexit sbs_exit(struct pci_dev *dev) +{ + u8 __iomem *p; + + p = pci_ioremap_bar(dev, 0); + /* FIXME: What if resource_len < OCT_REG_CR_OFF */ + if (p != NULL) + writeb(0, p + OCT_REG_CR_OFF); + iounmap(p); +} + +/* + * SIIG serial cards have an PCI interface chip which also controls + * the UART clocking frequency. Each UART can be clocked independently + * (except cards equiped with 4 UARTs) and initial clocking settings + * are stored in the EEPROM chip. It can cause problems because this + * version of serial driver doesn't support differently clocked UART's + * on single PCI card. To prevent this, initialization functions set + * high frequency clocking for all UART's on given card. It is safe (I + * hope) because it doesn't touch EEPROM settings to prevent conflicts + * with other OSes (like M$ DOS). + * + * SIIG support added by Andrey Panin , 10/1999 + * + * There is two family of SIIG serial cards with different PCI + * interface chip and different configuration methods: + * - 10x cards have control registers in IO and/or memory space; + * - 20x cards have control registers in standard PCI configuration space. + * + * Note: all 10x cards have PCI device ids 0x10.. + * all 20x cards have PCI device ids 0x20.. + * + * There are also Quartet Serial cards which use Oxford Semiconductor + * 16954 quad UART PCI chip clocked by 18.432 MHz quartz. + * + * Note: some SIIG cards are probed by the parport_serial object. + */ + +#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) + +static int pci_siig10x_init(struct pci_dev *dev) +{ + u16 data; + void __iomem *p; + + switch (dev->device & 0xfff8) { + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; + } + + p = ioremap_nocache(pci_resource_start(dev, 0), 0x80); + if (p == NULL) + return -ENOMEM; + + writew(readw(p + 0x28) & data, p + 0x28); + readw(p + 0x28); + iounmap(p); + return 0; +} + +#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) +#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) + +static int pci_siig20x_init(struct pci_dev *dev) +{ + u8 data; + + /* Change clock frequency for the first UART. */ + pci_read_config_byte(dev, 0x6f, &data); + pci_write_config_byte(dev, 0x6f, data & 0xef); + + /* If this card has 2 UART, we have to do the same with second UART. */ + if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) || + ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) { + pci_read_config_byte(dev, 0x73, &data); + pci_write_config_byte(dev, 0x73, data & 0xef); + } + return 0; +} + +static int pci_siig_init(struct pci_dev *dev) +{ + unsigned int type = dev->device & 0xff00; + + if (type == 0x1000) + return pci_siig10x_init(dev); + else if (type == 0x2000) + return pci_siig20x_init(dev); + + moan_device("Unknown SIIG card", dev); + return -ENODEV; +} + +static int pci_siig_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0; + + if (idx > 3) { + bar = 4; + offset = (idx - 4) * 8; + } + + return setup_port(priv, port, bar, offset, 0); +} + +/* + * Timedia has an explosion of boards, and to avoid the PCI table from + * growing *huge*, we use this function to collapse some 70 entries + * in the PCI table into one, for sanity's and compactness's sake. + */ +static const unsigned short timedia_single_port[] = { + 0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 +}; + +static const unsigned short timedia_dual_port[] = { + 0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085, + 0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, + 0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, + 0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079, + 0xD079, 0 +}; + +static const unsigned short timedia_quad_port[] = { + 0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, + 0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, + 0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056, + 0xB157, 0 +}; + +static const unsigned short timedia_eight_port[] = { + 0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, + 0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 +}; + +static const struct timedia_struct { + int num; + const unsigned short *ids; +} timedia_data[] = { + { 1, timedia_single_port }, + { 2, timedia_dual_port }, + { 4, timedia_quad_port }, + { 8, timedia_eight_port } +}; + +static int pci_timedia_init(struct pci_dev *dev) +{ + const unsigned short *ids; + int i, j; + + for (i = 0; i < ARRAY_SIZE(timedia_data); i++) { + ids = timedia_data[i].ids; + for (j = 0; ids[j]; j++) + if (dev->subsystem_device == ids[j]) + return timedia_data[i].num; + } + return 0; +} + +/* + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT + */ +static int +pci_timedia_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar = 0, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 0; + break; + case 1: + offset = board->uart_offset; + bar = 0; + break; + case 2: + bar = 1; + break; + case 3: + offset = board->uart_offset; + /* FALLTHROUGH */ + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + bar = idx - 2; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +/* + * Some Titan cards are also a little weird + */ +static int +titan_400l_800l_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 1; + break; + case 1: + bar = 2; + break; + default: + bar = 4; + offset = (idx - 2) * board->uart_offset; + } + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int pci_xircom_init(struct pci_dev *dev) +{ + msleep(100); + return 0; +} + +static int pci_ni8420_init(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return 0; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return -ENOMEM; + + /* Enable CPU Interrupt */ + writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT, + p + NI8420_INT_ENABLE_REG); + + iounmap(p); + return 0; +} + +#define MITE_IOWBSR1_WSIZE 0xa +#define MITE_IOWBSR1_WIN_OFFSET 0x800 +#define MITE_IOWBSR1_WENAB (1 << 7) +#define MITE_LCIMR1_IO_IE_0 (1 << 24) +#define MITE_LCIMR2_SET_CPU_IE (1 << 31) +#define MITE_IOWCR1_RAMSEL_MASK 0xfffffffe + +static int pci_ni8430_init(struct pci_dev *dev) +{ + void __iomem *p; + unsigned long base, len; + u32 device_window; + unsigned int bar = 0; + + if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar", dev); + return 0; + } + + base = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); + p = ioremap_nocache(base, len); + if (p == NULL) + return -ENOMEM; + + /* Set device window address and size in BAR0 */ + device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00) + | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE; + writel(device_window, p + MITE_IOWBSR1); + + /* Set window access to go to RAMSEL IO address space */ + writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK), + p + MITE_IOWCR1); + + /* Enable IO Bus Interrupt 0 */ + writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1); + + /* Enable CPU Interrupt */ + writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2); + + iounmap(p); + return 0; +} + +/* UART Port Control Register */ +#define NI8430_PORTCON 0x0f +#define NI8430_PORTCON_TXVR_ENABLE (1 << 3) + +static int +pci_ni8430_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + void __iomem *p; + unsigned long base, len; + unsigned int bar, offset = board->first_offset; + + if (idx >= board->num_ports) + return 1; + + bar = FL_GET_BASE(board->flags); + offset += idx * board->uart_offset; + + base = pci_resource_start(priv->dev, bar); + len = pci_resource_len(priv->dev, bar); + p = ioremap_nocache(base, len); + + /* enable the transciever */ + writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE, + p + offset + NI8430_PORTCON); + + iounmap(p); + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + + +static int pci_netmos_init(struct pci_dev *dev) +{ + /* subdevice 0x00PS means

parallel, serial */ + unsigned int num_serial = dev->subsystem_device & 0xf; + + if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || + (dev->device == PCI_DEVICE_ID_NETMOS_9865)) + return 0; + if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && + dev->subsystem_device == 0x0299) + return 0; + + if (num_serial == 0) + return -ENODEV; + return num_serial; +} + +/* + * These chips are available with optionally one parallel port and up to + * two serial ports. Unfortunately they all have the same product id. + * + * Basic configuration is done over a region of 32 I/O ports. The base + * ioport is called INTA or INTC, depending on docs/other drivers. + * + * The region of the 32 I/O ports is configured in POSIO0R... + */ + +/* registers */ +#define ITE_887x_MISCR 0x9c +#define ITE_887x_INTCBAR 0x78 +#define ITE_887x_UARTBAR 0x7c +#define ITE_887x_PS0BAR 0x10 +#define ITE_887x_POSIO0 0x60 + +/* I/O space size */ +#define ITE_887x_IOSIZE 32 +/* I/O space size (bits 26-24; 8 bytes = 011b) */ +#define ITE_887x_POSIO_IOSIZE_8 (3 << 24) +/* I/O space size (bits 26-24; 32 bytes = 101b) */ +#define ITE_887x_POSIO_IOSIZE_32 (5 << 24) +/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */ +#define ITE_887x_POSIO_SPEED (3 << 29) +/* enable IO_Space bit */ +#define ITE_887x_POSIO_ENABLE (1 << 31) + +static int pci_ite887x_init(struct pci_dev *dev) +{ + /* inta_addr are the configuration addresses of the ITE */ + static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0, + 0x200, 0x280, 0 }; + int ret, i, type; + struct resource *iobase = NULL; + u32 miscr, uartbar, ioport; + + /* search for the base-ioport */ + i = 0; + while (inta_addr[i] && iobase == NULL) { + iobase = request_region(inta_addr[i], ITE_887x_IOSIZE, + "ite887x"); + if (iobase != NULL) { + /* write POSIO0R - speed | size | ioport */ + pci_write_config_dword(dev, ITE_887x_POSIO0, + ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | + ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]); + /* write INTCBAR - ioport */ + pci_write_config_dword(dev, ITE_887x_INTCBAR, + inta_addr[i]); + ret = inb(inta_addr[i]); + if (ret != 0xff) { + /* ioport connected */ + break; + } + release_region(iobase->start, ITE_887x_IOSIZE); + iobase = NULL; + } + i++; + } + + if (!inta_addr[i]) { + printk(KERN_ERR "ite887x: could not find iobase\n"); + return -ENODEV; + } + + /* start of undocumented type checking (see parport_pc.c) */ + type = inb(iobase->start + 0x18) & 0x0f; + + switch (type) { + case 0x2: /* ITE8871 (1P) */ + case 0xa: /* ITE8875 (1P) */ + ret = 0; + break; + case 0xe: /* ITE8872 (2S1P) */ + ret = 2; + break; + case 0x6: /* ITE8873 (1S) */ + ret = 1; + break; + case 0x8: /* ITE8874 (2S) */ + ret = 2; + break; + default: + moan_device("Unknown ITE887x", dev); + ret = -ENODEV; + } + + /* configure all serial ports */ + for (i = 0; i < ret; i++) { + /* read the I/O port from the device */ + pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)), + &ioport); + ioport &= 0x0000FF00; /* the actual base address */ + pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)), + ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED | + ITE_887x_POSIO_IOSIZE_8 | ioport); + + /* write the ioport to the UARTBAR */ + pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar); + uartbar &= ~(0xffff << (16 * i)); /* clear half the reg */ + uartbar |= (ioport << (16 * i)); /* set the ioport */ + pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar); + + /* get current config */ + pci_read_config_dword(dev, ITE_887x_MISCR, &miscr); + /* disable interrupts (UARTx_Routing[3:0]) */ + miscr &= ~(0xf << (12 - 4 * i)); + /* activate the UART (UARTx_En) */ + miscr |= 1 << (23 - i); + /* write new config with activated UART */ + pci_write_config_dword(dev, ITE_887x_MISCR, miscr); + } + + if (ret <= 0) { + /* the device has no UARTs if we get here */ + release_region(iobase->start, ITE_887x_IOSIZE); + } + + return ret; +} + +static void __devexit pci_ite887x_exit(struct pci_dev *dev) +{ + u32 ioport; + /* the ioport is bit 0-15 in POSIO0R */ + pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport); + ioport &= 0xffff; + release_region(ioport, ITE_887x_IOSIZE); +} + +/* + * Oxford Semiconductor Inc. + * Check that device is part of the Tornado range of devices, then determine + * the number of ports available on the device. + */ +static int pci_oxsemi_tornado_init(struct pci_dev *dev) +{ + u8 __iomem *p; + unsigned long deviceID; + unsigned int number_uarts = 0; + + /* OxSemi Tornado devices are all 0xCxxx */ + if (dev->vendor == PCI_VENDOR_ID_OXSEMI && + (dev->device & 0xF000) != 0xC000) + return 0; + + p = pci_iomap(dev, 0, 5); + if (p == NULL) + return -ENOMEM; + + deviceID = ioread32(p); + /* Tornado device */ + if (deviceID == 0x07000200) { + number_uarts = ioread8(p + 4); + printk(KERN_DEBUG + "%d ports detected on Oxford PCI Express device\n", + number_uarts); + } + pci_iounmap(dev, p); + return number_uarts; +} + +static int +pci_default_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> + (board->reg_shift + 3); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + +static int +ce4100_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + int ret; + + ret = setup_port(priv, port, 0, 0, board->reg_shift); + port->iotype = UPIO_MEM32; + port->type = PORT_XSCALE; + port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + port->regshift = 2; + + return ret; +} + +static int skip_tx_en_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + port->flags |= UPF_NO_TXEN_TEST; + printk(KERN_DEBUG "serial8250: skipping TxEn test for device " + "[%04x:%04x] subsystem [%04x:%04x]\n", + priv->dev->vendor, + priv->dev->device, + priv->dev->subsystem_vendor, + priv->dev->subsystem_device); + + return pci_default_setup(priv, board, port, idx); +} + +/* This should be in linux/pci_ids.h */ +#define PCI_VENDOR_ID_SBSMODULARIO 0x124B +#define PCI_SUBVENDOR_ID_SBSMODULARIO 0x124B +#define PCI_DEVICE_ID_OCTPRO 0x0001 +#define PCI_SUBDEVICE_ID_OCTPRO232 0x0108 +#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 +#define PCI_SUBDEVICE_ID_POCTAL232 0x0308 +#define PCI_SUBDEVICE_ID_POCTAL422 0x0408 +#define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 +#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 +#define PCI_DEVICE_ID_TITAN_200I 0x8028 +#define PCI_DEVICE_ID_TITAN_400I 0x8048 +#define PCI_DEVICE_ID_TITAN_800I 0x8088 +#define PCI_DEVICE_ID_TITAN_800EH 0xA007 +#define PCI_DEVICE_ID_TITAN_800EHB 0xA008 +#define PCI_DEVICE_ID_TITAN_400EH 0xA009 +#define PCI_DEVICE_ID_TITAN_100E 0xA010 +#define PCI_DEVICE_ID_TITAN_200E 0xA012 +#define PCI_DEVICE_ID_TITAN_400E 0xA013 +#define PCI_DEVICE_ID_TITAN_800E 0xA014 +#define PCI_DEVICE_ID_TITAN_200EI 0xA016 +#define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 + +/* Unknown vendors/cards - this should not be in linux/pci_ids.h */ +#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 + +/* + * Master list of serial port init/setup/exit quirks. + * This does not describe the general nature of the port. + * (ie, baud base, number and location of ports, etc) + * + * This list is ordered alphabetically by vendor then device. + * Specific entries must come before more generic entries. + */ +static struct pci_serial_quirk pci_serial_quirks[] __refdata = { + /* + * ADDI-DATA GmbH communication cards + */ + { + .vendor = PCI_VENDOR_ID_ADDIDATA_OLD, + .device = PCI_DEVICE_ID_ADDIDATA_APCI7800, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = addidata_apci7800_setup, + }, + /* + * AFAVLAB cards - these may be called via parport_serial + * It is not clear whether this applies to all products. + */ + { + .vendor = PCI_VENDOR_ID_AFAVLAB, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = afavlab_setup, + }, + /* + * HP Diva + */ + { + .vendor = PCI_VENDOR_ID_HP, + .device = PCI_DEVICE_ID_HP_DIVA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_hp_diva_init, + .setup = pci_hp_diva_setup, + }, + /* + * Intel + */ + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_80960_RP, + .subvendor = 0xe4bf, + .subdevice = PCI_ANY_ID, + .init = pci_inteli960ni_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_8257X_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573L_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82573E_SOL, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = skip_tx_en_setup, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CE4100_UART, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = ce4100_serial_setup, + }, + /* + * ITE + */ + { + .vendor = PCI_VENDOR_ID_ITE, + .device = PCI_DEVICE_ID_ITE_8872, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ite887x_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ite887x_exit), + }, + /* + * National Instruments + */ + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2324I, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PCI2322I, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_23216, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2328, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8420_2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8422_2324, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_DEVICE_ID_NI_PXI8422_2322, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8420_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_ni8420_exit), + }, + { + .vendor = PCI_VENDOR_ID_NI, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_ni8430_init, + .setup = pci_ni8430_setup, + .exit = __devexit_p(pci_ni8430_exit), + }, + /* + * Panacom + */ + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * PLX + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9030, + .subvendor = PCI_SUBVENDOR_ID_PERLE, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_EXSYS, + .subdevice = PCI_SUBDEVICE_ID_EXSYS_4055, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, + .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_SUBDEVICE_ID_UNKNOWN_0x1584, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_ROMULUS, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * SBS Technologies, Inc., PMC-OCTALPRO 232 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_OCTPRO232, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SBS Technologies, Inc., PMC-OCTALPRO 422 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_OCTPRO422, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SBS Technologies, Inc., P-Octal 232 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_POCTAL232, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SBS Technologies, Inc., P-Octal 422 + */ + { + .vendor = PCI_VENDOR_ID_SBSMODULARIO, + .device = PCI_DEVICE_ID_OCTPRO, + .subvendor = PCI_SUBVENDOR_ID_SBSMODULARIO, + .subdevice = PCI_SUBDEVICE_ID_POCTAL422, + .init = sbs_init, + .setup = sbs_setup, + .exit = __devexit_p(sbs_exit), + }, + /* + * SIIG cards - these may be called via parport_serial + */ + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig_init, + .setup = pci_siig_setup, + }, + /* + * Titan cards + */ + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_400L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_800L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + /* + * Timedia cards + */ + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_DEVICE_ID_TIMEDIA_1889, + .subvendor = PCI_VENDOR_ID_TIMEDIA, + .subdevice = PCI_ANY_ID, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_timedia_setup, + }, + /* + * Xircom cards + */ + { + .vendor = PCI_VENDOR_ID_XIRCOM, + .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_xircom_init, + .setup = pci_default_setup, + }, + /* + * Netmos cards - these may be called via parport_serial + */ + { + .vendor = PCI_VENDOR_ID_NETMOS, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_netmos_init, + .setup = pci_default_setup, + }, + /* + * For Oxford Semiconductor and Mainpine + */ + { + .vendor = PCI_VENDOR_ID_OXSEMI, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_MAINPINE, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_oxsemi_tornado_init, + .setup = pci_default_setup, + }, + /* + * Default "match everything" terminator entry + */ + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + } +}; + +static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) +{ + return quirk_id == PCI_ANY_ID || quirk_id == dev_id; +} + +static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) +{ + struct pci_serial_quirk *quirk; + + for (quirk = pci_serial_quirks; ; quirk++) + if (quirk_id_matches(quirk->vendor, dev->vendor) && + quirk_id_matches(quirk->device, dev->device) && + quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && + quirk_id_matches(quirk->subdevice, dev->subsystem_device)) + break; + return quirk; +} + +static inline int get_pci_irq(struct pci_dev *dev, + const struct pciserial_board *board) +{ + if (board->flags & FL_NOIRQ) + return 0; + else + return dev->irq; +} + +/* + * This is the configuration table for all of the PCI serial boards + * which we support. It is directly indexed by the pci_board_num_t enum + * value, which is encoded in the pci_device_id PCI probe table's + * driver_data member. + * + * The makeup of these names are: + * pbn_bn{_bt}_n_baud{_offsetinhex} + * + * bn = PCI BAR number + * bt = Index using PCI BARs + * n = number of serial ports + * baud = baud rate + * offsetinhex = offset for each sequential port (in hex) + * + * This table is sorted by (in order): bn, bt, baud, offsetindex, n. + * + * Please note: in theory if n = 1, _bt infix should make no difference. + * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 + */ +enum pci_board_num_t { + pbn_default = 0, + + pbn_b0_1_115200, + pbn_b0_2_115200, + pbn_b0_4_115200, + pbn_b0_5_115200, + pbn_b0_8_115200, + + pbn_b0_1_921600, + pbn_b0_2_921600, + pbn_b0_4_921600, + + pbn_b0_2_1130000, + + pbn_b0_4_1152000, + + pbn_b0_2_1843200, + pbn_b0_4_1843200, + + pbn_b0_2_1843200_200, + pbn_b0_4_1843200_200, + pbn_b0_8_1843200_200, + + pbn_b0_1_4000000, + + pbn_b0_bt_1_115200, + pbn_b0_bt_2_115200, + pbn_b0_bt_4_115200, + pbn_b0_bt_8_115200, + + pbn_b0_bt_1_460800, + pbn_b0_bt_2_460800, + pbn_b0_bt_4_460800, + + pbn_b0_bt_1_921600, + pbn_b0_bt_2_921600, + pbn_b0_bt_4_921600, + pbn_b0_bt_8_921600, + + pbn_b1_1_115200, + pbn_b1_2_115200, + pbn_b1_4_115200, + pbn_b1_8_115200, + pbn_b1_16_115200, + + pbn_b1_1_921600, + pbn_b1_2_921600, + pbn_b1_4_921600, + pbn_b1_8_921600, + + pbn_b1_2_1250000, + + pbn_b1_bt_1_115200, + pbn_b1_bt_2_115200, + pbn_b1_bt_4_115200, + + pbn_b1_bt_2_921600, + + pbn_b1_1_1382400, + pbn_b1_2_1382400, + pbn_b1_4_1382400, + pbn_b1_8_1382400, + + pbn_b2_1_115200, + pbn_b2_2_115200, + pbn_b2_4_115200, + pbn_b2_8_115200, + + pbn_b2_1_460800, + pbn_b2_4_460800, + pbn_b2_8_460800, + pbn_b2_16_460800, + + pbn_b2_1_921600, + pbn_b2_4_921600, + pbn_b2_8_921600, + + pbn_b2_8_1152000, + + pbn_b2_bt_1_115200, + pbn_b2_bt_2_115200, + pbn_b2_bt_4_115200, + + pbn_b2_bt_2_921600, + pbn_b2_bt_4_921600, + + pbn_b3_2_115200, + pbn_b3_4_115200, + pbn_b3_8_115200, + + pbn_b4_bt_2_921600, + pbn_b4_bt_4_921600, + pbn_b4_bt_8_921600, + + /* + * Board-specific versions. + */ + pbn_panacom, + pbn_panacom2, + pbn_panacom4, + pbn_exsys_4055, + pbn_plx_romulus, + pbn_oxsemi, + pbn_oxsemi_1_4000000, + pbn_oxsemi_2_4000000, + pbn_oxsemi_4_4000000, + pbn_oxsemi_8_4000000, + pbn_intel_i960, + pbn_sgi_ioc3, + pbn_computone_4, + pbn_computone_6, + pbn_computone_8, + pbn_sbsxrsio, + pbn_exar_XR17C152, + pbn_exar_XR17C154, + pbn_exar_XR17C158, + pbn_exar_ibm_saturn, + pbn_pasemi_1682M, + pbn_ni8430_2, + pbn_ni8430_4, + pbn_ni8430_8, + pbn_ni8430_16, + pbn_ADDIDATA_PCIe_1_3906250, + pbn_ADDIDATA_PCIe_2_3906250, + pbn_ADDIDATA_PCIe_4_3906250, + pbn_ADDIDATA_PCIe_8_3906250, + pbn_ce4100_1_115200, +}; + +/* + * uart_offset - the space between channels + * reg_shift - describes how the UART registers are mapped + * to PCI memory by the card. + * For example IER register on SBS, Inc. PMC-OctPro is located at + * offset 0x10 from the UART base, while UART_IER is defined as 1 + * in include/linux/serial_reg.h, + * see first lines of serial_in() and serial_out() in 8250.c +*/ + +static struct pciserial_board pci_boards[] __devinitdata = { + [pbn_default] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_2_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_4_115200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_5_115200] = { + .flags = FL_BASE0, + .num_ports = 5, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_8_115200] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_921600] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_2_921600] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_4_921600] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b0_2_1130000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1130000, + .uart_offset = 8, + }, + + [pbn_b0_4_1152000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1152000, + .uart_offset = 8, + }, + + [pbn_b0_2_1843200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1843200, + .uart_offset = 8, + }, + [pbn_b0_4_1843200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1843200, + .uart_offset = 8, + }, + + [pbn_b0_2_1843200_200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 1843200, + .uart_offset = 0x200, + }, + [pbn_b0_4_1843200_200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 1843200, + .uart_offset = 0x200, + }, + [pbn_b0_8_1843200_200] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 1843200, + .uart_offset = 0x200, + }, + [pbn_b0_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_2_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_4_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_8_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_2_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_4_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_2_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_4_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_8_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_115200] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_2_115200] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_4_115200] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_8_115200] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_16_115200] = { + .flags = FL_BASE1, + .num_ports = 16, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_1_921600] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_921600] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_4_921600] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_8_921600] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_1250000] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1250000, + .uart_offset = 8, + }, + + [pbn_b1_bt_1_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_bt_2_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_bt_4_115200] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_bt_2_921600] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_1382400] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_2_1382400] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_4_1382400] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_8_1382400] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 1382400, + .uart_offset = 8, + }, + + [pbn_b2_1_115200] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_2_115200] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_4_115200] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_8_115200] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_1_460800] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_4_460800] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_8_460800] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_16_460800] = { + .flags = FL_BASE2, + .num_ports = 16, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b2_1_921600] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_4_921600] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_8_921600] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b2_8_1152000] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 1152000, + .uart_offset = 8, + }, + + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_2_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_4_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_bt_2_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_bt_4_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b3_2_115200] = { + .flags = FL_BASE3, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_4_115200] = { + .flags = FL_BASE3, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_8_115200] = { + .flags = FL_BASE3, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b4_bt_2_921600] = { + .flags = FL_BASE4, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_4_921600] = { + .flags = FL_BASE4, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_8_921600] = { + .flags = FL_BASE4, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + /* + * Entries following this are board-specific. + */ + + /* + * Panacom - IOMEM + */ + [pbn_panacom] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom2] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom4] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + + [pbn_exsys_4055] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + /* I think this entry is broken - the first_offset looks wrong --rmk */ + [pbn_plx_romulus] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x03, + }, + + /* + * This board uses the size of PCI Base region 0 to + * signal now many ports are available + */ + [pbn_oxsemi] = { + .flags = FL_BASE0|FL_REGION_SZ_CAP, + .num_ports = 32, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_oxsemi_1_4000000] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_2_4000000] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_4_4000000] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_oxsemi_8_4000000] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 4000000, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + + + /* + * EKF addition for i960 Boards form EKF with serial port. + * Max 256 ports. + */ + [pbn_intel_i960] = { + .flags = FL_BASE0, + .num_ports = 32, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x10000, + }, + [pbn_sgi_ioc3] = { + .flags = FL_BASE0|FL_NOIRQ, + .num_ports = 1, + .base_baud = 458333, + .uart_offset = 8, + .reg_shift = 0, + .first_offset = 0x20178, + }, + + /* + * Computone - uses IOMEM. + */ + [pbn_computone_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_6] = { + .flags = FL_BASE0, + .num_ports = 6, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_sbsxrsio] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 256, + .reg_shift = 4, + }, + /* + * Exar Corp. XR17C15[248] Dual/Quad/Octal UART + * Only basic 16550A support. + * XR17C15[24] are not tested, but they should work. + */ + [pbn_exar_XR17C152] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_exar_XR17C154] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_exar_XR17C158] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x200, + }, + [pbn_exar_ibm_saturn] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 0x200, + }, + + /* + * PA Semi PWRficient PA6T-1682M on-chip UART + */ + [pbn_pasemi_1682M] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 8333333, + }, + /* + * National Instruments 843x + */ + [pbn_ni8430_16] = { + .flags = FL_BASE0, + .num_ports = 16, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + [pbn_ni8430_2] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3686400, + .uart_offset = 0x10, + .first_offset = 0x800, + }, + /* + * ADDI-DATA GmbH PCI-Express communication cards + */ + [pbn_ADDIDATA_PCIe_1_3906250] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_2_3906250] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_4_3906250] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_8_3906250] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ce4100_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .reg_shift = 2, + }, +}; + +static const struct pci_device_id softmodem_blacklist[] = { + { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ + { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ + { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ +}; + +/* + * Given a complete unknown PCI device, try to use some heuristics to + * guess what the configuration might be, based on the pitiful PCI + * serial specs. Returns 0 on success, 1 on failure. + */ +static int __devinit +serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board) +{ + const struct pci_device_id *blacklist; + int num_iomem, num_port, first_port = -1, i; + + /* + * If it is not a communications device or the programming + * interface is greater than 6, give up. + * + * (Should we try to make guesses for multiport serial devices + * later?) + */ + if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && + ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || + (dev->class & 0xff) > 6) + return -ENODEV; + + /* + * Do not access blacklisted devices that are known not to + * feature serial ports. + */ + for (blacklist = softmodem_blacklist; + blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist); + blacklist++) { + if (dev->vendor == blacklist->vendor && + dev->device == blacklist->device) + return -ENODEV; + } + + num_iomem = num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { + num_port++; + if (first_port == -1) + first_port = i; + } + if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + num_iomem++; + } + + /* + * If there is 1 or 0 iomem regions, and exactly one port, + * use it. We guess the number of ports based on the IO + * region size. + */ + if (num_iomem <= 1 && num_port == 1) { + board->flags = first_port; + board->num_ports = pci_resource_len(dev, first_port) / 8; + return 0; + } + + /* + * Now guess if we've got a board which indexes by BARs. + * Each IO BAR should be 8 bytes, and they should follow + * consecutively. + */ + first_port = -1; + num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO && + pci_resource_len(dev, i) == 8 && + (first_port == -1 || (first_port + num_port) == i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + } + + if (num_port > 1) { + board->flags = first_port | FL_BASE_BARS; + board->num_ports = num_port; + return 0; + } + + return -ENODEV; +} + +static inline int +serial_pci_matches(const struct pciserial_board *board, + const struct pciserial_board *guessed) +{ + return + board->num_ports == guessed->num_ports && + board->base_baud == guessed->base_baud && + board->uart_offset == guessed->uart_offset && + board->reg_shift == guessed->reg_shift && + board->first_offset == guessed->first_offset; +} + +struct serial_private * +pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) +{ + struct uart_port serial_port; + struct serial_private *priv; + struct pci_serial_quirk *quirk; + int rc, nr_ports, i; + + nr_ports = board->num_ports; + + /* + * Find an init and setup quirks. + */ + quirk = find_quirk(dev); + + /* + * Run the new-style initialization function. + * The initialization function returns: + * <0 - error + * 0 - use board->num_ports + * >0 - number of ports + */ + if (quirk->init) { + rc = quirk->init(dev); + if (rc < 0) { + priv = ERR_PTR(rc); + goto err_out; + } + if (rc) + nr_ports = rc; + } + + priv = kzalloc(sizeof(struct serial_private) + + sizeof(unsigned int) * nr_ports, + GFP_KERNEL); + if (!priv) { + priv = ERR_PTR(-ENOMEM); + goto err_deinit; + } + + priv->dev = dev; + priv->quirk = quirk; + + memset(&serial_port, 0, sizeof(struct uart_port)); + serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ; + serial_port.uartclk = board->base_baud * 16; + serial_port.irq = get_pci_irq(dev, board); + serial_port.dev = &dev->dev; + + for (i = 0; i < nr_ports; i++) { + if (quirk->setup(priv, board, &serial_port, i)) + break; + +#ifdef SERIAL_DEBUG_PCI + printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", + serial_port.iobase, serial_port.irq, serial_port.iotype); +#endif + + priv->line[i] = serial8250_register_port(&serial_port); + if (priv->line[i] < 0) { + printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]); + break; + } + } + priv->nr = i; + return priv; + +err_deinit: + if (quirk->exit) + quirk->exit(dev); +err_out: + return priv; +} +EXPORT_SYMBOL_GPL(pciserial_init_ports); + +void pciserial_remove_ports(struct serial_private *priv) +{ + struct pci_serial_quirk *quirk; + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_unregister_port(priv->line[i]); + + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (priv->remapped_bar[i]) + iounmap(priv->remapped_bar[i]); + priv->remapped_bar[i] = NULL; + } + + /* + * Find the exit quirks. + */ + quirk = find_quirk(priv->dev); + if (quirk->exit) + quirk->exit(priv->dev); + + kfree(priv); +} +EXPORT_SYMBOL_GPL(pciserial_remove_ports); + +void pciserial_suspend_ports(struct serial_private *priv) +{ + int i; + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_suspend_port(priv->line[i]); +} +EXPORT_SYMBOL_GPL(pciserial_suspend_ports); + +void pciserial_resume_ports(struct serial_private *priv) +{ + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(priv->dev); + + for (i = 0; i < priv->nr; i++) + if (priv->line[i] >= 0) + serial8250_resume_port(priv->line[i]); +} +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 +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct serial_private *priv; + const struct pciserial_board *board; + struct pciserial_board tmp; + int rc; + + if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { + printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", + ent->driver_data); + return -EINVAL; + } + + board = &pci_boards[ent->driver_data]; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (ent->driver_data == pbn_default) { + /* + * Use a copy of the pci_board entry for this; + * avoid changing entries in the table. + */ + memcpy(&tmp, board, sizeof(struct pciserial_board)); + board = &tmp; + + /* + * We matched one of our class entries. Try to + * determine the parameters of this board. + */ + rc = serial_pci_guess_board(dev, &tmp); + if (rc) + goto disable; + } else { + /* + * We matched an explicit entry. If we are able to + * detect this boards settings with our heuristic, + * then we no longer need this entry. + */ + memcpy(&tmp, &pci_boards[pbn_default], + sizeof(struct pciserial_board)); + rc = serial_pci_guess_board(dev, &tmp); + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); + } + + priv = pciserial_init_ports(dev, board); + if (!IS_ERR(priv)) { + pci_set_drvdata(dev, priv); + return 0; + } + + rc = PTR_ERR(priv); + + disable: + pci_disable_device(dev); + return rc; +} + +static void __devexit pciserial_remove_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + pci_set_drvdata(dev, NULL); + + pciserial_remove_ports(priv); + + pci_disable_device(dev); +} + +#ifdef CONFIG_PM +static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) + pciserial_suspend_ports(priv); + + pci_save_state(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + return 0; +} + +static int pciserial_resume_one(struct pci_dev *dev) +{ + int err; + struct serial_private *priv = pci_get_drvdata(dev); + + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + + if (priv) { + /* + * The device may have been disabled. Re-enable it. + */ + err = pci_enable_device(dev); + /* FIXME: We cannot simply error out here */ + if (err) + printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); + pciserial_resume_ports(priv); + } + return 0; +} +#endif + +static struct pci_device_id serial_pci_tbl[] = { + /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, + PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, + pbn_b1_8_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0, + pbn_b1_4_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0, + pbn_b1_2_1382400 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0, + pbn_b1_2_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0, + pbn_b1_8_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0, + pbn_b1_4_921600 }, + { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0, + pbn_b1_2_1250000 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0, + pbn_b0_2_1843200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0, + pbn_b0_4_1843200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_AFAVLAB, + PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0, + pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0, + pbn_b0_2_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0, + pbn_b0_4_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_SUBVENDOR_ID_CONNECT_TECH, + PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, + pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, + 0, 0, pbn_exar_ibm_saturn }, + + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_1_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_115200 }, + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + /* + * VScom SPCOM800, from sl@s.pl + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_8_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_4_921600 }, + /* Unknown card - subdevice 0x1584 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_KEYSPAN, + PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0, + pbn_panacom }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom4 }, + { PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_panacom2 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_VENDOR_ID_ESDGMBH, + PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0, + pbn_b2_4_115200 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIFAST, + PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, + pbn_b2_16_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, + pbn_b2_4_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_CHASE_PCIRAS, + PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, + pbn_b2_8_460800 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_SUBVENDOR_ID_EXSYS, + PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0, + pbn_exsys_4055 }, + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, + 0x10b5, 0x106a, 0, 0, + pbn_plx_romulus }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_4_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_2_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, + 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, + 0, 0, + pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_OXSEMI, 0x9505, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + + /* + * The below card is a little controversial since it is the + * subject of a PCI vendor/device ID clash. (See + * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html). + * For now just used the hex ID 0x950a. + */ + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0, + pbn_b0_2_115200 }, + { PCI_VENDOR_ID_OXSEMI, 0x950a, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_1130000 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950, + PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_115200 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, + PCI_ANY_ID , PCI_ANY_ID, 0, 0, + pbn_b2_8_1152000 }, + + /* + * Oxford Semiconductor Inc. Tornado PCI express device range. + */ + { PCI_VENDOR_ID_OXSEMI, 0xc101, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc105, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc11b, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc11f, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc120, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc124, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc138, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc13d, /* OXPCIe952 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc140, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc141, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc144, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc145, /* OXPCIe952 1 Legacy UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc158, /* OXPCIe952 2 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc15d, /* OXPCIe952 2 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc208, /* OXPCIe954 4 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc20d, /* OXPCIe954 4 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc308, /* OXPCIe958 8 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc30d, /* OXPCIe958 8 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc40b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc40f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc41b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc41f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc42b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc42f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc43b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc43f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc44b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc44f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc45b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc45f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc46b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc46f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc47b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc47f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc48b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc48f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc49b, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc49f, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4ab, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4af, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4bb, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4bf, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4cb, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_OXSEMI, 0xc4cf, /* OXPCIe200 1 Native UART */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + /* + * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado + */ + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 1 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 2 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 4 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_MAINPINE, 0x4000, /* IQ Express 8 Port V.34 Super-G3 Fax */ + PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0, + pbn_oxsemi_8_4000000 }, + /* + * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards, + * from skokodyn@yahoo.com + */ + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0, + pbn_sbsxrsio }, + { PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO, + PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0, + pbn_sbsxrsio }, + + /* + * Digitan DS560-558, from jimd@esoft.com + */ + { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_115200 }, + + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_460800 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, + + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, + 0, 0, pbn_computone_4 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8, + 0, 0, pbn_computone_8 }, + { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, + PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6, + 0, 0, pbn_computone_6 }, + + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_921600 }, + + /* + * AFAVLAB serial card, from Harald Welte + */ + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_115200 }, + + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_460800 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_460800 }, + + /* + * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408). + * Cards are identified by their subsystem vendor IDs, which + * (in hex) match the model number. + * + * Note that JC140x are RS422/485 cards which require ox950 + * ACR = 0x10, and as such are not currently fully supported. + */ + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, +/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1402, 0x0002, 0, 0, + pbn_b0_2_921600 }, */ +/* { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0, + 0x1404, 0x0004, 0, 0, + pbn_b0_4_921600 }, */ + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + /* + * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com + */ + { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_1382400 }, + + /* + * Dell Remote Access Card III - Tim_T_Murphy@Dell.com + */ + { PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_1382400 }, + + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ + { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_bt_2_115200 }, + + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, + 0xE4BF, PCI_ANY_ID, 0, 0, + pbn_intel_i960 }, + + /* + * Xircom Cardbus/Ethernet combos + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + + /* + * Untested PCI modems, sent in from various folks... + */ + + /* + * Elsa Model 56K PCI Modem, from Andreas Rath + */ + { PCI_VENDOR_ID_ROCKWELL, 0x1004, + 0x1048, 0x1500, 0, 0, + pbn_b1_1_115200 }, + + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3, + 0xFF00, 0, 0, 0, + pbn_sgi_ioc3 }, + + /* + * HP Diva card + */ + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0, + pbn_b1_1_115200 }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_5_115200 }, + { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b2_1_115200 }, + + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_2_115200 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_4_115200 }, + { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b3_8_115200 }, + + /* + * Exar Corp. XR17C15[248] Dual/Quad/Octal UART + */ + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17C152 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17C154 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158, + PCI_ANY_ID, PCI_ANY_ID, + 0, + 0, pbn_exar_XR17C158 }, + + /* + * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) + */ + { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, + /* + * ITE + */ + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + pbn_b1_bt_1_115200 }, + + /* + * IntaShield IS-200 + */ + { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0811 */ + pbn_b2_2_115200 }, + /* + * IntaShield IS-400 + */ + { PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* 135a.0dc0 */ + pbn_b2_4_115200 }, + /* + * Perle PCI-RAS cards + */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4, + 0, 0, pbn_b2_4_921600 }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, + PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8, + 0, 0, pbn_b2_8_921600 }, + + /* + * Mainpine series cards: Fairly standard layout but fools + * parts of the autodetect in some cases and uses otherwise + * unmatched communications subclasses in the PCI Express case + */ + + { /* RockForceDUO */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0200, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUATRO */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0300, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceDUO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0400, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUATRO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0500, + 0, 0, pbn_b0_4_115200 }, + { /* RockForce+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0600, + 0, 0, pbn_b0_2_115200 }, + { /* RockForce+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0700, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceOCTO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0800, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceDUO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0C00, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceQUARTRO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x0D00, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceOCTO+ */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x1D00, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceD1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2000, + 0, 0, pbn_b0_1_115200 }, + { /* RockForceF1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2100, + 0, 0, pbn_b0_1_115200 }, + { /* RockForceD2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2200, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceF2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2300, + 0, 0, pbn_b0_2_115200 }, + { /* RockForceD4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2400, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceF4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2500, + 0, 0, pbn_b0_4_115200 }, + { /* RockForceD8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2600, + 0, 0, pbn_b0_8_115200 }, + { /* RockForceF8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x2700, + 0, 0, pbn_b0_8_115200 }, + { /* IQ Express D1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3000, + 0, 0, pbn_b0_1_115200 }, + { /* IQ Express F1 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3100, + 0, 0, pbn_b0_1_115200 }, + { /* IQ Express D2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3200, + 0, 0, pbn_b0_2_115200 }, + { /* IQ Express F2 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3300, + 0, 0, pbn_b0_2_115200 }, + { /* IQ Express D4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3400, + 0, 0, pbn_b0_4_115200 }, + { /* IQ Express F4 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3500, + 0, 0, pbn_b0_4_115200 }, + { /* IQ Express D8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3C00, + 0, 0, pbn_b0_8_115200 }, + { /* IQ Express F8 */ + PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE, + PCI_VENDOR_ID_MAINPINE, 0x3D00, + 0, 0, pbn_b0_8_115200 }, + + + /* + * PA Semi PA6T-1682M on-chip UART + */ + { PCI_VENDOR_ID_PASEMI, 0xa004, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_pasemi_1682M }, + + /* + * National Instruments + */ + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_16_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_16_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_8_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_4_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_115200 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_8 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_8 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_16 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_2 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + { PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ni8430_4 }, + + /* + * ADDI-DATA GmbH communication cards + */ + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA_OLD, + PCI_DEVICE_ID_ADDIDATA_APCI7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b1_8_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300_2, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7500_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_4_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7420_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_2_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7300_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCI7800_3, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_b0_8_115200 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_4_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_2_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_1_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_8_3906250 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_VENDOR_ID_IBM, 0x0299, + 0, 0, pbn_b0_bt_2_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + /* + * Best Connectivity PCI Multi I/O cards + */ + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004, + 0, 0, pbn_b0_bt_4_115200 }, + /* Intel CE4100 */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ce4100_1_115200 }, + + + /* + * These entries match devices with class COMMUNICATION_SERIAL, + * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL + */ + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL << 8, + 0xffff00, pbn_default }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MODEM << 8, + 0xffff00, pbn_default }, + { PCI_ANY_ID, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, + 0xffff00, pbn_default }, + { 0, } +}; + +static struct pci_driver serial_pci_driver = { + .name = "serial", + .probe = pciserial_init_one, + .remove = __devexit_p(pciserial_remove_one), +#ifdef CONFIG_PM + .suspend = pciserial_suspend_one, + .resume = pciserial_resume_one, +#endif + .id_table = serial_pci_tbl, +}; + +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_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module"); +MODULE_DEVICE_TABLE(pci, serial_pci_tbl); diff --git a/drivers/tty/serial/8250_pnp.c b/drivers/tty/serial/8250_pnp.c new file mode 100644 index 000000000000..4822cb50cd0f --- /dev/null +++ b/drivers/tty/serial/8250_pnp.c @@ -0,0 +1,523 @@ +/* + * linux/drivers/char/8250_pnp.c + * + * Probe module for 8250/16550-type ISAPNP serial ports. + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * Ported to the Linux PnP Layer - (C) Adam Belay. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "8250.h" + +#define UNKNOWN_DEV 0x3000 + + +static const struct pnp_device_id pnp_dev_table[] = { + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "AAC000F", 0 }, + /* Anchor Datacomm BV */ + /* SXPro 144 External Data Fax Modem Plug & Play */ + { "ADC0001", 0 }, + /* SXPro 288 External Data Fax Modem Plug & Play */ + { "ADC0002", 0 }, + /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ + { "AEI0250", 0 }, + /* Actiontec ISA PNP 56K X2 Fax Modem */ + { "AEI1240", 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, + /* AZT3005 PnP SOUND DEVICE */ + { "AZT4001", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Boca Research */ + /* Boca Complete Ofc Communicator 14.4 Data-FAX */ + { "BRI0A49", 0 }, + /* Boca Research 33,600 ACF Modem */ + { "BRI1400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI3400", 0 }, + /* Boca 33.6 Kbps Internal FD34FSVD */ + { "BRI0A49", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ + { "BDP3336", 0 }, + /* Computer Peripherals Inc */ + /* EuroViVa CommCenter-33.6 SP PnP */ + { "CPI4050", 0 }, + /* Creative Labs */ + /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */ + { "CTL3001", 0 }, + /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */ + { "CTL3011", 0 }, + /* Davicom ISA 33.6K Modem */ + { "DAV0336", 0 }, + /* Creative */ + /* Creative Modem Blaster Flash56 DI5601-1 */ + { "DMB1032", 0 }, + /* Creative Modem Blaster V.90 DI5660 */ + { "DMB2001", 0 }, + /* E-Tech */ + /* E-Tech CyberBULLET PC56RVP */ + { "ETT0002", 0 }, + /* FUJITSU */ + /* Fujitsu 33600 PnP-I2 R Plug & Play */ + { "FUJ0202", 0 }, + /* Fujitsu FMV-FX431 Plug & Play */ + { "FUJ0205", 0 }, + /* Fujitsu 33600 PnP-I4 R Plug & Play */ + { "FUJ0206", 0 }, + /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */ + { "FUJ0209", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "GVC000F", 0 }, + /* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */ + { "GVC0303", 0 }, + /* Hayes */ + /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */ + { "HAY0001", 0 }, + /* Hayes Optima 336 V.34 + FAX + Voice PnP */ + { "HAY000C", 0 }, + /* Hayes Optima 336B V.34 + FAX + Voice PnP */ + { "HAY000D", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5670", 0 }, + /* Hayes Accura 56K Ext Fax Modem PnP */ + { "HAY5674", 0 }, + /* Hayes Accura 56K Fax Modem PnP */ + { "HAY5675", 0 }, + /* Hayes 288, V.34 + FAX */ + { "HAYF000", 0 }, + /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */ + { "HAYF001", 0 }, + /* IBM */ + /* IBM Thinkpad 701 Internal Modem Voice */ + { "IBM0033", 0 }, + /* Intertex */ + /* Intertex 28k8 33k6 Voice EXT PnP */ + { "IXDC801", 0 }, + /* Intertex 33k6 56k Voice EXT PnP */ + { "IXDC901", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDD801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDD901", 0 }, + /* Intertex 28k8 33k6 Voice SP INT PnP */ + { "IXDF401", 0 }, + /* Intertex 28k8 33k6 Voice SP EXT PnP */ + { "IXDF801", 0 }, + /* Intertex 33k6 56k Voice SP EXT PnP */ + { "IXDF901", 0 }, + /* Kortex International */ + /* KORTEX 28800 Externe PnP */ + { "KOR4522", 0 }, + /* KXPro 33.6 Vocal ASVD PnP */ + { "KORF661", 0 }, + /* Lasat */ + /* LASAT Internet 33600 PnP */ + { "LAS4040", 0 }, + /* Lasat Safire 560 PnP */ + { "LAS4540", 0 }, + /* Lasat Safire 336 PnP */ + { "LAS5440", 0 }, + /* Microcom, Inc. */ + /* Microcom TravelPorte FAST V.34 Plug & Play */ + { "MNP0281", 0 }, + /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */ + { "MNP0336", 0 }, + /* Microcom DeskPorte FAST EP 28.8 Plug & Play */ + { "MNP0339", 0 }, + /* Microcom DeskPorte 28.8P Plug & Play */ + { "MNP0342", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0500", 0 }, + /* Microcom DeskPorte FAST ES 28.8 Plug & Play */ + { "MNP0501", 0 }, + /* Microcom DeskPorte 28.8S Internal Plug & Play */ + { "MNP0502", 0 }, + /* Motorola */ + /* Motorola BitSURFR Plug & Play */ + { "MOT1105", 0 }, + /* Motorola TA210 Plug & Play */ + { "MOT1111", 0 }, + /* Motorola HMTA 200 (ISDN) Plug & Play */ + { "MOT1114", 0 }, + /* Motorola BitSURFR Plug & Play */ + { "MOT1115", 0 }, + /* Motorola Lifestyle 28.8 Internal */ + { "MOT1190", 0 }, + /* Motorola V.3400 Plug & Play */ + { "MOT1501", 0 }, + /* Motorola Lifestyle 28.8 V.34 Plug & Play */ + { "MOT1502", 0 }, + /* Motorola Power 28.8 V.34 Plug & Play */ + { "MOT1505", 0 }, + /* Motorola ModemSURFR External 28.8 Plug & Play */ + { "MOT1509", 0 }, + /* Motorola Premier 33.6 Desktop Plug & Play */ + { "MOT150A", 0 }, + /* Motorola VoiceSURFR 56K External PnP */ + { "MOT150F", 0 }, + /* Motorola ModemSURFR 56K External PnP */ + { "MOT1510", 0 }, + /* Motorola ModemSURFR 56K Internal PnP */ + { "MOT1550", 0 }, + /* Motorola ModemSURFR Internal 28.8 Plug & Play */ + { "MOT1560", 0 }, + /* Motorola Premier 33.6 Internal Plug & Play */ + { "MOT1580", 0 }, + /* Motorola OnlineSURFR 28.8 Internal Plug & Play */ + { "MOT15B0", 0 }, + /* Motorola VoiceSURFR 56K Internal PnP */ + { "MOT15F0", 0 }, + /* Com 1 */ + /* Deskline K56 Phone System PnP */ + { "MVX00A1", 0 }, + /* PC Rider K56 Phone System PnP */ + { "MVX00F2", 0 }, + /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ + { "nEC8241", 0 }, + /* Pace 56 Voice Internal Plug & Play Modem */ + { "PMC2430", 0 }, + /* Generic */ + /* Generic standard PC COM port */ + { "PNP0500", 0 }, + /* Generic 16550A-compatible COM port */ + { "PNP0501", 0 }, + /* Compaq 14400 Modem */ + { "PNPC000", 0 }, + /* Compaq 2400/9600 Modem */ + { "PNPC001", 0 }, + /* Dial-Up Networking Serial Cable between 2 PCs */ + { "PNPC031", 0 }, + /* Dial-Up Networking Parallel Cable between 2 PCs */ + { "PNPC032", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC100", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC101", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC102", 0 }, + /* Standard Modem*/ + { "PNPC103", 0 }, + /* Standard 9600 bps Modem*/ + { "PNPC104", 0 }, + /* Standard 14400 bps Modem*/ + { "PNPC105", 0 }, + /* Standard 28800 bps Modem*/ + { "PNPC106", 0 }, + /* Standard Modem */ + { "PNPC107", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC108", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC109", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10A", 0 }, + /* Standard Modem */ + { "PNPC10B", 0 }, + /* Standard 9600 bps Modem */ + { "PNPC10C", 0 }, + /* Standard 14400 bps Modem */ + { "PNPC10D", 0 }, + /* Standard 28800 bps Modem */ + { "PNPC10E", 0 }, + /* Standard Modem */ + { "PNPC10F", 0 }, + /* Standard PCMCIA Card Modem */ + { "PNP2000", 0 }, + /* Rockwell */ + /* Modular Technology */ + /* Rockwell 33.6 DPF Internal PnP */ + /* Modular Technology 33.6 Internal PnP */ + { "ROK0030", 0 }, + /* Kortex International */ + /* KORTEX 14400 Externe PnP */ + { "ROK0100", 0 }, + /* Rockwell 28.8 */ + { "ROK4120", 0 }, + /* Viking Components, Inc */ + /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */ + { "ROK4920", 0 }, + /* Rockwell */ + /* British Telecom */ + /* Modular Technology */ + /* Rockwell 33.6 DPF External PnP */ + /* BT Prologue 33.6 External PnP */ + /* Modular Technology 33.6 External PnP */ + { "RSS00A0", 0 }, + /* Viking 56K FAX INT */ + { "RSS0262", 0 }, + /* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */ + { "RSS0250", 0 }, + /* SupraExpress 28.8 Data/Fax PnP modem */ + { "SUP1310", 0 }, + /* SupraExpress 336i PnP Voice Modem */ + { "SUP1381", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1421", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1590", 0 }, + /* SupraExpress 336i Sp ASVD */ + { "SUP1620", 0 }, + /* SupraExpress 33.6 Data/Fax PnP modem */ + { "SUP1760", 0 }, + /* SupraExpress 56i Sp Intl */ + { "SUP2171", 0 }, + /* Phoebe Micro */ + /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */ + { "TEX0011", 0 }, + /* Archtek America Corp. */ + /* Archtek SmartLink Modem 3334BT Plug & Play */ + { "UAC000F", 0 }, + /* 3Com Corp. */ + /* Gateway Telepath IIvi 33.6 */ + { "USR0000", 0 }, + /* U.S. Robotics Sporster 33.6K Fax INT PnP */ + { "USR0002", 0 }, + /* Sportster Vi 14.4 PnP FAX Voicemail */ + { "USR0004", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR0006", 0 }, + /* U.S. Robotics 33.6K Voice EXT PnP */ + { "USR0007", 0 }, + /* U.S. Robotics Courier V.Everything INT PnP */ + { "USR0009", 0 }, + /* U.S. Robotics 33.6K Voice INT PnP */ + { "USR2002", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR2070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR2080", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3031", 0 }, + /* U.S. Robotics 56K FAX INT */ + { "USR3050", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3070", 0 }, + /* U.S. Robotics 56K Voice EXT PnP */ + { "USR3080", 0 }, + /* U.S. Robotics 56K Voice INT PnP */ + { "USR3090", 0 }, + /* U.S. Robotics 56K Message */ + { "USR9100", 0 }, + /* U.S. Robotics 56K FAX EXT PnP*/ + { "USR9160", 0 }, + /* U.S. Robotics 56K FAX INT PnP*/ + { "USR9170", 0 }, + /* U.S. Robotics 56K Voice EXT PnP*/ + { "USR9180", 0 }, + /* U.S. Robotics 56K Voice INT PnP*/ + { "USR9190", 0 }, + /* Wacom tablets */ + { "WACFXXX", 0 }, + /* Compaq touchscreen */ + { "FPI2002", 0 }, + /* Fujitsu Stylistic touchscreens */ + { "FUJ02B2", 0 }, + { "FUJ02B3", 0 }, + /* Fujitsu Stylistic LT touchscreens */ + { "FUJ02B4", 0 }, + /* Passive Fujitsu Stylistic touchscreens */ + { "FUJ02B6", 0 }, + { "FUJ02B7", 0 }, + { "FUJ02B8", 0 }, + { "FUJ02B9", 0 }, + { "FUJ02BC", 0 }, + /* Fujitsu Wacom Tablet PC device */ + { "FUJ02E5", 0 }, + /* Fujitsu P-series tablet PC device */ + { "FUJ02E6", 0 }, + /* Fujitsu Wacom 2FGT Tablet PC device */ + { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, + /* + * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in + * disguise) + */ + { "LTS0001", 0 }, + /* Rockwell's (PORALiNK) 33600 INT PNP */ + { "WCI0003", 0 }, + /* Unknown PnP modems */ + { "PNPCXXX", UNKNOWN_DEV }, + /* More unknown PnP modems */ + { "PNPDXXX", UNKNOWN_DEV }, + { "", 0 } +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +static char *modem_names[] __devinitdata = { + "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", + "33600", "28800", "14400", "V.90", "V.34", "V.32", NULL +}; + +static int __devinit check_name(char *name) +{ + char **tmp; + + for (tmp = modem_names; *tmp; tmp++) + if (strstr(name, *tmp)) + return 1; + + return 0; +} + +static int __devinit check_resources(struct pnp_dev *dev) +{ + resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8}; + int i; + + for (i = 0; i < ARRAY_SIZE(base); i++) { + if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8)) + return 1; + } + + return 0; +} + +/* + * Given a complete unknown PnP device, try to use some heuristics to + * detect modems. Currently use such heuristic set: + * - dev->name or dev->bus->name must contain "modem" substring; + * - device must have only one IO region (8 byte long) with base address + * 0x2e8, 0x3e8, 0x2f8 or 0x3f8. + * + * Such detection looks very ugly, but can detect at least some of numerous + * PnP modems, alternatively we must hardcode all modems in pnp_devices[] + * table. + */ +static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags) +{ + if (!(check_name(pnp_dev_name(dev)) || + (dev->card && check_name(dev->card->name)))) + return -ENODEV; + + if (check_resources(dev)) + return 0; + + return -ENODEV; +} + +static int __devinit +serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct uart_port port; + int ret, line, flags = dev_id->driver_data; + + if (flags & UNKNOWN_DEV) { + ret = serial_pnp_guess_board(dev, &flags); + if (ret < 0) + return ret; + } + + memset(&port, 0, sizeof(struct uart_port)); + if (pnp_irq_valid(dev, 0)) + port.irq = pnp_irq(dev, 0); + if (pnp_port_valid(dev, 0)) { + port.iobase = pnp_port_start(dev, 0); + port.iotype = UPIO_PORT; + } else if (pnp_mem_valid(dev, 0)) { + port.mapbase = pnp_mem_start(dev, 0); + port.iotype = UPIO_MEM; + port.flags = UPF_IOREMAP; + } else + return -ENODEV; + +#ifdef SERIAL_DEBUG_PNP + printk(KERN_DEBUG + "Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n", + port.iobase, port.mapbase, port.irq, port.iotype); +#endif + + port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE) + port.flags |= UPF_SHARE_IRQ; + port.uartclk = 1843200; + port.dev = &dev->dev; + + line = serial8250_register_port(&port); + if (line < 0) + return -ENODEV; + + pnp_set_drvdata(dev, (void *)((long)line + 1)); + return 0; +} + +static void __devexit serial_pnp_remove(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + if (line) + serial8250_unregister_port(line - 1); +} + +#ifdef CONFIG_PM +static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_suspend_port(line - 1); + return 0; +} + +static int serial_pnp_resume(struct pnp_dev *dev) +{ + long line = (long)pnp_get_drvdata(dev); + + if (!line) + return -ENODEV; + serial8250_resume_port(line - 1); + return 0; +} +#else +#define serial_pnp_suspend NULL +#define serial_pnp_resume NULL +#endif /* CONFIG_PM */ + +static struct pnp_driver serial_pnp_driver = { + .name = "serial", + .probe = serial_pnp_probe, + .remove = __devexit_p(serial_pnp_remove), + .suspend = serial_pnp_suspend, + .resume = serial_pnp_resume, + .id_table = pnp_dev_table, +}; + +static int __init serial8250_pnp_init(void) +{ + return pnp_register_driver(&serial_pnp_driver); +} + +static void __exit serial8250_pnp_exit(void) +{ + pnp_unregister_driver(&serial_pnp_driver); +} + +module_init(serial8250_pnp_init); +module_exit(serial8250_pnp_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver"); diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig new file mode 100644 index 000000000000..c1df7676a73d --- /dev/null +++ b/drivers/tty/serial/Kconfig @@ -0,0 +1,1598 @@ +# +# Serial device configuration +# + +menu "Serial drivers" + depends on HAS_IOMEM + +# +# The new 8250/16550 serial drivers +config SERIAL_8250 + tristate "8250/16550 and compatible serial support" + select SERIAL_CORE + ---help--- + This selects whether you want to include the driver for the standard + serial ports. The standard answer is Y. People who might say N + here are those that are setting up dedicated Ethernet WWW/FTP + servers, or users that have one of the various bus mice instead of a + serial mouse and don't intend to use their machine's standard serial + port for anything. (Note that the Cyclades and Stallion multi + serial port drivers do not need this driver built in for them to + work.) + + To compile this driver as a module, choose M here: the + module will be called 8250. + [WARNING: Do not compile this driver as a module if you are using + non-standard serial ports, since the configuration information will + be lost when the driver is unloaded. This limitation may be lifted + in the future.] + + BTW1: If you have a mouseman serial mouse which is not recognized by + the X window system, try running gpm first. + + BTW2: If you intend to use a software modem (also called Winmodem) + under Linux, forget it. These modems are crippled and require + proprietary drivers which are only available under Windows. + + Most people will say Y or M here, so that they can use serial mice, + modems and similar devices connecting to the standard serial ports. + +config SERIAL_8250_CONSOLE + bool "Console on 8250/16550 and compatible serial port" + depends on SERIAL_8250=y + select SERIAL_CORE_CONSOLE + ---help--- + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). This could be useful if some terminal or printer is connected + to that serial port. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyS1". (Try "man bootparam" or see the documentation of + your boot loader (grub or lilo or loadlin) about how to pass options + to the kernel at boot time.) + + If you don't have a VGA card installed and you say Y here, the + kernel will automatically use the first serial line, /dev/ttyS0, as + system console. + + You can set that using a kernel command line option such as + "console=uart8250,io,0x3f8,9600n8" + "console=uart8250,mmio,0xff5e0000,115200n8". + and it will switch to normal serial console when the corresponding + port is ready. + "earlycon=uart8250,io,0x3f8,9600n8" + "earlycon=uart8250,mmio,0xff5e0000,115200n8". + it will not only setup early console. + + If unsure, say N. + +config FIX_EARLYCON_MEM + bool + depends on X86 + default y + +config SERIAL_8250_GSC + tristate + depends on SERIAL_8250 && GSC + default SERIAL_8250 + +config SERIAL_8250_PCI + tristate "8250/16550 PCI device support" if EMBEDDED + depends on SERIAL_8250 && PCI + default SERIAL_8250 + help + This builds standard PCI serial support. You may be able to + disable this feature if you only need legacy serial support. + Saves about 9K. + +config SERIAL_8250_PNP + tristate "8250/16550 PNP device support" if EMBEDDED + depends on SERIAL_8250 && PNP + default SERIAL_8250 + help + This builds standard PNP serial support. You may be able to + disable this feature if you only need legacy serial support. + +config SERIAL_8250_HP300 + tristate + depends on SERIAL_8250 && HP300 + default SERIAL_8250 + +config SERIAL_8250_CS + tristate "8250/16550 PCMCIA device support" + depends on PCMCIA && SERIAL_8250 + ---help--- + Say Y here to enable support for 16-bit PCMCIA serial devices, + including serial port cards, modems, and the modem functions of + multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are + credit-card size devices often used with laptops.) + + To compile this driver as a module, choose M here: the + module will be called serial_cs. + + If unsure, say N. + +config SERIAL_8250_NR_UARTS + int "Maximum number of 8250/16550 serial ports" + depends on SERIAL_8250 + default "4" + help + Set this to the number of serial ports you want the driver + to support. This includes any ports discovered via ACPI or + PCI enumeration and any ports that may be added at run-time + via hot-plug, or any ISA multi-port serial cards. + +config SERIAL_8250_RUNTIME_UARTS + int "Number of 8250/16550 serial ports to register at runtime" + depends on SERIAL_8250 + range 0 SERIAL_8250_NR_UARTS + default "4" + help + Set this to the maximum number of serial ports you want + the kernel to register at boot time. This can be overridden + with the module parameter "nr_uarts", or boot-time parameter + 8250.nr_uarts + +config SERIAL_8250_EXTENDED + bool "Extended 8250/16550 serial driver options" + depends on SERIAL_8250 + help + If you wish to use any non-standard features of the standard "dumb" + driver, say Y here. This includes HUB6 support, shared serial + interrupts, special multiport support, support for more than the + four COM 1/2/3/4 boards, etc. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial driver options. If unsure, say N. + +config SERIAL_8250_MANY_PORTS + bool "Support more than 4 legacy serial ports" + depends on SERIAL_8250_EXTENDED && !IA64 + help + Say Y here if you have dumb serial boards other than the four + standard COM 1/2/3/4 ports. This may happen if you have an AST + FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available + from ), or other custom + serial port hardware which acts similar to standard serial port + hardware. If you only use the standard COM 1/2/3/4 ports, you can + say N here to save some memory. You can also say Y if you have an + "intelligent" multiport card such as Cyclades, Digiboards, etc. + +# +# Multi-port serial cards +# + +config SERIAL_8250_FOURPORT + tristate "Support Fourport cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an AST FourPort serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_fourport. + +config SERIAL_8250_ACCENT + tristate "Support Accent cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have an Accent Async serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_accent. + +config SERIAL_8250_BOCA + tristate "Support Boca cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a Boca serial board. Please read the Boca + mini-HOWTO, available from + + To compile this driver as a module, choose M here: the module + will be called 8250_boca. + +config SERIAL_8250_EXAR_ST16C554 + tristate "Support Exar ST16C554/554D Quad UART" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + The Uplogix Envoy TU301 uses this Exar Quad UART. If you are + tinkering with your Envoy TU301, or have a machine with this UART, + say Y here. + + To compile this driver as a module, choose M here: the module + will be called 8250_exar_st16c554. + +config SERIAL_8250_HUB6 + tristate "Support Hub6 cards" + depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS + help + Say Y here if you have a HUB6 serial board. + + To compile this driver as a module, choose M here: the module + will be called 8250_hub6. + +config SERIAL_8250_SHARE_IRQ + bool "Support for sharing serial interrupts" + depends on SERIAL_8250_EXTENDED + help + Some serial boards have hardware support which allows multiple dumb + serial ports on the same board to share a single IRQ. To enable + support for this in the serial driver, say Y here. + +config SERIAL_8250_DETECT_IRQ + bool "Autodetect IRQ on standard ports (unsafe)" + depends on SERIAL_8250_EXTENDED + help + Say Y here if you want the kernel to try to guess which IRQ + to use for your serial port. + + This is considered unsafe; it is far better to configure the IRQ in + a boot script using the setserial command. + + If unsure, say N. + +config SERIAL_8250_RSA + bool "Support RSA serial ports" + depends on SERIAL_8250_EXTENDED + help + ::: To be written ::: + +config SERIAL_8250_MCA + tristate "Support 8250-type ports on MCA buses" + depends on SERIAL_8250 != n && MCA + help + Say Y here if you have a MCA serial ports. + + To compile this driver as a module, choose M here: the module + will be called 8250_mca. + +config SERIAL_8250_ACORN + tristate "Acorn expansion card serial port support" + depends on ARCH_ACORN && SERIAL_8250 + help + If you have an Atomwide Serial card or Serial Port card for an Acorn + system, say Y to this option. The driver can handle 1, 2, or 3 port + cards. If unsure, say N. + +config SERIAL_8250_RM9K + bool "Support for MIPS RM9xxx integrated serial port" + depends on SERIAL_8250 != n && SERIAL_RM9000 + select SERIAL_8250_SHARE_IRQ + help + Selecting this option will add support for the integrated serial + port hardware found on MIPS RM9122 and similar processors. + If unsure, say N. + +comment "Non-8250 serial port support" + +config SERIAL_AMBA_PL010 + tristate "ARM AMBA PL010 serial port support" + depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE) + select SERIAL_CORE + help + This selects the ARM(R) AMBA(R) PrimeCell PL010 UART. If you have + an Integrator/AP or Integrator/PP2 platform, or if you have a + Cirrus Logic EP93xx CPU, say Y or M here. + + If unsure, say N. + +config SERIAL_AMBA_PL010_CONSOLE + bool "Support for console on AMBA serial port" + depends on SERIAL_AMBA_PL010=y + select SERIAL_CORE_CONSOLE + ---help--- + Say Y here if you wish to use an AMBA PrimeCell UART as the system + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyAM0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_AMBA_PL011 + tristate "ARM AMBA PL011 serial port support" + depends on ARM_AMBA + select SERIAL_CORE + help + This selects the ARM(R) AMBA(R) PrimeCell PL011 UART. If you have + an Integrator/PP2, Integrator/CP or Versatile platform, say Y or M + here. + + If unsure, say N. + +config SERIAL_AMBA_PL011_CONSOLE + bool "Support for console on AMBA serial port" + depends on SERIAL_AMBA_PL011=y + select SERIAL_CORE_CONSOLE + ---help--- + Say Y here if you wish to use an AMBA PrimeCell UART as the system + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyAMA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_SB1250_DUART + tristate "BCM1xxx on-chip DUART serial support" + depends on SIBYTE_SB1xxx_SOC=y + select SERIAL_CORE + default y + ---help--- + Support for the asynchronous serial interface (DUART) included in + the BCM1250 and derived System-On-a-Chip (SOC) devices. Note that + the letter D in DUART stands for "dual", which is how the device + is implemented. Depending on the SOC configuration there may be + one or more DUARTs available of which all are handled. + + If unsure, say Y. To compile this driver as a module, choose M here: + the module will be called sb1250-duart. + +config SERIAL_SB1250_DUART_CONSOLE + bool "Support for console on a BCM1xxx DUART serial port" + depends on SERIAL_SB1250_DUART=y + select SERIAL_CORE_CONSOLE + default y + ---help--- + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). + + If unsure, say Y. + +config SERIAL_ATMEL + bool "AT91 / AT32 on-chip serial port support" + depends on (ARM && ARCH_AT91) || AVR32 + select SERIAL_CORE + help + This enables the driver for the on-chip UARTs of the Atmel + AT91 and AT32 processors. + +config SERIAL_ATMEL_CONSOLE + bool "Support for console on AT91 / AT32 serial port" + depends on SERIAL_ATMEL=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use an on-chip UART on a Atmel + AT91 or AT32 processor as the system console (the system + console is the device which receives all kernel messages and + warnings and which allows logins in single user mode). + +config SERIAL_ATMEL_PDC + bool "Support DMA transfers on AT91 / AT32 serial port" + depends on SERIAL_ATMEL + default y + help + Say Y here if you wish to use the PDC to do DMA transfers to + and from the Atmel AT91 / AT32 serial port. In order to + actually use DMA transfers, make sure that the use_dma_tx + and use_dma_rx members in the atmel_uart_data struct is set + appropriately for each port. + + Note that break and error handling currently doesn't work + properly when DMA is enabled. Make sure that ports where + this matters don't use DMA. + +config SERIAL_ATMEL_TTYAT + bool "Install as device ttyATn instead of ttySn" + depends on SERIAL_ATMEL=y + help + Say Y here if you wish to have the internal AT91 / AT32 UARTs + appear as /dev/ttyATn (major 204, minor starting at 154) + instead of the normal /dev/ttySn (major 4, minor starting at + 64). This is necessary if you also want other UARTs, such as + external 8250/16C550 compatible UARTs. + The ttySn nodes are legally reserved for the 8250 serial driver + but are often misused by other serial drivers. + + To use this, you should create suitable ttyATn device nodes in + /dev/, and pass "console=ttyATn" to the kernel. + + Say Y if you have an external 8250/16C550 UART. If unsure, say N. + +config SERIAL_KS8695 + bool "Micrel KS8695 (Centaur) serial port support" + depends on ARCH_KS8695 + select SERIAL_CORE + help + This selects the Micrel Centaur KS8695 UART. Say Y here. + +config SERIAL_KS8695_CONSOLE + bool "Support for console on KS8695 (Centaur) serial port" + depends on SERIAL_KS8695=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use a KS8695 (Centaur) UART as the + system console (the system console is the device which + receives all kernel messages and warnings and which allows + logins in single user mode). + +config SERIAL_CLPS711X + tristate "CLPS711X serial port support" + depends on ARM && ARCH_CLPS711X + select SERIAL_CORE + help + ::: To be written ::: + +config SERIAL_CLPS711X_CONSOLE + bool "Support for console on CLPS711X serial port" + depends on SERIAL_CLPS711X=y + select SERIAL_CORE_CONSOLE + help + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyCL1". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_SAMSUNG + tristate "Samsung SoC serial support" + depends on ARM && PLAT_SAMSUNG + select SERIAL_CORE + help + Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, + providing /dev/ttySAC0, 1 and 2 (note, some machines may not + provide all of these ports, depending on how the serial port + pins are configured. + +config SERIAL_SAMSUNG_UARTS_4 + bool + depends on ARM && PLAT_SAMSUNG + default y if CPU_S3C2443 + help + Internal node for the common case of 4 Samsung compatible UARTs + +config SERIAL_SAMSUNG_UARTS + int + depends on ARM && PLAT_SAMSUNG + default 2 if ARCH_S3C2400 + default 6 if ARCH_S5P6450 + default 4 if SERIAL_SAMSUNG_UARTS_4 + default 3 + help + Select the number of available UART ports for the Samsung S3C + serial driver + +config SERIAL_SAMSUNG_DEBUG + bool "Samsung SoC serial debug" + depends on SERIAL_SAMSUNG && DEBUG_LL + help + Add support for debugging the serial driver. Since this is + generally being used as a console, we use our own output + routines that go via the low-level debug printascii() + function. + +config SERIAL_SAMSUNG_CONSOLE + bool "Support for console on Samsung SoC serial port" + depends on SERIAL_SAMSUNG=y + select SERIAL_CORE_CONSOLE + help + Allow selection of the S3C24XX on-board serial ports for use as + an virtual console. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySACx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + +config SERIAL_S3C2400 + tristate "Samsung S3C2410 Serial port support" + depends on ARM && SERIAL_SAMSUNG && CPU_S3C2400 + default y if CPU_S3C2400 + help + Serial port support for the Samsung S3C2400 SoC + +config SERIAL_S3C2410 + tristate "Samsung S3C2410 Serial port support" + depends on SERIAL_SAMSUNG && CPU_S3C2410 + default y if CPU_S3C2410 + help + Serial port support for the Samsung S3C2410 SoC + +config SERIAL_S3C2412 + tristate "Samsung S3C2412/S3C2413 Serial port support" + depends on SERIAL_SAMSUNG && CPU_S3C2412 + default y if CPU_S3C2412 + help + Serial port support for the Samsung S3C2412 and S3C2413 SoC + +config SERIAL_S3C2440 + tristate "Samsung S3C2440/S3C2442/S3C2416 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442 || CPU_S3C2416) + default y if CPU_S3C2440 + default y if CPU_S3C2442 + select SERIAL_SAMSUNG_UARTS_4 if CPU_S3C2416 + help + Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC + +config SERIAL_S3C24A0 + tristate "Samsung S3C24A0 Serial port support" + depends on SERIAL_SAMSUNG && CPU_S3C24A0 + default y if CPU_S3C24A0 + help + Serial port support for the Samsung S3C24A0 SoC + +config SERIAL_S3C6400 + tristate "Samsung S3C6400/S3C6410/S5P6440/S5P6450/S5PC100 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5P6450 || CPU_S5PC100) + select SERIAL_SAMSUNG_UARTS_4 + default y + help + Serial port support for the Samsung S3C6400, S3C6410, S5P6440, S5P6450 + and S5PC100 SoCs + +config SERIAL_S5PV210 + tristate "Samsung S5PV210 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310) + select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310) + default y + help + Serial port support for Samsung's S5P Family of SoC's + + +config SERIAL_MAX3100 + tristate "MAX3100 support" + depends on SPI + select SERIAL_CORE + help + MAX3100 chip support + +config SERIAL_MAX3107 + tristate "MAX3107 support" + depends on SPI + select SERIAL_CORE + help + MAX3107 chip support + +config SERIAL_MAX3107_AAVA + tristate "MAX3107 AAVA platform support" + depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB + select SERIAL_CORE + help + Support for the MAX3107 chip configuration found on the AAVA + platform. Includes the extra initialisation and GPIO support + neded for this device. + +config SERIAL_DZ + bool "DECstation DZ serial driver" + depends on MACH_DECSTATION && 32BIT + select SERIAL_CORE + default y + ---help--- + DZ11-family serial controllers for DECstations and VAXstations, + including the DC7085, M7814, and M7819. + +config SERIAL_DZ_CONSOLE + bool "Support console on DECstation DZ serial driver" + depends on SERIAL_DZ=y + select SERIAL_CORE_CONSOLE + default y + ---help--- + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). + + Note that the firmware uses ttyS3 as the serial console on + DECstations that use this driver. + + If unsure, say Y. + +config SERIAL_ZS + tristate "DECstation Z85C30 serial support" + depends on MACH_DECSTATION + select SERIAL_CORE + default y + ---help--- + Support for the Zilog 85C350 serial communications controller used + for serial ports in newer DECstation systems. These include the + DECsystem 5900 and all models of the DECstation and DECsystem 5000 + systems except from model 200. + + If unsure, say Y. To compile this driver as a module, choose M here: + the module will be called zs. + +config SERIAL_ZS_CONSOLE + bool "Support for console on a DECstation Z85C30 serial port" + depends on SERIAL_ZS=y + select SERIAL_CORE_CONSOLE + default y + ---help--- + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). + + Note that the firmware uses ttyS1 as the serial console on the + Maxine and ttyS3 on the others using this driver. + + If unsure, say Y. + +config SERIAL_21285 + tristate "DC21285 serial port support" + depends on ARM && FOOTBRIDGE + select SERIAL_CORE + help + If you have a machine based on a 21285 (Footbridge) StrongARM(R)/ + PCI bridge you can enable its onboard serial port by enabling this + option. + +config SERIAL_21285_CONSOLE + bool "Console on DC21285 serial port" + depends on SERIAL_21285=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the 21285 footbridge you can + make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyFB". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_MPSC + bool "Marvell MPSC serial port support" + depends on PPC32 && MV64X60 + select SERIAL_CORE + help + Say Y here if you want to use the Marvell MPSC serial controller. + +config SERIAL_MPSC_CONSOLE + bool "Support for console on Marvell MPSC serial port" + depends on SERIAL_MPSC + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console on a Marvell MPSC. + +config SERIAL_PXA + bool "PXA serial port support" + depends on ARCH_PXA || ARCH_MMP + select SERIAL_CORE + help + If you have a machine based on an Intel XScale PXA2xx CPU you + can enable its onboard serial ports by enabling this option. + +config SERIAL_PXA_CONSOLE + bool "Console on PXA serial port" + depends on SERIAL_PXA + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Intel XScale PXA + CPU you can make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_SA1100 + bool "SA1100 serial port support" + depends on ARM && ARCH_SA1100 + select SERIAL_CORE + help + If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you + can enable its onboard serial port by enabling this option. + Please read for further + info. + +config SERIAL_SA1100_CONSOLE + bool "Console on SA1100 serial port" + depends on SERIAL_SA1100 + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the SA1100/SA1110 StrongARM + CPU you can make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_MRST_MAX3110 + tristate "SPI UART driver for Max3110" + depends on SPI_DW_PCI + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + help + This is the UART protocol driver for the MAX3110 device on + the Intel Moorestown platform. On other systems use the max3100 + driver. + +config SERIAL_MFD_HSU + tristate "Medfield High Speed UART support" + depends on PCI + select SERIAL_CORE + +config SERIAL_MFD_HSU_CONSOLE + boolean "Medfile HSU serial console support" + depends on SERIAL_MFD_HSU=y + select SERIAL_CORE_CONSOLE + +config SERIAL_BFIN + tristate "Blackfin serial port support" + depends on BLACKFIN + select SERIAL_CORE + select SERIAL_BFIN_UART0 if (BF531 || BF532 || BF533 || BF561) + help + Add support for the built-in UARTs on the Blackfin. + + To compile this driver as a module, choose M here: the + module will be called bfin_5xx. + +config SERIAL_BFIN_CONSOLE + bool "Console on Blackfin serial port" + depends on SERIAL_BFIN=y + select SERIAL_CORE_CONSOLE + +choice + prompt "UART Mode" + depends on SERIAL_BFIN + default SERIAL_BFIN_DMA + help + This driver supports the built-in serial ports of the Blackfin family + of CPUs + +config SERIAL_BFIN_DMA + bool "DMA mode" + depends on !DMA_UNCACHED_NONE && KGDB_SERIAL_CONSOLE=n + help + This driver works under DMA mode. If this option is selected, the + blackfin simple dma driver is also enabled. + +config SERIAL_BFIN_PIO + bool "PIO mode" + help + This driver works under PIO mode. + +endchoice + +config SERIAL_BFIN_UART0 + bool "Enable UART0" + depends on SERIAL_BFIN + help + Enable UART0 + +config BFIN_UART0_CTSRTS + bool "Enable UART0 hardware flow control" + depends on SERIAL_BFIN_UART0 + help + Enable hardware flow control in the driver. + +config SERIAL_BFIN_UART1 + bool "Enable UART1" + depends on SERIAL_BFIN && (!BF531 && !BF532 && !BF533 && !BF561) + help + Enable UART1 + +config BFIN_UART1_CTSRTS + bool "Enable UART1 hardware flow control" + depends on SERIAL_BFIN_UART1 + help + Enable hardware flow control in the driver. + +config SERIAL_BFIN_UART2 + bool "Enable UART2" + depends on SERIAL_BFIN && (BF54x || BF538 || BF539) + help + Enable UART2 + +config BFIN_UART2_CTSRTS + bool "Enable UART2 hardware flow control" + depends on SERIAL_BFIN_UART2 + help + Enable hardware flow control in the driver. + +config SERIAL_BFIN_UART3 + bool "Enable UART3" + depends on SERIAL_BFIN && (BF54x) + help + Enable UART3 + +config BFIN_UART3_CTSRTS + bool "Enable UART3 hardware flow control" + depends on SERIAL_BFIN_UART3 + help + Enable hardware flow control in the driver. + +config SERIAL_IMX + bool "IMX serial port support" + depends on ARM && (ARCH_IMX || ARCH_MXC) + select SERIAL_CORE + select RATIONAL + help + If you have a machine based on a Motorola IMX CPU you + can enable its onboard serial port by enabling this option. + +config SERIAL_IMX_CONSOLE + bool "Console on IMX serial port" + depends on SERIAL_IMX + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Motorola IMX + CPU you can make it the console by answering Y to this option. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttySA0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_UARTLITE + tristate "Xilinx uartlite serial port support" + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE + select SERIAL_CORE + help + Say Y here if you want to use the Xilinx uartlite serial controller. + + To compile this driver as a module, choose M here: the + module will be called uartlite. + +config SERIAL_UARTLITE_CONSOLE + bool "Support for console on Xilinx uartlite serial port" + depends on SERIAL_UARTLITE=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use a Xilinx uartlite as the system + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + +config SERIAL_SUNCORE + bool + depends on SPARC + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + default y + +config SERIAL_SUNZILOG + tristate "Sun Zilog8530 serial support" + depends on SPARC + help + This driver supports the Zilog8530 serial ports found on many Sparc + systems. Say Y or M if you want to be able to these serial ports. + +config SERIAL_SUNZILOG_CONSOLE + bool "Console on Sun Zilog8530 serial port" + depends on SERIAL_SUNZILOG=y + help + If you would like to be able to use the Zilog8530 serial port + on your Sparc system as the console, you can do so by answering + Y to this option. + +config SERIAL_SUNSU + tristate "Sun SU serial support" + depends on SPARC && PCI + help + This driver supports the 8250 serial ports that run the keyboard and + mouse on (PCI) UltraSPARC systems. Say Y or M if you want to be able + to these serial ports. + +config SERIAL_SUNSU_CONSOLE + bool "Console on Sun SU serial port" + depends on SERIAL_SUNSU=y + help + If you would like to be able to use the SU serial port + on your Sparc system as the console, you can do so by answering + Y to this option. + +config SERIAL_MUX + tristate "Serial MUX support" + depends on GSC + select SERIAL_CORE + default y + ---help--- + Saying Y here will enable the hardware MUX serial driver for + the Nova, K class systems and D class with a 'remote control card'. + The hardware MUX is not 8250/16550 compatible therefore the + /dev/ttyB0 device is shared between the Serial MUX and the PDC + software console. The following steps need to be completed to use + the Serial MUX: + + 1. create the device entry (mknod /dev/ttyB0 c 11 0) + 2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0 + 3. Add device ttyB0 to /etc/securetty (if you want to log on as + root on this console.) + 4. Change the kernel command console parameter to: console=ttyB0 + +config SERIAL_MUX_CONSOLE + bool "Support for console on serial MUX" + depends on SERIAL_MUX=y + select SERIAL_CORE_CONSOLE + default y + +config PDC_CONSOLE + bool "PDC software console support" + depends on PARISC && !SERIAL_MUX && VT + default n + help + Saying Y here will enable the software based PDC console to be + used as the system console. This is useful for machines in + which the hardware based console has not been written yet. The + following steps must be competed to use the PDC console: + + 1. create the device entry (mknod /dev/ttyB0 c 11 0) + 2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0 + 3. Add device ttyB0 to /etc/securetty (if you want to log on as + root on this console.) + 4. Change the kernel command console parameter to: console=ttyB0 + +config SERIAL_SUNSAB + tristate "Sun Siemens SAB82532 serial support" + depends on SPARC && PCI + help + This driver supports the Siemens SAB82532 DUSCC serial ports on newer + (PCI) UltraSPARC systems. Say Y or M if you want to be able to these + serial ports. + +config SERIAL_SUNSAB_CONSOLE + bool "Console on Sun Siemens SAB82532 serial port" + depends on SERIAL_SUNSAB=y + help + If you would like to be able to use the SAB82532 serial port + on your Sparc system as the console, you can do so by answering + Y to this option. + +config SERIAL_SUNHV + bool "Sun4v Hypervisor Console support" + depends on SPARC64 + help + This driver supports the console device found on SUN4V Sparc + systems. Say Y if you want to be able to use this device. + +config SERIAL_IP22_ZILOG + tristate "SGI Zilog8530 serial support" + depends on SGI_HAS_ZILOG + select SERIAL_CORE + help + This driver supports the Zilog8530 serial ports found on SGI + systems. Say Y or M if you want to be able to these serial ports. + +config SERIAL_IP22_ZILOG_CONSOLE + bool "Console on SGI Zilog8530 serial port" + depends on SERIAL_IP22_ZILOG=y + select SERIAL_CORE_CONSOLE + +config SERIAL_SH_SCI + tristate "SuperH SCI(F) serial port support" + depends on HAVE_CLK && (SUPERH || H8300 || ARCH_SHMOBILE) + select SERIAL_CORE + +config SERIAL_SH_SCI_NR_UARTS + int "Maximum number of SCI(F) serial ports" + depends on SERIAL_SH_SCI + default "2" + +config SERIAL_SH_SCI_CONSOLE + bool "Support for console on SuperH SCI(F)" + depends on SERIAL_SH_SCI=y + select SERIAL_CORE_CONSOLE + +config SERIAL_SH_SCI_DMA + bool "DMA support" + depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL + +config SERIAL_PNX8XXX + bool "Enable PNX8XXX SoCs' UART Support" + depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) + select SERIAL_CORE + help + If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 + and you want to use serial ports, say Y. Otherwise, say N. + +config SERIAL_PNX8XXX_CONSOLE + bool "Enable PNX8XX0 serial console" + depends on SERIAL_PNX8XXX + select SERIAL_CORE_CONSOLE + help + If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 + and you want to use serial console, say Y. Otherwise, say N. + +config SERIAL_CORE + tristate + +config SERIAL_CORE_CONSOLE + bool + +config CONSOLE_POLL + bool + +config SERIAL_68328 + bool "68328 serial support" + depends on M68328 || M68EZ328 || M68VZ328 + help + This driver supports the built-in serial port of the Motorola 68328 + (standard, EZ and VZ varieties). + +config SERIAL_68328_RTS_CTS + bool "Support RTS/CTS on 68328 serial port" + depends on SERIAL_68328 + +config SERIAL_MCF + bool "Coldfire serial support" + depends on COLDFIRE + select SERIAL_CORE + help + This serial driver supports the Freescale Coldfire serial ports. + +config SERIAL_MCF_BAUDRATE + int "Default baudrate for Coldfire serial ports" + depends on SERIAL_MCF + default 19200 + help + This setting lets you define what the default baudrate is for the + ColdFire serial ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_MCF_CONSOLE + bool "Coldfire serial console support" + depends on SERIAL_MCF + select SERIAL_CORE_CONSOLE + help + Enable a ColdFire internal serial port to be the system console. + +config SERIAL_68360_SMC + bool "68360 SMC uart support" + depends on M68360 + help + This driver supports the SMC serial ports of the Motorola 68360 CPU. + +config SERIAL_68360_SCC + bool "68360 SCC uart support" + depends on M68360 + help + This driver supports the SCC serial ports of the Motorola 68360 CPU. + +config SERIAL_68360 + bool + depends on SERIAL_68360_SMC || SERIAL_68360_SCC + default y + +config SERIAL_PMACZILOG + tristate "Mac or PowerMac z85c30 ESCC support" + depends on (M68K && MAC) || (PPC_OF && PPC_PMAC) + select SERIAL_CORE + help + This driver supports the Zilog z85C30 serial ports found on + (Power)Mac machines. + Say Y or M if you want to be able to these serial ports. + +config SERIAL_PMACZILOG_TTYS + bool "Use ttySn device nodes for Zilog z85c30" + depends on SERIAL_PMACZILOG + help + The pmac_zilog driver for the z85C30 chip on many powermacs + historically used the device numbers for /dev/ttySn. The + 8250 serial port driver also uses these numbers, which means + the two drivers being unable to coexist; you could not use + both z85C30 and 8250 type ports at the same time. + + If this option is not selected, the pmac_zilog driver will + use the device numbers allocated for /dev/ttyPZn. This allows + the pmac_zilog and 8250 drivers to co-exist, but may cause + existing userspace setups to break. Programs that need to + access the built-in serial ports on powermacs will need to + be reconfigured to use /dev/ttyPZn instead of /dev/ttySn. + + If you enable this option, any z85c30 ports in the system will + be registered as ttyS0 onwards as in the past, and you will be + unable to use the 8250 module for PCMCIA or other 16C550-style + UARTs. + + Say N unless you need the z85c30 ports on your (Power)Mac + to appear as /dev/ttySn. + +config SERIAL_PMACZILOG_CONSOLE + bool "Console on Mac or PowerMac z85c30 serial port" + depends on SERIAL_PMACZILOG=y + select SERIAL_CORE_CONSOLE + help + If you would like to be able to use the z85c30 serial port + on your (Power)Mac as the console, you can do so by answering + Y to this option. + +config SERIAL_LH7A40X + tristate "Sharp LH7A40X embedded UART support" + depends on ARM && ARCH_LH7A40X + select SERIAL_CORE + help + This enables support for the three on-board UARTs of the + Sharp LH7A40X series CPUs. Choose Y or M. + +config SERIAL_LH7A40X_CONSOLE + bool "Support for console on Sharp LH7A40X serial port" + depends on SERIAL_LH7A40X=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use one of the serial ports as the + system console--the system console is the device which + receives all kernel messages and warnings and which allows + logins in single user mode. + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the default system console, but + you can alter that using a kernel command line, for example + "console=ttyAM1". + +config SERIAL_CPM + tristate "CPM SCC/SMC serial port support" + depends on CPM2 || 8xx + select SERIAL_CORE + help + This driver supports the SCC and SMC serial ports on Motorola + embedded PowerPC that contain a CPM1 (8xx) or CPM2 (8xxx) + +config SERIAL_CPM_CONSOLE + bool "Support for console on CPM SCC/SMC serial port" + depends on SERIAL_CPM=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you wish to use a SCC or SMC CPM UART as the system + console (the system console is the device which receives all kernel + messages and warnings and which allows logins in single user mode). + + Even if you say Y here, the currently visible framebuffer console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyCPM0". (Try "man bootparam" or see the documentation of + your boot loader (lilo or loadlin) about how to pass options to the + kernel at boot time.) + +config SERIAL_SGI_L1_CONSOLE + bool "SGI Altix L1 serial console support" + depends on IA64_GENERIC || IA64_SGI_SN2 + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + help + If you have an SGI Altix and you would like to use the system + controller serial port as your console (you want this!), + say Y. Otherwise, say N. + +config SERIAL_MPC52xx + tristate "Freescale MPC52xx/MPC512x family PSC serial support" + depends on PPC_MPC52xx || PPC_MPC512x + select SERIAL_CORE + help + This driver supports MPC52xx and MPC512x PSC serial ports. If you would + like to use them, you must answer Y or M to this option. Note that + for use as console, it must be included in kernel and not as a + module. + +config SERIAL_MPC52xx_CONSOLE + bool "Console on a Freescale MPC52xx/MPC512x family PSC serial port" + depends on SERIAL_MPC52xx=y + select SERIAL_CORE_CONSOLE + help + Select this options if you'd like to use one of the PSC serial port + of the Freescale MPC52xx family as a console. + +config SERIAL_MPC52xx_CONSOLE_BAUD + int "Freescale MPC52xx/MPC512x family PSC serial port baud" + depends on SERIAL_MPC52xx_CONSOLE=y + default "9600" + help + Select the MPC52xx console baud rate. + This value is only used if the bootloader doesn't pass in the + console baudrate + +config SERIAL_ICOM + tristate "IBM Multiport Serial Adapter" + depends on PCI && (PPC_ISERIES || PPC_PSERIES) + select SERIAL_CORE + select FW_LOADER + help + This driver is for a family of multiport serial adapters + including 2 port RVX, 2 port internal modem, 4 port internal + modem and a split 1 port RVX and 1 port internal modem. + + This driver can also be built as a module. If so, the module + will be called icom. + +config SERIAL_M32R_SIO + bool "M32R SIO I/F" + depends on M32R + default y + select SERIAL_CORE + help + Say Y here if you want to use the M32R serial controller. + +config SERIAL_M32R_SIO_CONSOLE + bool "use SIO console" + depends on SERIAL_M32R_SIO=y + select SERIAL_CORE_CONSOLE + help + Say Y here if you want to support a serial console. + + If you use an M3T-M32700UT or an OPSPUT platform, + please say also y for SERIAL_M32R_PLDSIO. + +config SERIAL_M32R_PLDSIO + bool "M32R SIO I/F on a PLD" + depends on SERIAL_M32R_SIO=y && (PLAT_OPSPUT || PLAT_USRV || PLAT_M32700UT) + default n + help + Say Y here if you want to use the M32R serial controller + on a PLD (Programmable Logic Device). + + If you use an M3T-M32700UT or an OPSPUT platform, + please say Y. + +config SERIAL_TXX9 + bool "TMPTX39XX/49XX SIO support" + depends on HAS_TXX9_SERIAL + select SERIAL_CORE + default y + +config HAS_TXX9_SERIAL + bool + +config SERIAL_TXX9_NR_UARTS + int "Maximum number of TMPTX39XX/49XX SIO ports" + depends on SERIAL_TXX9 + default "6" + +config SERIAL_TXX9_CONSOLE + bool "TMPTX39XX/49XX SIO Console support" + depends on SERIAL_TXX9=y + select SERIAL_CORE_CONSOLE + +config SERIAL_TXX9_STDSERIAL + bool "TX39XX/49XX SIO act as standard serial" + depends on !SERIAL_8250 && SERIAL_TXX9 + +config SERIAL_VR41XX + tristate "NEC VR4100 series Serial Interface Unit support" + depends on CPU_VR41XX + select SERIAL_CORE + help + If you have a NEC VR4100 series processor and you want to use + Serial Interface Unit(SIU) or Debug Serial Interface Unit(DSIU) + (not include VR4111/VR4121 DSIU), say Y. Otherwise, say N. + +config SERIAL_VR41XX_CONSOLE + bool "Enable NEC VR4100 series Serial Interface Unit console" + depends on SERIAL_VR41XX=y + select SERIAL_CORE_CONSOLE + help + If you have a NEC VR4100 series processor and you want to use + a console on a serial port, say Y. Otherwise, say N. + +config SERIAL_JSM + tristate "Digi International NEO PCI Support" + depends on PCI + select SERIAL_CORE + help + This is a driver for Digi International's Neo series + of cards which provide multiple serial ports. You would need + something like this to connect more than two modems to your Linux + box, for instance in order to become a dial-in server. This driver + supports PCI boards only. + + If you have a card like this, say Y here, otherwise say N. + + To compile this driver as a module, choose M here: the + module will be called jsm. + +config SERIAL_SGI_IOC4 + tristate "SGI IOC4 controller serial support" + depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC4 + select SERIAL_CORE + help + If you have an SGI Altix with an IOC4 based Base IO card + and wish to use the serial ports on this card, say Y. + Otherwise, say N. + +config SERIAL_SGI_IOC3 + tristate "SGI Altix IOC3 serial support" + depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC3 + select SERIAL_CORE + help + If you have an SGI Altix with an IOC3 serial card, + say Y or M. Otherwise, say N. + +config SERIAL_MSM + bool "MSM on-chip serial port support" + depends on ARM && ARCH_MSM + select SERIAL_CORE + +config SERIAL_MSM_CONSOLE + bool "MSM serial console support" + depends on SERIAL_MSM=y + select SERIAL_CORE_CONSOLE + +config SERIAL_VT8500 + bool "VIA VT8500 on-chip serial port support" + depends on ARM && ARCH_VT8500 + select SERIAL_CORE + +config SERIAL_VT8500_CONSOLE + bool "VIA VT8500 serial console support" + depends on SERIAL_VT8500=y + select SERIAL_CORE_CONSOLE + +config SERIAL_NETX + tristate "NetX serial port support" + depends on ARM && ARCH_NETX + select SERIAL_CORE + help + If you have a machine based on a Hilscher NetX SoC you + can enable its onboard serial port by enabling this option. + + To compile this driver as a module, choose M here: the + module will be called netx-serial. + +config SERIAL_NETX_CONSOLE + bool "Console on NetX serial port" + depends on SERIAL_NETX=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Hilscher NetX SoC + you can make it the console by answering Y to this option. + +config SERIAL_OF_PLATFORM + tristate "Serial port on Open Firmware platform bus" + depends on OF + depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL + help + If you have a PowerPC based system that has serial ports + on a platform specific bus, you should enable this option. + Currently, only 8250 compatible ports are supported, but + others can easily be added. + +config SERIAL_OMAP + tristate "OMAP serial port support" + depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 + select SERIAL_CORE + help + If you have a machine based on an Texas Instruments OMAP CPU you + can enable its onboard serial ports by enabling this option. + + By enabling this option you take advantage of dma feature available + with the omap-serial driver. DMA support can be enabled from platform + data. + +config SERIAL_OMAP_CONSOLE + bool "Console on OMAP serial port" + depends on SERIAL_OMAP + select SERIAL_CORE_CONSOLE + help + Select this option if you would like to use omap serial port as + console. + + Even if you say Y here, the currently visible virtual console + (/dev/tty0) will still be used as the system console by default, but + you can alter that using a kernel command line option such as + "console=ttyOx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + +config SERIAL_OF_PLATFORM_NWPSERIAL + tristate "NWP serial port driver" + depends on PPC_OF && PPC_DCR + select SERIAL_OF_PLATFORM + select SERIAL_CORE_CONSOLE + select SERIAL_CORE + help + This driver supports the cell network processor nwp serial + device. + +config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE + bool "Console on NWP serial port" + depends on SERIAL_OF_PLATFORM_NWPSERIAL=y + select SERIAL_CORE_CONSOLE + help + Support for Console on the NWP serial ports. + +config SERIAL_QE + tristate "Freescale QUICC Engine serial port support" + depends on QUICC_ENGINE + select SERIAL_CORE + select FW_LOADER + default n + help + This driver supports the QE serial ports on Freescale embedded + PowerPC that contain a QUICC Engine. + +config SERIAL_SC26XX + tristate "SC2681/SC2692 serial port support" + depends on SNI_RM + select SERIAL_CORE + help + This is a driver for the onboard serial ports of + older RM400 machines. + +config SERIAL_SC26XX_CONSOLE + bool "Console on SC2681/SC2692 serial port" + depends on SERIAL_SC26XX + select SERIAL_CORE_CONSOLE + help + Support for Console on SC2681/SC2692 serial ports. + +config SERIAL_BFIN_SPORT + tristate "Blackfin SPORT emulate UART" + depends on BLACKFIN + select SERIAL_CORE + help + Enable SPORT emulate UART on Blackfin series. + + To compile this driver as a module, choose M here: the + module will be called bfin_sport_uart. + +config SERIAL_BFIN_SPORT_CONSOLE + bool "Console on Blackfin sport emulated uart" + depends on SERIAL_BFIN_SPORT=y + select SERIAL_CORE_CONSOLE + +config SERIAL_BFIN_SPORT0_UART + bool "Enable UART over SPORT0" + depends on SERIAL_BFIN_SPORT && !(BF542 || BF544) + help + Enable UART over SPORT0 + +config SERIAL_BFIN_SPORT0_UART_CTSRTS + bool "Enable UART over SPORT0 hardware flow control" + depends on SERIAL_BFIN_SPORT0_UART + help + Enable hardware flow control in the driver. + +config SERIAL_BFIN_SPORT1_UART + bool "Enable UART over SPORT1" + depends on SERIAL_BFIN_SPORT + help + Enable UART over SPORT1 + +config SERIAL_BFIN_SPORT1_UART_CTSRTS + bool "Enable UART over SPORT1 hardware flow control" + depends on SERIAL_BFIN_SPORT1_UART + help + Enable hardware flow control in the driver. + +config SERIAL_BFIN_SPORT2_UART + bool "Enable UART over SPORT2" + depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) + help + Enable UART over SPORT2 + +config SERIAL_BFIN_SPORT2_UART_CTSRTS + bool "Enable UART over SPORT2 hardware flow control" + depends on SERIAL_BFIN_SPORT2_UART + help + Enable hardware flow control in the driver. + +config SERIAL_BFIN_SPORT3_UART + bool "Enable UART over SPORT3" + depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) + help + Enable UART over SPORT3 + +config SERIAL_BFIN_SPORT3_UART_CTSRTS + bool "Enable UART over SPORT3 hardware flow control" + depends on SERIAL_BFIN_SPORT3_UART + help + Enable hardware flow control in the driver. + +config SERIAL_TIMBERDALE + tristate "Support for timberdale UART" + select SERIAL_CORE + ---help--- + Add support for UART controller on timberdale. + +config SERIAL_BCM63XX + tristate "bcm63xx serial port support" + select SERIAL_CORE + depends on BCM63XX + help + If you have a bcm63xx CPU, you can enable its onboard + serial port by enabling this options. + + To compile this driver as a module, choose M here: the + module will be called bcm963xx_uart. + +config SERIAL_BCM63XX_CONSOLE + bool "Console on bcm63xx serial port" + depends on SERIAL_BCM63XX=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the bcm63xx CPU + you can make it the console by answering Y to this option. + +config SERIAL_GRLIB_GAISLER_APBUART + tristate "GRLIB APBUART serial support" + depends on OF + ---help--- + Add support for the GRLIB APBUART serial port. + +config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE + bool "Console on GRLIB APBUART serial port" + depends on SERIAL_GRLIB_GAISLER_APBUART=y + select SERIAL_CORE_CONSOLE + help + Support for running a console on the GRLIB APBUART + +config SERIAL_ALTERA_JTAGUART + tristate "Altera JTAG UART support" + select SERIAL_CORE + help + This driver supports the Altera JTAG UART port. + +config SERIAL_ALTERA_JTAGUART_CONSOLE + bool "Altera JTAG UART console support" + depends on SERIAL_ALTERA_JTAGUART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera JTAG UART port to be the system console. + +config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS + bool "Bypass output when no connection" + depends on SERIAL_ALTERA_JTAGUART_CONSOLE + select SERIAL_CORE_CONSOLE + help + Bypass console output and keep going even if there is no + JTAG terminal connection with the host. + +config SERIAL_ALTERA_UART + tristate "Altera UART support" + select SERIAL_CORE + help + This driver supports the Altera softcore UART port. + +config SERIAL_ALTERA_UART_MAXPORTS + int "Maximum number of Altera UART ports" + depends on SERIAL_ALTERA_UART + default 4 + help + This setting lets you define the maximum number of the Altera + UART ports. The usual default varies from board to board, and + this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_BAUDRATE + int "Default baudrate for Altera UART ports" + depends on SERIAL_ALTERA_UART + default 115200 + help + This setting lets you define what the default baudrate is for the + Altera UART ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_CONSOLE + bool "Altera UART console support" + depends on SERIAL_ALTERA_UART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera UART port to be the system console. + +config SERIAL_IFX6X60 + tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)" + depends on GPIOLIB && SPI && EXPERIMENTAL + help + Support for the IFX6x60 modem devices on Intel MID platforms. + +config SERIAL_PCH_UART + tristate "Intel EG20T PCH UART" + depends on PCI && DMADEVICES + select SERIAL_CORE + select PCH_DMA + help + This driver is for PCH(Platform controller Hub) UART of Intel EG20T + which is an IOH(Input/Output Hub) for x86 embedded processor. + Enabling PCH_DMA, this PCH UART works as DMA mode. +endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile new file mode 100644 index 000000000000..8ea92e9c73b0 --- /dev/null +++ b/drivers/tty/serial/Makefile @@ -0,0 +1,94 @@ +# +# Makefile for the kernel serial device drivers. +# + +obj-$(CONFIG_SERIAL_CORE) += serial_core.o +obj-$(CONFIG_SERIAL_21285) += 21285.o + +# These Sparc drivers have to appear before others such as 8250 +# which share ttySx minor node space. Otherwise console device +# names change and other unplesantries. +obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o +obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o +obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o +obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o +obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o + +obj-$(CONFIG_SERIAL_8250) += 8250.o +obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o +obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o +obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o +obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o +obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o +obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o +obj-$(CONFIG_SERIAL_8250_FOURPORT) += 8250_fourport.o +obj-$(CONFIG_SERIAL_8250_ACCENT) += 8250_accent.o +obj-$(CONFIG_SERIAL_8250_BOCA) += 8250_boca.o +obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554) += 8250_exar_st16c554.o +obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o +obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o +obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o +obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o +obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o +obj-$(CONFIG_SERIAL_PXA) += pxa.o +obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o +obj-$(CONFIG_SERIAL_SA1100) += sa1100.o +obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o +obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o +obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o +obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o +obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o +obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o +obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o +obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o +obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o +obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o +obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o +obj-$(CONFIG_SERIAL_MAX3100) += max3100.o +obj-$(CONFIG_SERIAL_MAX3107) += max3107.o +obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o +obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o +obj-$(CONFIG_SERIAL_MUX) += mux.o +obj-$(CONFIG_SERIAL_68328) += 68328serial.o +obj-$(CONFIG_SERIAL_68360) += 68360serial.o +obj-$(CONFIG_SERIAL_MCF) += mcf.o +obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o +obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o +obj-$(CONFIG_SERIAL_DZ) += dz.o +obj-$(CONFIG_SERIAL_ZS) += zs.o +obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o +obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o +obj-$(CONFIG_SERIAL_CPM) += cpm_uart/ +obj-$(CONFIG_SERIAL_IMX) += imx.o +obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o +obj-$(CONFIG_SERIAL_ICOM) += icom.o +obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o +obj-$(CONFIG_SERIAL_MPSC) += mpsc.o +obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o +obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o +obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o +obj-$(CONFIG_SERIAL_JSM) += jsm/ +obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o +obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o +obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o +obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o +obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o +obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o +obj-$(CONFIG_SERIAL_MSM) += msm_serial.o +obj-$(CONFIG_SERIAL_NETX) += netx-serial.o +obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o +obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o +obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o +obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o +obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o +obj-$(CONFIG_SERIAL_QE) += ucc_uart.o +obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o +obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o +obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o +obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o +obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o +obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o +obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o +obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c new file mode 100644 index 000000000000..f9b49b5ff5e1 --- /dev/null +++ b/drivers/tty/serial/altera_jtaguart.c @@ -0,0 +1,504 @@ +/* + * altera_jtaguart.c -- Altera JTAG UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer + * (C) Copyright 2008, Thomas Chou + * (C) Copyright 2010, Tobias Klauser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_jtaguart" + +/* + * Altera JTAG UART register definitions according to the Altera JTAG UART + * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf + */ + +#define ALTERA_JTAGUART_SIZE 8 + +#define ALTERA_JTAGUART_DATA_REG 0 + +#define ALTERA_JTAGUART_DATA_DATA_MSK 0x000000FF +#define ALTERA_JTAGUART_DATA_RVALID_MSK 0x00008000 +#define ALTERA_JTAGUART_DATA_RAVAIL_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_DATA_RAVAIL_OFF 16 + +#define ALTERA_JTAGUART_CONTROL_REG 4 + +#define ALTERA_JTAGUART_CONTROL_RE_MSK 0x00000001 +#define ALTERA_JTAGUART_CONTROL_WE_MSK 0x00000002 +#define ALTERA_JTAGUART_CONTROL_RI_MSK 0x00000100 +#define ALTERA_JTAGUART_CONTROL_RI_OFF 8 +#define ALTERA_JTAGUART_CONTROL_WI_MSK 0x00000200 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF 16 + +/* + * Local per-uart structure. + */ +struct altera_jtaguart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned long imr; /* Local IMR mirror */ +}; + +static unsigned int altera_jtaguart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ +} + +static void altera_jtaguart_start_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_rx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void altera_jtaguart_enable_ms(struct uart_port *port) +{ +} + +static void altera_jtaguart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + /* Just copy the old termios settings back */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned long status; + + while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) & + ALTERA_JTAGUART_DATA_RVALID_MSK) { + ch = status & ALTERA_JTAGUART_DATA_DATA_MSK; + flag = TTY_NORMAL; + port->icount.rx++; + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, 0, 0, ch, flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int pending, count; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + pending = uart_circ_chars_pending(xmit); + if (pending > 0) { + count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >> + ALTERA_JTAGUART_CONTROL_WSPACE_OFF; + if (count > pending) + count = pending; + if (count > 0) { + pending -= count; + while (count--) { + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_JTAGUART_DATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (pending < WAKEUP_CHARS) + uart_write_wakeup(port); + } + } + + if (pending == 0) { + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + } +} + +static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned int isr; + + isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> + ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr; + + spin_lock(&port->lock); + + if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) + altera_jtaguart_rx_chars(pp); + if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) + altera_jtaguart_tx_chars(pp); + + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_jtaguart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_JTAGUART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static int altera_jtaguart_startup(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_jtaguart_shutdown(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_jtaguart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL; +} + +static int altera_jtaguart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_jtaguart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_jtaguart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_jtaguart_ops = { + .tx_empty = altera_jtaguart_tx_empty, + .get_mctrl = altera_jtaguart_get_mctrl, + .set_mctrl = altera_jtaguart_set_mctrl, + .start_tx = altera_jtaguart_start_tx, + .stop_tx = altera_jtaguart_stop_tx, + .stop_rx = altera_jtaguart_stop_rx, + .enable_ms = altera_jtaguart_enable_ms, + .break_ctl = altera_jtaguart_break_ctl, + .startup = altera_jtaguart_startup, + .shutdown = altera_jtaguart_shutdown, + .set_termios = altera_jtaguart_set_termios, + .type = altera_jtaguart_type, + .request_port = altera_jtaguart_request_port, + .release_port = altera_jtaguart_release_port, + .config_port = altera_jtaguart_config_port, + .verify_port = altera_jtaguart_verify_port, +}; + +#define ALTERA_JTAGUART_MAXPORTS 1 +static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +int __init early_altera_jtaguart_setup(struct altera_jtaguart_platform_uart + *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_jtaguart_ops; + } + + return 0; +} + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; /* no connection activity */ + } + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#else +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#endif + +static void altera_jtaguart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_jtaguart_console_putc(co, *s); + if (*s == '\n') + altera_jtaguart_console_putc(co, '\r'); + } +} + +static int __init altera_jtaguart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + + if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS) + return -EINVAL; + port = &altera_jtaguart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + return 0; +} + +static struct uart_driver altera_jtaguart_driver; + +static struct console altera_jtaguart_console = { + .name = "ttyJ", + .write = altera_jtaguart_console_write, + .device = uart_console_device, + .setup = altera_jtaguart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_jtaguart_driver, +}; + +static int __init altera_jtaguart_console_init(void) +{ + register_console(&altera_jtaguart_console); + return 0; +} + +console_initcall(altera_jtaguart_console_init); + +#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) + +#else + +#define ALTERA_JTAGUART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ + +static struct uart_driver altera_jtaguart_driver = { + .owner = THIS_MODULE, + .driver_name = "altera_jtaguart", + .dev_name = "ttyJ", + .major = ALTERA_JTAGUART_MAJOR, + .minor = ALTERA_JTAGUART_MINOR, + .nr = ALTERA_JTAGUART_MAXPORTS, + .cons = ALTERA_JTAGUART_CONSOLE, +}; + +static int __devinit altera_jtaguart_probe(struct platform_device *pdev) +{ + struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->ops = &altera_jtaguart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static int __devexit altera_jtaguart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS; i++) { + port = &altera_jtaguart_ports[i].port; + if (port) + uart_remove_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_jtaguart_platform_driver = { + .probe = altera_jtaguart_probe, + .remove = __devexit_p(altera_jtaguart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init altera_jtaguart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_jtaguart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_jtaguart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_jtaguart_driver); + return rc; + } + return 0; +} + +static void __exit altera_jtaguart_exit(void) +{ + platform_driver_unregister(&altera_jtaguart_platform_driver); + uart_unregister_driver(&altera_jtaguart_driver); +} + +module_init(altera_jtaguart_init); +module_exit(altera_jtaguart_exit); + +MODULE_DESCRIPTION("Altera JTAG UART driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c new file mode 100644 index 000000000000..721216292a50 --- /dev/null +++ b/drivers/tty/serial/altera_uart.c @@ -0,0 +1,608 @@ +/* + * altera_uart.c -- Altera UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer + * (C) Copyright 2008, Thomas Chou + * (C) Copyright 2010, Tobias Klauser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "altera_uart" +#define SERIAL_ALTERA_MAJOR 204 +#define SERIAL_ALTERA_MINOR 213 + +/* + * Altera UART register definitions according to the Nios UART datasheet: + * http://www.altera.com/literature/ds/ds_nios_uart.pdf + */ + +#define ALTERA_UART_SIZE 32 + +#define ALTERA_UART_RXDATA_REG 0 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_CONTROL_REG 12 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_EOP_REG 20 + +#define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */ +#define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */ +#define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */ +#define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */ +#define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */ +#define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */ +#define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */ +#define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */ +#define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */ +#define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */ +#define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */ +#define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */ + + /* Enable interrupt on... */ +#define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */ +#define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */ +#define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */ +#define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */ +#define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */ +#define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */ +#define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */ +#define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */ +#define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/ + +#define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */ +#define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */ +#define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */ +#define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */ + +/* + * Local per-uart structure. + */ +struct altera_uart { + struct uart_port port; + struct timer_list tmr; + unsigned int sigs; /* Local copy of line sigs */ + unsigned short imr; /* Local IMR mirror */ +}; + +static u32 altera_uart_readl(struct uart_port *port, int reg) +{ + struct altera_uart_platform_uart *platp = port->private_data; + + return readl(port->membase + (reg << platp->bus_shift)); +} + +static void altera_uart_writel(struct uart_port *port, u32 dat, int reg) +{ + struct altera_uart_platform_uart *platp = port->private_data; + + writel(dat, port->membase + (reg << platp->bus_shift)); +} + +static unsigned int altera_uart_tx_empty(struct uart_port *port) +{ + return (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_uart_get_mctrl(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int sigs; + + sigs = (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0; + sigs |= (pp->sigs & TIOCM_RTS); + + return sigs; +} + +static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->sigs = sigs; + if (sigs & TIOCM_RTS) + pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_start_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_stop_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_stop_rx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_enable_ms(struct uart_port *port) +{ +} + +static void altera_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; + + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + baudclk = port->uartclk / baud; + + if (old) + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); + altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_rx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned short status; + + while ((status = altera_uart_readl(port, ALTERA_UART_STATUS_REG)) & + ALTERA_UART_STATUS_RRDY_MSK) { + ch = altera_uart_readl(port, ALTERA_UART_RXDATA_REG); + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & ALTERA_UART_STATUS_E_MSK) { + altera_uart_writel(port, status, + ALTERA_UART_STATUS_REG); + + if (status & ALTERA_UART_STATUS_BRK_MSK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & ALTERA_UART_STATUS_PE_MSK) { + port->icount.parity++; + } else if (status & ALTERA_UART_STATUS_ROE_MSK) { + port->icount.overrun++; + } else if (status & ALTERA_UART_STATUS_FE_MSK) { + port->icount.frame++; + } + + status &= port->read_status_mask; + + if (status & ALTERA_UART_STATUS_BRK_MSK) + flag = TTY_BREAK; + else if (status & ALTERA_UART_STATUS_PE_MSK) + flag = TTY_PARITY; + else if (status & ALTERA_UART_STATUS_FE_MSK) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch, + flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_uart_tx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + /* Send special char - probably flow control */ + altera_uart_writel(port, port->x_char, ALTERA_UART_TXDATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + while (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) { + if (xmit->head == xmit->tail) + break; + altera_uart_writel(port, xmit->buf[xmit->tail], + ALTERA_UART_TXDATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (xmit->head == xmit->tail) { + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + } +} + +static irqreturn_t altera_uart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int isr; + + isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr; + + spin_lock(&port->lock); + if (isr & ALTERA_UART_STATUS_RRDY_MSK) + altera_uart_rx_chars(pp); + if (isr & ALTERA_UART_STATUS_TRDY_MSK) + altera_uart_tx_chars(pp); + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_uart_timer(unsigned long data) +{ + struct uart_port *port = (void *)data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + altera_uart_interrupt(0, port); + mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); +} + +static void altera_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_UART; + + /* Clear mask, so no surprise interrupts. */ + altera_uart_writel(port, 0, ALTERA_UART_CONTROL_REG); + /* Clear status register */ + altera_uart_writel(port, 0, ALTERA_UART_STATUS_REG); +} + +static int altera_uart_startup(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + int ret; + + if (!port->irq) { + setup_timer(&pp->tmr, altera_uart_timer, (unsigned long)port); + mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); + return 0; + } + + ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_uart_shutdown(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + if (port->irq) + free_irq(port->irq, port); + else + del_timer_sync(&pp->tmr); +} + +static const char *altera_uart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL; +} + +static int altera_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_uart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART)) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_uart_ops = { + .tx_empty = altera_uart_tx_empty, + .get_mctrl = altera_uart_get_mctrl, + .set_mctrl = altera_uart_set_mctrl, + .start_tx = altera_uart_start_tx, + .stop_tx = altera_uart_stop_tx, + .stop_rx = altera_uart_stop_rx, + .enable_ms = altera_uart_enable_ms, + .break_ctl = altera_uart_break_ctl, + .startup = altera_uart_startup, + .shutdown = altera_uart_shutdown, + .set_termios = altera_uart_set_termios, + .type = altera_uart_type, + .request_port = altera_uart_request_port, + .release_port = altera_uart_release_port, + .config_port = altera_uart_config_port, + .verify_port = altera_uart_verify_port, +}; + +static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &altera_uart_ops; + port->private_data = platp; + } + + return 0; +} + +static void altera_uart_console_putc(struct uart_port *port, const char c) +{ + while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK)) + cpu_relax(); + + writel(c, port->membase + ALTERA_UART_TXDATA_REG); +} + +static void altera_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &(altera_uart_ports + co->index)->port; + + for (; count; count--, s++) { + altera_uart_console_putc(port, *s); + if (*s == '\n') + altera_uart_console_putc(port, '\r'); + } +} + +static int __init altera_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + port = &altera_uart_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver altera_uart_driver; + +static struct console altera_uart_console = { + .name = "ttyAL", + .write = altera_uart_console_write, + .device = uart_console_device, + .setup = altera_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_uart_driver, +}; + +static int __init altera_uart_console_init(void) +{ + register_console(&altera_uart_console); + return 0; +} + +console_initcall(altera_uart_console_init); + +#define ALTERA_UART_CONSOLE (&altera_uart_console) + +#else + +#define ALTERA_UART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_UART_CONSOLE */ + +/* + * Define the altera_uart UART driver structure. + */ +static struct uart_driver altera_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyAL", + .major = SERIAL_ALTERA_MAJOR, + .minor = SERIAL_ALTERA_MINOR, + .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS, + .cons = ALTERA_UART_CONSOLE, +}; + +static int __devinit altera_uart_probe(struct platform_device *pdev) +{ + struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + struct resource *res_mem; + struct resource *res_irq; + int i = pdev->id; + + /* -1 emphasizes that the platform must have one port, no .N suffix */ + if (i == -1) + i = 0; + + if (i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + + port = &altera_uart_ports[i].port; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_mem) + port->mapbase = res_mem->start; + else if (platp->mapbase) + port->mapbase = platp->mapbase; + else + return -EINVAL; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res_irq) + port->irq = res_irq->start; + else if (platp->irq) + port->irq = platp->irq; + + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + if (!port->membase) + return -ENOMEM; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->iotype = SERIAL_IO_MEM; + port->uartclk = platp->uartclk; + port->ops = &altera_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->private_data = platp; + + uart_add_one_port(&altera_uart_driver, port); + + return 0; +} + +static int __devexit altera_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port = &altera_uart_ports[pdev->id].port; + + uart_remove_one_port(&altera_uart_driver, port); + return 0; +} + +static struct platform_driver altera_uart_platform_driver = { + .probe = altera_uart_probe, + .remove = __devexit_p(altera_uart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, +}; + +static int __init altera_uart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_uart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_uart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_uart_driver); + return rc; + } + return 0; +} + +static void __exit altera_uart_exit(void) +{ + platform_driver_unregister(&altera_uart_platform_driver); + uart_unregister_driver(&altera_uart_driver); +} + +module_init(altera_uart_init); +module_exit(altera_uart_exit); + +MODULE_DESCRIPTION("Altera UART driver"); +MODULE_AUTHOR("Thomas Chou "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_ALTERA_MAJOR); diff --git a/drivers/tty/serial/amba-pl010.c b/drivers/tty/serial/amba-pl010.c new file mode 100644 index 000000000000..2904aa044126 --- /dev/null +++ b/drivers/tty/serial/amba-pl010.c @@ -0,0 +1,825 @@ +/* + * linux/drivers/char/amba.c + * + * Driver for AMBA serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * 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 + * + * This is a generic driver for ARM AMBA-type serial ports. They + * have a lot of 16550-like features, but are not register compatible. + * Note that although they do have CTS, DCD and DSR inputs, they do + * not have an RI input, nor do they have DTR or RTS outputs. If + * required, these have to be supplied via some other means (eg, GPIO) + * and hooked into this driver. + */ + +#if defined(CONFIG_SERIAL_AMBA_PL010_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UART_NR 8 + +#define SERIAL_AMBA_MAJOR 204 +#define SERIAL_AMBA_MINOR 16 +#define SERIAL_AMBA_NR UART_NR + +#define AMBA_ISR_PASS_LIMIT 256 + +#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0) +#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0) + +#define UART_DUMMY_RSR_RX 256 +#define UART_PORT_SIZE 64 + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_amba_port { + struct uart_port port; + struct clk *clk; + struct amba_device *dev; + struct amba_pl010_data *data; + unsigned int old_status; +}; + +static void pl010_stop_tx(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + + cr = readb(uap->port.membase + UART010_CR); + cr &= ~UART010_CR_TIE; + writel(cr, uap->port.membase + UART010_CR); +} + +static void pl010_start_tx(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + + cr = readb(uap->port.membase + UART010_CR); + cr |= UART010_CR_TIE; + writel(cr, uap->port.membase + UART010_CR); +} + +static void pl010_stop_rx(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + + cr = readb(uap->port.membase + UART010_CR); + cr &= ~(UART010_CR_RIE | UART010_CR_RTIE); + writel(cr, uap->port.membase + UART010_CR); +} + +static void pl010_enable_ms(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + + cr = readb(uap->port.membase + UART010_CR); + cr |= UART010_CR_MSIE; + writel(cr, uap->port.membase + UART010_CR); +} + +static void pl010_rx_chars(struct uart_amba_port *uap) +{ + struct tty_struct *tty = uap->port.state->port.tty; + unsigned int status, ch, flag, rsr, max_count = 256; + + status = readb(uap->port.membase + UART01x_FR); + while (UART_RX_DATA(status) && max_count--) { + ch = readb(uap->port.membase + UART01x_DR); + flag = TTY_NORMAL; + + uap->port.icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + rsr = readb(uap->port.membase + UART01x_RSR) | UART_DUMMY_RSR_RX; + if (unlikely(rsr & UART01x_RSR_ANY)) { + writel(0, uap->port.membase + UART01x_ECR); + + if (rsr & UART01x_RSR_BE) { + rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE); + uap->port.icount.brk++; + if (uart_handle_break(&uap->port)) + goto ignore_char; + } else if (rsr & UART01x_RSR_PE) + uap->port.icount.parity++; + else if (rsr & UART01x_RSR_FE) + uap->port.icount.frame++; + if (rsr & UART01x_RSR_OE) + uap->port.icount.overrun++; + + rsr &= uap->port.read_status_mask; + + if (rsr & UART01x_RSR_BE) + flag = TTY_BREAK; + else if (rsr & UART01x_RSR_PE) + flag = TTY_PARITY; + else if (rsr & UART01x_RSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&uap->port, ch)) + goto ignore_char; + + uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag); + + ignore_char: + status = readb(uap->port.membase + UART01x_FR); + } + spin_unlock(&uap->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&uap->port.lock); +} + +static void pl010_tx_chars(struct uart_amba_port *uap) +{ + struct circ_buf *xmit = &uap->port.state->xmit; + int count; + + if (uap->port.x_char) { + writel(uap->port.x_char, uap->port.membase + UART01x_DR); + uap->port.icount.tx++; + uap->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { + pl010_stop_tx(&uap->port); + return; + } + + count = uap->port.fifosize >> 1; + do { + writel(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uap->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + + if (uart_circ_empty(xmit)) + pl010_stop_tx(&uap->port); +} + +static void pl010_modem_status(struct uart_amba_port *uap) +{ + unsigned int status, delta; + + writel(0, uap->port.membase + UART010_ICR); + + status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + + delta = status ^ uap->old_status; + uap->old_status = status; + + if (!delta) + return; + + if (delta & UART01x_FR_DCD) + uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); + + if (delta & UART01x_FR_DSR) + uap->port.icount.dsr++; + + if (delta & UART01x_FR_CTS) + uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); + + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); +} + +static irqreturn_t pl010_int(int irq, void *dev_id) +{ + struct uart_amba_port *uap = dev_id; + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + int handled = 0; + + spin_lock(&uap->port.lock); + + status = readb(uap->port.membase + UART010_IIR); + if (status) { + do { + if (status & (UART010_IIR_RTIS | UART010_IIR_RIS)) + pl010_rx_chars(uap); + if (status & UART010_IIR_MIS) + pl010_modem_status(uap); + if (status & UART010_IIR_TIS) + pl010_tx_chars(uap); + + if (pass_counter-- == 0) + break; + + status = readb(uap->port.membase + UART010_IIR); + } while (status & (UART010_IIR_RTIS | UART010_IIR_RIS | + UART010_IIR_TIS)); + handled = 1; + } + + spin_unlock(&uap->port.lock); + + return IRQ_RETVAL(handled); +} + +static unsigned int pl010_tx_empty(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int status = readb(uap->port.membase + UART01x_FR); + return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT; +} + +static unsigned int pl010_get_mctrl(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int result = 0; + unsigned int status; + + status = readb(uap->port.membase + UART01x_FR); + if (status & UART01x_FR_DCD) + result |= TIOCM_CAR; + if (status & UART01x_FR_DSR) + result |= TIOCM_DSR; + if (status & UART01x_FR_CTS) + result |= TIOCM_CTS; + + return result; +} + +static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + if (uap->data) + uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl); +} + +static void pl010_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned long flags; + unsigned int lcr_h; + + spin_lock_irqsave(&uap->port.lock, flags); + lcr_h = readb(uap->port.membase + UART010_LCRH); + if (break_state == -1) + lcr_h |= UART01x_LCRH_BRK; + else + lcr_h &= ~UART01x_LCRH_BRK; + writel(lcr_h, uap->port.membase + UART010_LCRH); + spin_unlock_irqrestore(&uap->port.lock, flags); +} + +static int pl010_startup(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + int retval; + + /* + * Try to enable the clock producer. + */ + retval = clk_enable(uap->clk); + if (retval) + goto out; + + uap->port.uartclk = clk_get_rate(uap->clk); + + /* + * Allocate the IRQ + */ + retval = request_irq(uap->port.irq, pl010_int, 0, "uart-pl010", uap); + if (retval) + goto clk_dis; + + /* + * initialise the old status of the modem signals + */ + uap->old_status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + + /* + * Finally, enable interrupts + */ + writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE, + uap->port.membase + UART010_CR); + + return 0; + + clk_dis: + clk_disable(uap->clk); + out: + return retval; +} + +static void pl010_shutdown(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + /* + * Free the interrupt + */ + free_irq(uap->port.irq, uap); + + /* + * disable all interrupts, disable the port + */ + writel(0, uap->port.membase + UART010_CR); + + /* disable break condition and fifos */ + writel(readb(uap->port.membase + UART010_LCRH) & + ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN), + uap->port.membase + UART010_LCRH); + + /* + * Shut down the clock producer + */ + clk_disable(uap->clk); +} + +static void +pl010_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int lcr_h, old_cr; + unsigned long flags; + unsigned int baud, quot; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, uap->port.uartclk/16); + quot = uart_get_divisor(port, baud); + + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr_h = UART01x_LCRH_WLEN_5; + break; + case CS6: + lcr_h = UART01x_LCRH_WLEN_6; + break; + case CS7: + lcr_h = UART01x_LCRH_WLEN_7; + break; + default: // CS8 + lcr_h = UART01x_LCRH_WLEN_8; + break; + } + if (termios->c_cflag & CSTOPB) + lcr_h |= UART01x_LCRH_STP2; + if (termios->c_cflag & PARENB) { + lcr_h |= UART01x_LCRH_PEN; + if (!(termios->c_cflag & PARODD)) + lcr_h |= UART01x_LCRH_EPS; + } + if (uap->port.fifosize > 1) + lcr_h |= UART01x_LCRH_FEN; + + spin_lock_irqsave(&uap->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + uap->port.read_status_mask = UART01x_RSR_OE; + if (termios->c_iflag & INPCK) + uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + uap->port.read_status_mask |= UART01x_RSR_BE; + + /* + * Characters to ignore + */ + uap->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + uap->port.ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; + if (termios->c_iflag & IGNBRK) { + uap->port.ignore_status_mask |= UART01x_RSR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + uap->port.ignore_status_mask |= UART01x_RSR_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX; + + /* first, disable everything */ + old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE; + + if (UART_ENABLE_MS(port, termios->c_cflag)) + old_cr |= UART010_CR_MSIE; + + writel(0, uap->port.membase + UART010_CR); + + /* Set baud rate */ + quot -= 1; + writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM); + writel(quot & 0xff, uap->port.membase + UART010_LCRL); + + /* + * ----------v----------v----------v----------v----- + * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L + * ----------^----------^----------^----------^----- + */ + writel(lcr_h, uap->port.membase + UART010_LCRH); + writel(old_cr, uap->port.membase + UART010_CR); + + spin_unlock_irqrestore(&uap->port.lock, flags); +} + +static void pl010_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + pl010_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + +static const char *pl010_type(struct uart_port *port) +{ + return port->type == PORT_AMBA ? "AMBA" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void pl010_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int pl010_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010") + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void pl010_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_AMBA; + pl010_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= nr_irqs) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops amba_pl010_pops = { + .tx_empty = pl010_tx_empty, + .set_mctrl = pl010_set_mctrl, + .get_mctrl = pl010_get_mctrl, + .stop_tx = pl010_stop_tx, + .start_tx = pl010_start_tx, + .stop_rx = pl010_stop_rx, + .enable_ms = pl010_enable_ms, + .break_ctl = pl010_break_ctl, + .startup = pl010_startup, + .shutdown = pl010_shutdown, + .set_termios = pl010_set_termios, + .set_ldisc = pl010_set_ldisc, + .type = pl010_type, + .release_port = pl010_release_port, + .request_port = pl010_request_port, + .config_port = pl010_config_port, + .verify_port = pl010_verify_port, +}; + +static struct uart_amba_port *amba_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE + +static void pl010_console_putchar(struct uart_port *port, int ch) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int status; + + do { + status = readb(uap->port.membase + UART01x_FR); + barrier(); + } while (!UART_TX_READY(status)); + writel(ch, uap->port.membase + UART01x_DR); +} + +static void +pl010_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_amba_port *uap = amba_ports[co->index]; + unsigned int status, old_cr; + + clk_enable(uap->clk); + + /* + * First save the CR then disable the interrupts + */ + old_cr = readb(uap->port.membase + UART010_CR); + writel(UART01x_CR_UARTEN, uap->port.membase + UART010_CR); + + uart_console_write(&uap->port, s, count, pl010_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = readb(uap->port.membase + UART01x_FR); + barrier(); + } while (status & UART01x_FR_BUSY); + writel(old_cr, uap->port.membase + UART010_CR); + + clk_disable(uap->clk); +} + +static void __init +pl010_console_get_options(struct uart_amba_port *uap, int *baud, + int *parity, int *bits) +{ + if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) { + unsigned int lcr_h, quot; + lcr_h = readb(uap->port.membase + UART010_LCRH); + + *parity = 'n'; + if (lcr_h & UART01x_LCRH_PEN) { + if (lcr_h & UART01x_LCRH_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) + *bits = 7; + else + *bits = 8; + + quot = readb(uap->port.membase + UART010_LCRL) | + readb(uap->port.membase + UART010_LCRM) << 8; + *baud = uap->port.uartclk / (16 * (quot + 1)); + } +} + +static int __init pl010_console_setup(struct console *co, char *options) +{ + struct uart_amba_port *uap; + 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. + */ + if (co->index >= UART_NR) + co->index = 0; + uap = amba_ports[co->index]; + if (!uap) + return -ENODEV; + + uap->port.uartclk = clk_get_rate(uap->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + pl010_console_get_options(uap, &baud, &parity, &bits); + + return uart_set_options(&uap->port, co, baud, parity, bits, flow); +} + +static struct uart_driver amba_reg; +static struct console amba_console = { + .name = "ttyAM", + .write = pl010_console_write, + .device = uart_console_device, + .setup = pl010_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &amba_reg, +}; + +#define AMBA_CONSOLE &amba_console +#else +#define AMBA_CONSOLE NULL +#endif + +static struct uart_driver amba_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyAM", + .dev_name = "ttyAM", + .major = SERIAL_AMBA_MAJOR, + .minor = SERIAL_AMBA_MINOR, + .nr = UART_NR, + .cons = AMBA_CONSOLE, +}; + +static int pl010_probe(struct amba_device *dev, struct amba_id *id) +{ + struct uart_amba_port *uap; + void __iomem *base; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) + if (amba_ports[i] == NULL) + break; + + if (i == ARRAY_SIZE(amba_ports)) { + ret = -EBUSY; + goto out; + } + + uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); + if (!uap) { + ret = -ENOMEM; + goto out; + } + + base = ioremap(dev->res.start, resource_size(&dev->res)); + if (!base) { + ret = -ENOMEM; + goto free; + } + + uap->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(uap->clk)) { + ret = PTR_ERR(uap->clk); + goto unmap; + } + + uap->port.dev = &dev->dev; + uap->port.mapbase = dev->res.start; + uap->port.membase = base; + uap->port.iotype = UPIO_MEM; + uap->port.irq = dev->irq[0]; + uap->port.fifosize = 16; + uap->port.ops = &amba_pl010_pops; + uap->port.flags = UPF_BOOT_AUTOCONF; + uap->port.line = i; + uap->dev = dev; + uap->data = dev->dev.platform_data; + + amba_ports[i] = uap; + + amba_set_drvdata(dev, uap); + ret = uart_add_one_port(&amba_reg, &uap->port); + if (ret) { + amba_set_drvdata(dev, NULL); + amba_ports[i] = NULL; + clk_put(uap->clk); + unmap: + iounmap(base); + free: + kfree(uap); + } + out: + return ret; +} + +static int pl010_remove(struct amba_device *dev) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + int i; + + amba_set_drvdata(dev, NULL); + + uart_remove_one_port(&amba_reg, &uap->port); + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) + if (amba_ports[i] == uap) + amba_ports[i] = NULL; + + iounmap(uap->port.membase); + clk_put(uap->clk); + kfree(uap); + return 0; +} + +static int pl010_suspend(struct amba_device *dev, pm_message_t state) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + + if (uap) + uart_suspend_port(&amba_reg, &uap->port); + + return 0; +} + +static int pl010_resume(struct amba_device *dev) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + + if (uap) + uart_resume_port(&amba_reg, &uap->port); + + return 0; +} + +static struct amba_id pl010_ids[] = { + { + .id = 0x00041010, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl010_driver = { + .drv = { + .name = "uart-pl010", + }, + .id_table = pl010_ids, + .probe = pl010_probe, + .remove = pl010_remove, + .suspend = pl010_suspend, + .resume = pl010_resume, +}; + +static int __init pl010_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: AMBA driver\n"); + + ret = uart_register_driver(&amba_reg); + if (ret == 0) { + ret = amba_driver_register(&pl010_driver); + if (ret) + uart_unregister_driver(&amba_reg); + } + return ret; +} + +static void __exit pl010_exit(void) +{ + amba_driver_unregister(&pl010_driver); + uart_unregister_driver(&amba_reg); +} + +module_init(pl010_init); +module_exit(pl010_exit); + +MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("ARM AMBA serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c new file mode 100644 index 000000000000..e76d7d000128 --- /dev/null +++ b/drivers/tty/serial/amba-pl011.c @@ -0,0 +1,1519 @@ +/* + * linux/drivers/char/amba.c + * + * Driver for AMBA serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * Copyright (C) 2010 ST-Ericsson SA + * + * 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 + * + * This is a generic driver for ARM AMBA-type serial ports. They + * have a lot of 16550-like features, but are not register compatible. + * Note that although they do have CTS, DCD and DSR inputs, they do + * not have an RI input, nor do they have DTR or RTS outputs. If + * required, these have to be supplied via some other means (eg, GPIO) + * and hooked into this driver. + */ + +#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define UART_NR 14 + +#define SERIAL_AMBA_MAJOR 204 +#define SERIAL_AMBA_MINOR 64 +#define SERIAL_AMBA_NR UART_NR + +#define AMBA_ISR_PASS_LIMIT 256 + +#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) +#define UART_DUMMY_DR_RX (1 << 16) + +/* There is by now at least one vendor with differing details, so handle it */ +struct vendor_data { + unsigned int ifls; + unsigned int fifosize; + unsigned int lcrh_tx; + unsigned int lcrh_rx; + bool oversampling; + bool dma_threshold; +}; + +static struct vendor_data vendor_arm = { + .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, + .fifosize = 16, + .lcrh_tx = UART011_LCRH, + .lcrh_rx = UART011_LCRH, + .oversampling = false, + .dma_threshold = false, +}; + +static struct vendor_data vendor_st = { + .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, + .fifosize = 64, + .lcrh_tx = ST_UART011_LCRH_TX, + .lcrh_rx = ST_UART011_LCRH_RX, + .oversampling = true, + .dma_threshold = true, +}; + +/* Deals with DMA transactions */ +struct pl011_dmatx_data { + struct dma_chan *chan; + struct scatterlist sg; + char *buf; + bool queued; +}; + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_amba_port { + struct uart_port port; + struct clk *clk; + const struct vendor_data *vendor; + unsigned int dmacr; /* dma control reg */ + unsigned int im; /* interrupt mask */ + unsigned int old_status; + unsigned int fifosize; /* vendor-specific */ + unsigned int lcrh_tx; /* vendor-specific */ + unsigned int lcrh_rx; /* vendor-specific */ + bool autorts; + char type[12]; +#ifdef CONFIG_DMA_ENGINE + /* DMA stuff */ + bool using_dma; + struct pl011_dmatx_data dmatx; +#endif +}; + +/* + * All the DMA operation mode stuff goes inside this ifdef. + * This assumes that you have a generic DMA device interface, + * no custom DMA interfaces are supported. + */ +#ifdef CONFIG_DMA_ENGINE + +#define PL011_DMA_BUFFER_SIZE PAGE_SIZE + +static void pl011_dma_probe_initcall(struct uart_amba_port *uap) +{ + /* DMA is the sole user of the platform data right now */ + struct amba_pl011_data *plat = uap->port.dev->platform_data; + struct dma_slave_config tx_conf = { + .dst_addr = uap->port.mapbase + UART01x_DR, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .direction = DMA_TO_DEVICE, + .dst_maxburst = uap->fifosize >> 1, + }; + struct dma_chan *chan; + dma_cap_mask_t mask; + + /* We need platform data */ + if (!plat || !plat->dma_filter) { + dev_info(uap->port.dev, "no DMA platform data\n"); + return; + } + + /* Try to acquire a generic DMA engine slave channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); + if (!chan) { + dev_err(uap->port.dev, "no TX DMA channel!\n"); + return; + } + + dmaengine_slave_config(chan, &tx_conf); + uap->dmatx.chan = chan; + + dev_info(uap->port.dev, "DMA channel TX %s\n", + dma_chan_name(uap->dmatx.chan)); +} + +#ifndef MODULE +/* + * Stack up the UARTs and let the above initcall be done at device + * initcall time, because the serial driver is called as an arch + * initcall, and at this time the DMA subsystem is not yet registered. + * At this point the driver will switch over to using DMA where desired. + */ +struct dma_uap { + struct list_head node; + struct uart_amba_port *uap; +}; + +static LIST_HEAD(pl011_dma_uarts); + +static int __init pl011_dma_initcall(void) +{ + struct list_head *node, *tmp; + + list_for_each_safe(node, tmp, &pl011_dma_uarts) { + struct dma_uap *dmau = list_entry(node, struct dma_uap, node); + pl011_dma_probe_initcall(dmau->uap); + list_del(node); + kfree(dmau); + } + return 0; +} + +device_initcall(pl011_dma_initcall); + +static void pl011_dma_probe(struct uart_amba_port *uap) +{ + struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL); + if (dmau) { + dmau->uap = uap; + list_add_tail(&dmau->node, &pl011_dma_uarts); + } +} +#else +static void pl011_dma_probe(struct uart_amba_port *uap) +{ + pl011_dma_probe_initcall(uap); +} +#endif + +static void pl011_dma_remove(struct uart_amba_port *uap) +{ + /* TODO: remove the initcall if it has not yet executed */ + if (uap->dmatx.chan) + dma_release_channel(uap->dmatx.chan); +} + + +/* Forward declare this for the refill routine */ +static int pl011_dma_tx_refill(struct uart_amba_port *uap); + +/* + * The current DMA TX buffer has been sent. + * Try to queue up another DMA buffer. + */ +static void pl011_dma_tx_callback(void *data) +{ + struct uart_amba_port *uap = data; + struct pl011_dmatx_data *dmatx = &uap->dmatx; + unsigned long flags; + u16 dmacr; + + spin_lock_irqsave(&uap->port.lock, flags); + if (uap->dmatx.queued) + dma_unmap_sg(dmatx->chan->device->dev, &dmatx->sg, 1, + DMA_TO_DEVICE); + + dmacr = uap->dmacr; + uap->dmacr = dmacr & ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + /* + * If TX DMA was disabled, it means that we've stopped the DMA for + * some reason (eg, XOFF received, or we want to send an X-char.) + * + * Note: we need to be careful here of a potential race between DMA + * and the rest of the driver - if the driver disables TX DMA while + * a TX buffer completing, we must update the tx queued status to + * get further refills (hence we check dmacr). + */ + if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || + uart_circ_empty(&uap->port.state->xmit)) { + uap->dmatx.queued = false; + spin_unlock_irqrestore(&uap->port.lock, flags); + return; + } + + if (pl011_dma_tx_refill(uap) <= 0) { + /* + * We didn't queue a DMA buffer for some reason, but we + * have data pending to be sent. Re-enable the TX IRQ. + */ + uap->im |= UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + } + spin_unlock_irqrestore(&uap->port.lock, flags); +} + +/* + * Try to refill the TX DMA buffer. + * Locking: called with port lock held and IRQs disabled. + * Returns: + * 1 if we queued up a TX DMA buffer. + * 0 if we didn't want to handle this by DMA + * <0 on error + */ +static int pl011_dma_tx_refill(struct uart_amba_port *uap) +{ + struct pl011_dmatx_data *dmatx = &uap->dmatx; + struct dma_chan *chan = dmatx->chan; + struct dma_device *dma_dev = chan->device; + struct dma_async_tx_descriptor *desc; + struct circ_buf *xmit = &uap->port.state->xmit; + unsigned int count; + + /* + * Try to avoid the overhead involved in using DMA if the + * transaction fits in the first half of the FIFO, by using + * the standard interrupt handling. This ensures that we + * issue a uart_write_wakeup() at the appropriate time. + */ + count = uart_circ_chars_pending(xmit); + if (count < (uap->fifosize >> 1)) { + uap->dmatx.queued = false; + return 0; + } + + /* + * Bodge: don't send the last character by DMA, as this + * will prevent XON from notifying us to restart DMA. + */ + count -= 1; + + /* Else proceed to copy the TX chars to the DMA buffer and fire DMA */ + if (count > PL011_DMA_BUFFER_SIZE) + count = PL011_DMA_BUFFER_SIZE; + + if (xmit->tail < xmit->head) + memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); + else { + size_t first = UART_XMIT_SIZE - xmit->tail; + size_t second = xmit->head; + + memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first); + if (second) + memcpy(&dmatx->buf[first], &xmit->buf[0], second); + } + + dmatx->sg.length = count; + + if (dma_map_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE) != 1) { + uap->dmatx.queued = false; + dev_dbg(uap->port.dev, "unable to map TX DMA\n"); + return -EBUSY; + } + + desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE); + uap->dmatx.queued = false; + /* + * If DMA cannot be used right now, we complete this + * transaction via IRQ and let the TTY layer retry. + */ + dev_dbg(uap->port.dev, "TX DMA busy\n"); + return -EBUSY; + } + + /* Some data to go along to the callback */ + desc->callback = pl011_dma_tx_callback; + desc->callback_param = uap; + + /* All errors should happen at prepare time */ + dmaengine_submit(desc); + + /* Fire the DMA transaction */ + dma_dev->device_issue_pending(chan); + + uap->dmacr |= UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + uap->dmatx.queued = true; + + /* + * Now we know that DMA will fire, so advance the ring buffer + * with the stuff we just dispatched. + */ + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + uap->port.icount.tx += count; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + + return 1; +} + +/* + * We received a transmit interrupt without a pending X-char but with + * pending characters. + * Locking: called with port lock held and IRQs disabled. + * Returns: + * false if we want to use PIO to transmit + * true if we queued a DMA buffer + */ +static bool pl011_dma_tx_irq(struct uart_amba_port *uap) +{ + if (!uap->using_dma) + return false; + + /* + * If we already have a TX buffer queued, but received a + * TX interrupt, it will be because we've just sent an X-char. + * Ensure the TX DMA is enabled and the TX IRQ is disabled. + */ + if (uap->dmatx.queued) { + uap->dmacr |= UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + uap->im &= ~UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + return true; + } + + /* + * We don't have a TX buffer queued, so try to queue one. + * If we succesfully queued a buffer, mask the TX IRQ. + */ + if (pl011_dma_tx_refill(uap) > 0) { + uap->im &= ~UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + return true; + } + return false; +} + +/* + * Stop the DMA transmit (eg, due to received XOFF). + * Locking: called with port lock held and IRQs disabled. + */ +static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) +{ + if (uap->dmatx.queued) { + uap->dmacr &= ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + } +} + +/* + * Try to start a DMA transmit, or in the case of an XON/OFF + * character queued for send, try to get that character out ASAP. + * Locking: called with port lock held and IRQs disabled. + * Returns: + * false if we want the TX IRQ to be enabled + * true if we have a buffer queued + */ +static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) +{ + u16 dmacr; + + if (!uap->using_dma) + return false; + + if (!uap->port.x_char) { + /* no X-char, try to push chars out in DMA mode */ + bool ret = true; + + if (!uap->dmatx.queued) { + if (pl011_dma_tx_refill(uap) > 0) { + uap->im &= ~UART011_TXIM; + ret = true; + } else { + uap->im |= UART011_TXIM; + ret = false; + } + writew(uap->im, uap->port.membase + UART011_IMSC); + } else if (!(uap->dmacr & UART011_TXDMAE)) { + uap->dmacr |= UART011_TXDMAE; + writew(uap->dmacr, + uap->port.membase + UART011_DMACR); + } + return ret; + } + + /* + * We have an X-char to send. Disable DMA to prevent it loading + * the TX fifo, and then see if we can stuff it into the FIFO. + */ + dmacr = uap->dmacr; + uap->dmacr &= ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) { + /* + * No space in the FIFO, so enable the transmit interrupt + * so we know when there is space. Note that once we've + * loaded the character, we should just re-enable DMA. + */ + return false; + } + + writew(uap->port.x_char, uap->port.membase + UART01x_DR); + uap->port.icount.tx++; + uap->port.x_char = 0; + + /* Success - restore the DMA state */ + uap->dmacr = dmacr; + writew(dmacr, uap->port.membase + UART011_DMACR); + + return true; +} + +/* + * Flush the transmit buffer. + * Locking: called with port lock held and IRQs disabled. + */ +static void pl011_dma_flush_buffer(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + if (!uap->using_dma) + return; + + /* Avoid deadlock with the DMA engine callback */ + spin_unlock(&uap->port.lock); + dmaengine_terminate_all(uap->dmatx.chan); + spin_lock(&uap->port.lock); + if (uap->dmatx.queued) { + dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1, + DMA_TO_DEVICE); + uap->dmatx.queued = false; + uap->dmacr &= ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + } +} + + +static void pl011_dma_startup(struct uart_amba_port *uap) +{ + if (!uap->dmatx.chan) + return; + + uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); + if (!uap->dmatx.buf) { + dev_err(uap->port.dev, "no memory for DMA TX buffer\n"); + uap->port.fifosize = uap->fifosize; + return; + } + + sg_init_one(&uap->dmatx.sg, uap->dmatx.buf, PL011_DMA_BUFFER_SIZE); + + /* The DMA buffer is now the FIFO the TTY subsystem can use */ + uap->port.fifosize = PL011_DMA_BUFFER_SIZE; + uap->using_dma = true; + + /* Turn on DMA error (RX/TX will be enabled on demand) */ + uap->dmacr |= UART011_DMAONERR; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + /* + * ST Micro variants has some specific dma burst threshold + * compensation. Set this to 16 bytes, so burst will only + * be issued above/below 16 bytes. + */ + if (uap->vendor->dma_threshold) + writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16, + uap->port.membase + ST_UART011_DMAWM); +} + +static void pl011_dma_shutdown(struct uart_amba_port *uap) +{ + if (!uap->using_dma) + return; + + /* Disable RX and TX DMA */ + while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) + barrier(); + + spin_lock_irq(&uap->port.lock); + uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE); + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + spin_unlock_irq(&uap->port.lock); + + /* In theory, this should already be done by pl011_dma_flush_buffer */ + dmaengine_terminate_all(uap->dmatx.chan); + if (uap->dmatx.queued) { + dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1, + DMA_TO_DEVICE); + uap->dmatx.queued = false; + } + + kfree(uap->dmatx.buf); + + uap->using_dma = false; +} + +#else +/* Blank functions if the DMA engine is not available */ +static inline void pl011_dma_probe(struct uart_amba_port *uap) +{ +} + +static inline void pl011_dma_remove(struct uart_amba_port *uap) +{ +} + +static inline void pl011_dma_startup(struct uart_amba_port *uap) +{ +} + +static inline void pl011_dma_shutdown(struct uart_amba_port *uap) +{ +} + +static inline bool pl011_dma_tx_irq(struct uart_amba_port *uap) +{ + return false; +} + +static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) +{ +} + +static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) +{ + return false; +} + +#define pl011_dma_flush_buffer NULL +#endif + + +static void pl011_stop_tx(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + uap->im &= ~UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_dma_tx_stop(uap); +} + +static void pl011_start_tx(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + if (!pl011_dma_tx_start(uap)) { + uap->im |= UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + } +} + +static void pl011_stop_rx(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM| + UART011_PEIM|UART011_BEIM|UART011_OEIM); + writew(uap->im, uap->port.membase + UART011_IMSC); +} + +static void pl011_enable_ms(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM; + writew(uap->im, uap->port.membase + UART011_IMSC); +} + +static void pl011_rx_chars(struct uart_amba_port *uap) +{ + struct tty_struct *tty = uap->port.state->port.tty; + unsigned int status, ch, flag, max_count = 256; + + status = readw(uap->port.membase + UART01x_FR); + while ((status & UART01x_FR_RXFE) == 0 && max_count--) { + ch = readw(uap->port.membase + UART01x_DR) | UART_DUMMY_DR_RX; + flag = TTY_NORMAL; + uap->port.icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + if (unlikely(ch & UART_DR_ERROR)) { + if (ch & UART011_DR_BE) { + ch &= ~(UART011_DR_FE | UART011_DR_PE); + uap->port.icount.brk++; + if (uart_handle_break(&uap->port)) + goto ignore_char; + } else if (ch & UART011_DR_PE) + uap->port.icount.parity++; + else if (ch & UART011_DR_FE) + uap->port.icount.frame++; + if (ch & UART011_DR_OE) + uap->port.icount.overrun++; + + ch &= uap->port.read_status_mask; + + if (ch & UART011_DR_BE) + flag = TTY_BREAK; + else if (ch & UART011_DR_PE) + flag = TTY_PARITY; + else if (ch & UART011_DR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&uap->port, ch & 255)) + goto ignore_char; + + uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag); + + ignore_char: + status = readw(uap->port.membase + UART01x_FR); + } + spin_unlock(&uap->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&uap->port.lock); +} + +static void pl011_tx_chars(struct uart_amba_port *uap) +{ + struct circ_buf *xmit = &uap->port.state->xmit; + int count; + + if (uap->port.x_char) { + writew(uap->port.x_char, uap->port.membase + UART01x_DR); + uap->port.icount.tx++; + uap->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { + pl011_stop_tx(&uap->port); + return; + } + + /* If we are using DMA mode, try to send some characters. */ + if (pl011_dma_tx_irq(uap)) + return; + + count = uap->fifosize >> 1; + do { + writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uap->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + + if (uart_circ_empty(xmit)) + pl011_stop_tx(&uap->port); +} + +static void pl011_modem_status(struct uart_amba_port *uap) +{ + unsigned int status, delta; + + status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + + delta = status ^ uap->old_status; + uap->old_status = status; + + if (!delta) + return; + + if (delta & UART01x_FR_DCD) + uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); + + if (delta & UART01x_FR_DSR) + uap->port.icount.dsr++; + + if (delta & UART01x_FR_CTS) + uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); + + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); +} + +static irqreturn_t pl011_int(int irq, void *dev_id) +{ + struct uart_amba_port *uap = dev_id; + unsigned long flags; + unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; + int handled = 0; + + spin_lock_irqsave(&uap->port.lock, flags); + + status = readw(uap->port.membase + UART011_MIS); + if (status) { + do { + writew(status & ~(UART011_TXIS|UART011_RTIS| + UART011_RXIS), + uap->port.membase + UART011_ICR); + + if (status & (UART011_RTIS|UART011_RXIS)) + pl011_rx_chars(uap); + if (status & (UART011_DSRMIS|UART011_DCDMIS| + UART011_CTSMIS|UART011_RIMIS)) + pl011_modem_status(uap); + if (status & UART011_TXIS) + pl011_tx_chars(uap); + + if (pass_counter-- == 0) + break; + + status = readw(uap->port.membase + UART011_MIS); + } while (status != 0); + handled = 1; + } + + spin_unlock_irqrestore(&uap->port.lock, flags); + + return IRQ_RETVAL(handled); +} + +static unsigned int pl01x_tx_empty(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int status = readw(uap->port.membase + UART01x_FR); + return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT; +} + +static unsigned int pl01x_get_mctrl(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int result = 0; + unsigned int status = readw(uap->port.membase + UART01x_FR); + +#define TIOCMBIT(uartbit, tiocmbit) \ + if (status & uartbit) \ + result |= tiocmbit + + TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR); + TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR); + TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS); + TIOCMBIT(UART011_FR_RI, TIOCM_RNG); +#undef TIOCMBIT + return result; +} + +static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + + cr = readw(uap->port.membase + UART011_CR); + +#define TIOCMBIT(tiocmbit, uartbit) \ + if (mctrl & tiocmbit) \ + cr |= uartbit; \ + else \ + cr &= ~uartbit + + TIOCMBIT(TIOCM_RTS, UART011_CR_RTS); + TIOCMBIT(TIOCM_DTR, UART011_CR_DTR); + TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1); + TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2); + TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE); + + if (uap->autorts) { + /* We need to disable auto-RTS if we want to turn RTS off */ + TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN); + } +#undef TIOCMBIT + + writew(cr, uap->port.membase + UART011_CR); +} + +static void pl011_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned long flags; + unsigned int lcr_h; + + spin_lock_irqsave(&uap->port.lock, flags); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); + if (break_state == -1) + lcr_h |= UART01x_LCRH_BRK; + else + lcr_h &= ~UART01x_LCRH_BRK; + writew(lcr_h, uap->port.membase + uap->lcrh_tx); + spin_unlock_irqrestore(&uap->port.lock, flags); +} + +#ifdef CONFIG_CONSOLE_POLL +static int pl010_get_poll_char(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int status; + + status = readw(uap->port.membase + UART01x_FR); + if (status & UART01x_FR_RXFE) + return NO_POLL_CHAR; + + return readw(uap->port.membase + UART01x_DR); +} + +static void pl010_put_poll_char(struct uart_port *port, + unsigned char ch) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) + barrier(); + + writew(ch, uap->port.membase + UART01x_DR); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static int pl011_startup(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int cr; + int retval; + + /* + * Try to enable the clock producer. + */ + retval = clk_enable(uap->clk); + if (retval) + goto out; + + uap->port.uartclk = clk_get_rate(uap->clk); + + /* + * Allocate the IRQ + */ + retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap); + if (retval) + goto clk_dis; + + writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); + + /* + * Provoke TX FIFO interrupt into asserting. + */ + cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE; + writew(cr, uap->port.membase + UART011_CR); + writew(0, uap->port.membase + UART011_FBRD); + writew(1, uap->port.membase + UART011_IBRD); + writew(0, uap->port.membase + uap->lcrh_rx); + if (uap->lcrh_tx != uap->lcrh_rx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(0, uap->port.membase + uap->lcrh_tx); + } + writew(0, uap->port.membase + UART01x_DR); + while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) + barrier(); + + cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; + writew(cr, uap->port.membase + UART011_CR); + + /* Clear pending error interrupts */ + writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS, + uap->port.membase + UART011_ICR); + + /* + * initialise the old status of the modem signals + */ + uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + + /* Startup DMA */ + pl011_dma_startup(uap); + + /* + * Finally, enable interrupts + */ + spin_lock_irq(&uap->port.lock); + uap->im = UART011_RXIM | UART011_RTIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + spin_unlock_irq(&uap->port.lock); + + return 0; + + clk_dis: + clk_disable(uap->clk); + out: + return retval; +} + +static void pl011_shutdown_channel(struct uart_amba_port *uap, + unsigned int lcrh) +{ + unsigned long val; + + val = readw(uap->port.membase + lcrh); + val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); + writew(val, uap->port.membase + lcrh); +} + +static void pl011_shutdown(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + /* + * disable all interrupts + */ + spin_lock_irq(&uap->port.lock); + uap->im = 0; + writew(uap->im, uap->port.membase + UART011_IMSC); + writew(0xffff, uap->port.membase + UART011_ICR); + spin_unlock_irq(&uap->port.lock); + + pl011_dma_shutdown(uap); + + /* + * Free the interrupt + */ + free_irq(uap->port.irq, uap); + + /* + * disable the port + */ + uap->autorts = false; + writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR); + + /* + * disable break condition and fifos + */ + pl011_shutdown_channel(uap, uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) + pl011_shutdown_channel(uap, uap->lcrh_tx); + + /* + * Shut down the clock producer + */ + clk_disable(uap->clk); +} + +static void +pl011_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + unsigned int lcr_h, old_cr; + unsigned long flags; + unsigned int baud, quot, clkdiv; + + if (uap->vendor->oversampling) + clkdiv = 8; + else + clkdiv = 16; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / clkdiv); + + if (baud > port->uartclk/16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); + + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr_h = UART01x_LCRH_WLEN_5; + break; + case CS6: + lcr_h = UART01x_LCRH_WLEN_6; + break; + case CS7: + lcr_h = UART01x_LCRH_WLEN_7; + break; + default: // CS8 + lcr_h = UART01x_LCRH_WLEN_8; + break; + } + if (termios->c_cflag & CSTOPB) + lcr_h |= UART01x_LCRH_STP2; + if (termios->c_cflag & PARENB) { + lcr_h |= UART01x_LCRH_PEN; + if (!(termios->c_cflag & PARODD)) + lcr_h |= UART01x_LCRH_EPS; + } + if (uap->fifosize > 1) + lcr_h |= UART01x_LCRH_FEN; + + spin_lock_irqsave(&port->lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = UART011_DR_OE | 255; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART011_DR_FE | UART011_DR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART011_DR_BE; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= UART011_DR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART011_DR_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_DR_RX; + + if (UART_ENABLE_MS(port, termios->c_cflag)) + pl011_enable_ms(port); + + /* first, disable everything */ + old_cr = readw(port->membase + UART011_CR); + writew(0, port->membase + UART011_CR); + + if (termios->c_cflag & CRTSCTS) { + if (old_cr & UART011_CR_RTS) + old_cr |= UART011_CR_RTSEN; + + old_cr |= UART011_CR_CTSEN; + uap->autorts = true; + } else { + old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN); + uap->autorts = false; + } + + if (uap->vendor->oversampling) { + if (baud > port->uartclk / 16) + old_cr |= ST_UART011_CR_OVSFACT; + else + old_cr &= ~ST_UART011_CR_OVSFACT; + } + + /* Set baud rate */ + writew(quot & 0x3f, port->membase + UART011_FBRD); + writew(quot >> 6, port->membase + UART011_IBRD); + + /* + * ----------v----------v----------v----------v----- + * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L + * ----------^----------^----------^----------^----- + */ + writew(lcr_h, port->membase + uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(lcr_h, port->membase + uap->lcrh_tx); + } + writew(old_cr, port->membase + UART011_CR); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *pl011_type(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + return uap->port.type == PORT_AMBA ? uap->type : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void pl010_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, SZ_4K); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int pl010_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, SZ_4K, "uart-pl011") + != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void pl010_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_AMBA; + pl010_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= nr_irqs) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops amba_pl011_pops = { + .tx_empty = pl01x_tx_empty, + .set_mctrl = pl011_set_mctrl, + .get_mctrl = pl01x_get_mctrl, + .stop_tx = pl011_stop_tx, + .start_tx = pl011_start_tx, + .stop_rx = pl011_stop_rx, + .enable_ms = pl011_enable_ms, + .break_ctl = pl011_break_ctl, + .startup = pl011_startup, + .shutdown = pl011_shutdown, + .flush_buffer = pl011_dma_flush_buffer, + .set_termios = pl011_set_termios, + .type = pl011_type, + .release_port = pl010_release_port, + .request_port = pl010_request_port, + .config_port = pl010_config_port, + .verify_port = pl010_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pl010_get_poll_char, + .poll_put_char = pl010_put_poll_char, +#endif +}; + +static struct uart_amba_port *amba_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE + +static void pl011_console_putchar(struct uart_port *port, int ch) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) + barrier(); + writew(ch, uap->port.membase + UART01x_DR); +} + +static void +pl011_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_amba_port *uap = amba_ports[co->index]; + unsigned int status, old_cr, new_cr; + + clk_enable(uap->clk); + + /* + * First save the CR then disable the interrupts + */ + old_cr = readw(uap->port.membase + UART011_CR); + new_cr = old_cr & ~UART011_CR_CTSEN; + new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE; + writew(new_cr, uap->port.membase + UART011_CR); + + uart_console_write(&uap->port, s, count, pl011_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = readw(uap->port.membase + UART01x_FR); + } while (status & UART01x_FR_BUSY); + writew(old_cr, uap->port.membase + UART011_CR); + + clk_disable(uap->clk); +} + +static void __init +pl011_console_get_options(struct uart_amba_port *uap, int *baud, + int *parity, int *bits) +{ + if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) { + unsigned int lcr_h, ibrd, fbrd; + + lcr_h = readw(uap->port.membase + uap->lcrh_tx); + + *parity = 'n'; + if (lcr_h & UART01x_LCRH_PEN) { + if (lcr_h & UART01x_LCRH_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) + *bits = 7; + else + *bits = 8; + + ibrd = readw(uap->port.membase + UART011_IBRD); + fbrd = readw(uap->port.membase + UART011_FBRD); + + *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + + if (uap->vendor->oversampling) { + if (readw(uap->port.membase + UART011_CR) + & ST_UART011_CR_OVSFACT) + *baud *= 2; + } + } +} + +static int __init pl011_console_setup(struct console *co, char *options) +{ + struct uart_amba_port *uap; + 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. + */ + if (co->index >= UART_NR) + co->index = 0; + uap = amba_ports[co->index]; + if (!uap) + return -ENODEV; + + uap->port.uartclk = clk_get_rate(uap->clk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + pl011_console_get_options(uap, &baud, &parity, &bits); + + return uart_set_options(&uap->port, co, baud, parity, bits, flow); +} + +static struct uart_driver amba_reg; +static struct console amba_console = { + .name = "ttyAMA", + .write = pl011_console_write, + .device = uart_console_device, + .setup = pl011_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &amba_reg, +}; + +#define AMBA_CONSOLE (&amba_console) +#else +#define AMBA_CONSOLE NULL +#endif + +static struct uart_driver amba_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyAMA", + .dev_name = "ttyAMA", + .major = SERIAL_AMBA_MAJOR, + .minor = SERIAL_AMBA_MINOR, + .nr = UART_NR, + .cons = AMBA_CONSOLE, +}; + +static int pl011_probe(struct amba_device *dev, struct amba_id *id) +{ + struct uart_amba_port *uap; + struct vendor_data *vendor = id->data; + void __iomem *base; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) + if (amba_ports[i] == NULL) + break; + + if (i == ARRAY_SIZE(amba_ports)) { + ret = -EBUSY; + goto out; + } + + uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL); + if (uap == NULL) { + ret = -ENOMEM; + goto out; + } + + base = ioremap(dev->res.start, resource_size(&dev->res)); + if (!base) { + ret = -ENOMEM; + goto free; + } + + uap->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(uap->clk)) { + ret = PTR_ERR(uap->clk); + goto unmap; + } + + uap->vendor = vendor; + uap->lcrh_rx = vendor->lcrh_rx; + uap->lcrh_tx = vendor->lcrh_tx; + uap->fifosize = vendor->fifosize; + uap->port.dev = &dev->dev; + uap->port.mapbase = dev->res.start; + uap->port.membase = base; + uap->port.iotype = UPIO_MEM; + uap->port.irq = dev->irq[0]; + uap->port.fifosize = uap->fifosize; + uap->port.ops = &amba_pl011_pops; + uap->port.flags = UPF_BOOT_AUTOCONF; + uap->port.line = i; + pl011_dma_probe(uap); + + snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); + + amba_ports[i] = uap; + + amba_set_drvdata(dev, uap); + ret = uart_add_one_port(&amba_reg, &uap->port); + if (ret) { + 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; +} + +static int pl011_remove(struct amba_device *dev) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + int i; + + amba_set_drvdata(dev, NULL); + + uart_remove_one_port(&amba_reg, &uap->port); + + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) + if (amba_ports[i] == uap) + amba_ports[i] = NULL; + + pl011_dma_remove(uap); + iounmap(uap->port.membase); + clk_put(uap->clk); + kfree(uap); + return 0; +} + +#ifdef CONFIG_PM +static int pl011_suspend(struct amba_device *dev, pm_message_t state) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + + if (!uap) + return -EINVAL; + + return uart_suspend_port(&amba_reg, &uap->port); +} + +static int pl011_resume(struct amba_device *dev) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + + if (!uap) + return -EINVAL; + + return uart_resume_port(&amba_reg, &uap->port); +} +#endif + +static struct amba_id pl011_ids[] = { + { + .id = 0x00041011, + .mask = 0x000fffff, + .data = &vendor_arm, + }, + { + .id = 0x00380802, + .mask = 0x00ffffff, + .data = &vendor_st, + }, + { 0, 0 }, +}; + +static struct amba_driver pl011_driver = { + .drv = { + .name = "uart-pl011", + }, + .id_table = pl011_ids, + .probe = pl011_probe, + .remove = pl011_remove, +#ifdef CONFIG_PM + .suspend = pl011_suspend, + .resume = pl011_resume, +#endif +}; + +static int __init pl011_init(void) +{ + int ret; + printk(KERN_INFO "Serial: AMBA PL011 UART driver\n"); + + ret = uart_register_driver(&amba_reg); + if (ret == 0) { + ret = amba_driver_register(&pl011_driver); + if (ret) + uart_unregister_driver(&amba_reg); + } + return ret; +} + +static void __exit pl011_exit(void) +{ + amba_driver_unregister(&pl011_driver); + uart_unregister_driver(&amba_reg); +} + +/* + * While this can be a module, if builtin it's most likely the console + * So let's leave module_exit but move module_init to an earlier place + */ +arch_initcall(pl011_init); +module_exit(pl011_exit); + +MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("ARM AMBA serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c new file mode 100644 index 000000000000..095a5d562618 --- /dev/null +++ b/drivers/tty/serial/apbuart.c @@ -0,0 +1,709 @@ +/* + * Driver for GRLIB serial ports (APBUART) + * + * Based on linux/drivers/serial/amba.c + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * Copyright (C) 2003 Konrad Eisele + * Copyright (C) 2006 Daniel Hellstrom , Aeroflex Gaisler AB + * Copyright (C) 2008 Gilead Kutnick + * Copyright (C) 2009 Kristoffer Glembo , Aeroflex Gaisler AB + */ + +#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apbuart.h" + +#define SERIAL_APBUART_MAJOR TTY_MAJOR +#define SERIAL_APBUART_MINOR 64 +#define UART_DUMMY_RSR_RX 0x8000 /* for ignore all read */ + +static void apbuart_tx_chars(struct uart_port *port); + +static void apbuart_stop_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr &= ~UART_CTRL_TI; + UART_PUT_CTRL(port, cr); +} + +static void apbuart_start_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr |= UART_CTRL_TI; + UART_PUT_CTRL(port, cr); + + if (UART_GET_STATUS(port) & UART_STATUS_THE) + apbuart_tx_chars(port); +} + +static void apbuart_stop_rx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr &= ~(UART_CTRL_RI); + UART_PUT_CTRL(port, cr); +} + +static void apbuart_enable_ms(struct uart_port *port) +{ + /* No modem status change interrupts for APBUART */ +} + +static void apbuart_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + unsigned int status, ch, rsr, flag; + unsigned int max_chars = port->fifosize; + + status = UART_GET_STATUS(port); + + while (UART_RX_DATA(status) && (max_chars--)) { + + ch = UART_GET_CHAR(port); + flag = TTY_NORMAL; + + port->icount.rx++; + + rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX; + UART_PUT_STATUS(port, 0); + if (rsr & UART_STATUS_ERR) { + + if (rsr & UART_STATUS_BR) { + rsr &= ~(UART_STATUS_FE | UART_STATUS_PE); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rsr & UART_STATUS_PE) { + port->icount.parity++; + } else if (rsr & UART_STATUS_FE) { + port->icount.frame++; + } + if (rsr & UART_STATUS_OE) + port->icount.overrun++; + + rsr &= port->read_status_mask; + + if (rsr & UART_STATUS_PE) + flag = TTY_PARITY; + else if (rsr & UART_STATUS_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag); + + + ignore_char: + status = UART_GET_STATUS(port); + } + + tty_flip_buffer_push(tty); +} + +static void apbuart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + apbuart_stop_tx(port); + return; + } + + /* amba: fill FIFO */ + count = port->fifosize >> 1; + do { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + apbuart_stop_tx(port); +} + +static irqreturn_t apbuart_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int status; + + spin_lock(&port->lock); + + status = UART_GET_STATUS(port); + if (status & UART_STATUS_DR) + apbuart_rx_chars(port); + if (status & UART_STATUS_THE) + apbuart_tx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int apbuart_tx_empty(struct uart_port *port) +{ + unsigned int status = UART_GET_STATUS(port); + return status & UART_STATUS_THE ? TIOCSER_TEMT : 0; +} + +static unsigned int apbuart_get_mctrl(struct uart_port *port) +{ + /* The GRLIB APBUART handles flow control in hardware */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* The GRLIB APBUART handles flow control in hardware */ +} + +static void apbuart_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support sending break */ +} + +static int apbuart_startup(struct uart_port *port) +{ + int retval; + unsigned int cr; + + /* Allocate the IRQ */ + retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port); + if (retval) + return retval; + + /* Finally, enable interrupts */ + cr = UART_GET_CTRL(port); + UART_PUT_CTRL(port, + cr | UART_CTRL_RE | UART_CTRL_TE | + UART_CTRL_RI | UART_CTRL_TI); + + return 0; +} + +static void apbuart_shutdown(struct uart_port *port) +{ + unsigned int cr; + + /* disable all interrupts, disable the port */ + cr = UART_GET_CTRL(port); + UART_PUT_CTRL(port, + cr & ~(UART_CTRL_RE | UART_CTRL_TE | + UART_CTRL_RI | UART_CTRL_TI)); + + /* Free the interrupt */ + free_irq(port->irq, port); +} + +static void apbuart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int cr; + unsigned long flags; + unsigned int baud, quot; + + /* Ask the core to calculate the divisor for us. */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + if (baud == 0) + panic("invalid baudrate %i\n", port->uartclk / 16); + + /* uart_get_divisor calc a *16 uart freq, apbuart is *8 */ + quot = (uart_get_divisor(port, baud)) * 2; + cr = UART_GET_CTRL(port); + cr &= ~(UART_CTRL_PE | UART_CTRL_PS); + + if (termios->c_cflag & PARENB) { + cr |= UART_CTRL_PE; + if ((termios->c_cflag & PARODD)) + cr |= UART_CTRL_PS; + } + + /* Enable flow control. */ + if (termios->c_cflag & CRTSCTS) + cr |= UART_CTRL_FL; + + spin_lock_irqsave(&port->lock, flags); + + /* Update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = UART_STATUS_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE; + + /* Characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE; + + /* Ignore all characters if CREAD is not set. */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_RSR_RX; + + /* Set baud rate */ + quot -= 1; + UART_PUT_SCAL(port, quot); + UART_PUT_CTRL(port, cr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *apbuart_type(struct uart_port *port) +{ + return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL; +} + +static void apbuart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, 0x100); +} + +static int apbuart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, 0x100, "grlib-apbuart") + != NULL ? 0 : -EBUSY; + return 0; +} + +/* Configure/autoconfigure the port */ +static void apbuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_APBUART; + apbuart_request_port(port); + } +} + +/* Verify the new serial_struct (for TIOCSSERIAL) */ +static int apbuart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops grlib_apbuart_ops = { + .tx_empty = apbuart_tx_empty, + .set_mctrl = apbuart_set_mctrl, + .get_mctrl = apbuart_get_mctrl, + .stop_tx = apbuart_stop_tx, + .start_tx = apbuart_start_tx, + .stop_rx = apbuart_stop_rx, + .enable_ms = apbuart_enable_ms, + .break_ctl = apbuart_break_ctl, + .startup = apbuart_startup, + .shutdown = apbuart_shutdown, + .set_termios = apbuart_set_termios, + .type = apbuart_type, + .release_port = apbuart_release_port, + .request_port = apbuart_request_port, + .config_port = apbuart_config_port, + .verify_port = apbuart_verify_port, +}; + +static struct uart_port grlib_apbuart_ports[UART_NR]; +static struct device_node *grlib_apbuart_nodes[UART_NR]; + +static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber) +{ + int ctrl, loop = 0; + int status; + int fifosize; + unsigned long flags; + + ctrl = UART_GET_CTRL(port); + + /* + * Enable the transceiver and wait for it to be ready to send data. + * Clear interrupts so that this process will not be externally + * interrupted in the middle (which can cause the transceiver to + * drain prematurely). + */ + + local_irq_save(flags); + + UART_PUT_CTRL(port, ctrl | UART_CTRL_TE); + + while (!UART_TX_READY(UART_GET_STATUS(port))) + loop++; + + /* + * Disable the transceiver so data isn't actually sent during the + * actual test. + */ + + UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE)); + + fifosize = 1; + UART_PUT_CHAR(port, 0); + + /* + * So long as transmitting a character increments the tranceivier FIFO + * length the FIFO must be at least that big. These bytes will + * automatically drain off of the FIFO. + */ + + status = UART_GET_STATUS(port); + while (((status >> 20) & 0x3F) == fifosize) { + fifosize++; + UART_PUT_CHAR(port, 0); + status = UART_GET_STATUS(port); + } + + fifosize--; + + UART_PUT_CTRL(port, ctrl); + local_irq_restore(flags); + + if (fifosize == 0) + fifosize = 1; + + return fifosize; +} + +static void apbuart_flush_fifo(struct uart_port *port) +{ + int i; + + for (i = 0; i < port->fifosize; i++) + UART_GET_CHAR(port); +} + + +/* ======================================================================== */ +/* Console driver, if enabled */ +/* ======================================================================== */ + +#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE + +static void apbuart_console_putchar(struct uart_port *port, int ch) +{ + unsigned int status; + do { + status = UART_GET_STATUS(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, ch); +} + +static void +apbuart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &grlib_apbuart_ports[co->index]; + unsigned int status, old_cr, new_cr; + + /* First save the CR then disable the interrupts */ + old_cr = UART_GET_CTRL(port); + new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI); + UART_PUT_CTRL(port, new_cr); + + uart_console_write(port, s, count, apbuart_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = UART_GET_STATUS(port); + } while (!UART_TX_READY(status)); + UART_PUT_CTRL(port, old_cr); +} + +static void __init +apbuart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) { + + unsigned int quot, status; + status = UART_GET_STATUS(port); + + *parity = 'n'; + if (status & UART_CTRL_PE) { + if ((status & UART_CTRL_PS) == 0) + *parity = 'e'; + else + *parity = 'o'; + } + + *bits = 8; + quot = UART_GET_SCAL(port) / 8; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init apbuart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n", + co, co->index, options); + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= grlib_apbuart_port_nr) + co->index = 0; + + port = &grlib_apbuart_ports[co->index]; + + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + apbuart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver grlib_apbuart_driver; + +static struct console grlib_apbuart_console = { + .name = "ttyS", + .write = apbuart_console_write, + .device = uart_console_device, + .setup = apbuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &grlib_apbuart_driver, +}; + + +static int grlib_apbuart_configure(void); + +static int __init apbuart_console_init(void) +{ + if (grlib_apbuart_configure()) + return -ENODEV; + register_console(&grlib_apbuart_console); + return 0; +} + +console_initcall(apbuart_console_init); + +#define APBUART_CONSOLE (&grlib_apbuart_console) +#else +#define APBUART_CONSOLE NULL +#endif + +static struct uart_driver grlib_apbuart_driver = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = SERIAL_APBUART_MAJOR, + .minor = SERIAL_APBUART_MINOR, + .nr = UART_NR, + .cons = APBUART_CONSOLE, +}; + + +/* ======================================================================== */ +/* OF Platform Driver */ +/* ======================================================================== */ + +static int __devinit apbuart_probe(struct platform_device *op, + const struct of_device_id *match) +{ + int i = -1; + struct uart_port *port = NULL; + + i = 0; + for (i = 0; i < grlib_apbuart_port_nr; i++) { + if (op->dev.of_node == grlib_apbuart_nodes[i]) + break; + } + + port = &grlib_apbuart_ports[i]; + port->dev = &op->dev; + + uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port); + + apbuart_flush_fifo((struct uart_port *) port); + + printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n", + (unsigned long long) port->mapbase, port->irq); + return 0; +} + +static struct of_device_id __initdata apbuart_match[] = { + { + .name = "GAISLER_APBUART", + }, + { + .name = "01_00c", + }, + {}, +}; + +static struct of_platform_driver grlib_apbuart_of_driver = { + .probe = apbuart_probe, + .driver = { + .owner = THIS_MODULE, + .name = "grlib-apbuart", + .of_match_table = apbuart_match, + }, +}; + + +static int grlib_apbuart_configure(void) +{ + struct device_node *np, *rp; + const u32 *prop; + int freq_khz, line = 0; + + /* Get bus frequency */ + rp = of_find_node_by_path("/"); + if (!rp) + return -ENODEV; + rp = of_get_next_child(rp, NULL); + if (!rp) + return -ENODEV; + prop = of_get_property(rp, "clock-frequency", NULL); + if (!prop) + return -ENODEV; + freq_khz = *prop; + + for_each_matching_node(np, apbuart_match) { + const int *irqs, *ampopts; + const struct amba_prom_registers *regs; + struct uart_port *port; + unsigned long addr; + + ampopts = of_get_property(np, "ampopts", NULL); + if (ampopts && (*ampopts == 0)) + continue; /* Ignore if used by another OS instance */ + + irqs = of_get_property(np, "interrupts", NULL); + regs = of_get_property(np, "reg", NULL); + + if (!irqs || !regs) + continue; + + grlib_apbuart_nodes[line] = np; + + addr = regs->phys_addr; + + port = &grlib_apbuart_ports[line]; + + port->mapbase = addr; + port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map)); + port->irq = *irqs; + port->iotype = UPIO_MEM; + port->ops = &grlib_apbuart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->line = line; + port->uartclk = freq_khz * 1000; + port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line); + line++; + + /* We support maximum UART_NR uarts ... */ + if (line == UART_NR) + break; + } + + grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line; + return line ? 0 : -ENODEV; +} + +static int __init grlib_apbuart_init(void) +{ + int ret; + + /* Find all APBUARTS in device the tree and initialize their ports */ + ret = grlib_apbuart_configure(); + if (ret) + return ret; + + printk(KERN_INFO "Serial: GRLIB APBUART driver\n"); + + ret = uart_register_driver(&grlib_apbuart_driver); + + if (ret) { + printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", + __FILE__, ret); + return ret; + } + + ret = of_register_platform_driver(&grlib_apbuart_of_driver); + if (ret) { + printk(KERN_ERR + "%s: of_register_platform_driver failed (%i)\n", + __FILE__, ret); + uart_unregister_driver(&grlib_apbuart_driver); + return ret; + } + + return ret; +} + +static void __exit grlib_apbuart_exit(void) +{ + int i; + + for (i = 0; i < grlib_apbuart_port_nr; i++) + uart_remove_one_port(&grlib_apbuart_driver, + &grlib_apbuart_ports[i]); + + uart_unregister_driver(&grlib_apbuart_driver); + of_unregister_platform_driver(&grlib_apbuart_of_driver); +} + +module_init(grlib_apbuart_init); +module_exit(grlib_apbuart_exit); + +MODULE_AUTHOR("Aeroflex Gaisler AB"); +MODULE_DESCRIPTION("GRLIB APBUART serial driver"); +MODULE_VERSION("2.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/apbuart.h b/drivers/tty/serial/apbuart.h new file mode 100644 index 000000000000..5faf87c8d2bc --- /dev/null +++ b/drivers/tty/serial/apbuart.h @@ -0,0 +1,64 @@ +#ifndef __GRLIB_APBUART_H__ +#define __GRLIB_APBUART_H__ + +#include + +#define UART_NR 8 +static int grlib_apbuart_port_nr; + +struct grlib_apbuart_regs_map { + u32 data; + u32 status; + u32 ctrl; + u32 scaler; +}; + +struct amba_prom_registers { + unsigned int phys_addr; + unsigned int reg_size; +}; + +/* + * The following defines the bits in the APBUART Status Registers. + */ +#define UART_STATUS_DR 0x00000001 /* Data Ready */ +#define UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */ +#define UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */ +#define UART_STATUS_BR 0x00000008 /* Break Error */ +#define UART_STATUS_OE 0x00000010 /* RX Overrun Error */ +#define UART_STATUS_PE 0x00000020 /* RX Parity Error */ +#define UART_STATUS_FE 0x00000040 /* RX Framing Error */ +#define UART_STATUS_ERR 0x00000078 /* Error Mask */ + +/* + * The following defines the bits in the APBUART Ctrl Registers. + */ +#define UART_CTRL_RE 0x00000001 /* Receiver enable */ +#define UART_CTRL_TE 0x00000002 /* Transmitter enable */ +#define UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */ +#define UART_CTRL_TI 0x00000008 /* Transmitter irq */ +#define UART_CTRL_PS 0x00000010 /* Parity select */ +#define UART_CTRL_PE 0x00000020 /* Parity enable */ +#define UART_CTRL_FL 0x00000040 /* Flow control enable */ +#define UART_CTRL_LB 0x00000080 /* Loopback enable */ + +#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase)) + +#define APBBASE_DATA_P(port) (&(APBBASE(port)->data)) +#define APBBASE_STATUS_P(port) (&(APBBASE(port)->status)) +#define APBBASE_CTRL_P(port) (&(APBBASE(port)->ctrl)) +#define APBBASE_SCALAR_P(port) (&(APBBASE(port)->scaler)) + +#define UART_GET_CHAR(port) (__raw_readl(APBBASE_DATA_P(port))) +#define UART_PUT_CHAR(port, v) (__raw_writel(v, APBBASE_DATA_P(port))) +#define UART_GET_STATUS(port) (__raw_readl(APBBASE_STATUS_P(port))) +#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port))) +#define UART_GET_CTRL(port) (__raw_readl(APBBASE_CTRL_P(port))) +#define UART_PUT_CTRL(port, v) (__raw_writel(v, APBBASE_CTRL_P(port))) +#define UART_GET_SCAL(port) (__raw_readl(APBBASE_SCALAR_P(port))) +#define UART_PUT_SCAL(port, v) (__raw_writel(v, APBBASE_SCALAR_P(port))) + +#define UART_RX_DATA(s) (((s) & UART_STATUS_DR) != 0) +#define UART_TX_READY(s) (((s) & UART_STATUS_THE) != 0) + +#endif /* __GRLIB_APBUART_H__ */ diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c new file mode 100644 index 000000000000..3892666b5fbd --- /dev/null +++ b/drivers/tty/serial/atmel_serial.c @@ -0,0 +1,1808 @@ +/* + * linux/drivers/char/atmel_serial.c + * + * Driver for Atmel AT91 / AT32 Serial ports + * Copyright (C) 2003 Rick Bronson + * + * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * DMA support added by Chip Coldwell. + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef CONFIG_ARM +#include +#include +#endif + +#define PDC_BUFFER_SIZE 512 +/* Revisit: We should calculate this based on the actual port settings */ +#define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ + +#if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +static void atmel_start_rx(struct uart_port *port); +static void atmel_stop_rx(struct uart_port *port); + +#ifdef CONFIG_SERIAL_ATMEL_TTYAT + +/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we + * should coexist with the 8250 driver, such as if we have an external 16C550 + * UART. */ +#define SERIAL_ATMEL_MAJOR 204 +#define MINOR_START 154 +#define ATMEL_DEVICENAME "ttyAT" + +#else + +/* Use device name ttyS, major 4, minor 64-68. This is the usual serial port + * name, but it is legally reserved for the 8250 driver. */ +#define SERIAL_ATMEL_MAJOR TTY_MAJOR +#define MINOR_START 64 +#define ATMEL_DEVICENAME "ttyS" + +#endif + +#define ATMEL_ISR_PASS_LIMIT 256 + +/* UART registers. CR is write-only, hence no GET macro */ +#define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR) +#define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR) +#define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR) +#define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER) +#define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR) +#define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR) +#define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR) +#define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR) +#define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR) +#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) +#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) +#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) + + /* PDC registers */ +#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) +#define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR) + +#define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR) +#define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR) +#define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR) +#define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR) +#define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR) + +#define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR) +#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR) +#define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR) + +static int (*atmel_open_hook)(struct uart_port *); +static void (*atmel_close_hook)(struct uart_port *); + +struct atmel_dma_buffer { + unsigned char *buf; + dma_addr_t dma_addr; + unsigned int dma_size; + unsigned int ofs; +}; + +struct atmel_uart_char { + u16 status; + u16 ch; +}; + +#define ATMEL_SERIAL_RINGSIZE 1024 + +/* + * We wrap our port structure around the generic uart_port. + */ +struct atmel_uart_port { + struct uart_port uart; /* uart */ + struct clk *clk; /* uart clock */ + int may_wakeup; /* cached value of device_may_wakeup for times we need to disable it */ + u32 backup_imr; /* IMR saved during suspend */ + int break_active; /* break being received */ + + short use_dma_rx; /* enable PDC receiver */ + short pdc_rx_idx; /* current PDC RX buffer */ + struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ + + short use_dma_tx; /* enable PDC transmitter */ + struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ + + struct tasklet_struct tasklet; + unsigned int irq_status; + unsigned int irq_status_prev; + + struct circ_buf rx_ring; + + struct serial_rs485 rs485; /* rs485 settings */ + unsigned int tx_done_mask; +}; + +static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; + +#ifdef SUPPORT_SYSRQ +static struct console atmel_console; +#endif + +static inline struct atmel_uart_port * +to_atmel_uart_port(struct uart_port *uart) +{ + return container_of(uart, struct atmel_uart_port, uart); +} + +#ifdef CONFIG_SERIAL_ATMEL_PDC +static bool atmel_use_dma_rx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_rx; +} + +static bool atmel_use_dma_tx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_tx; +} +#else +static bool atmel_use_dma_rx(struct uart_port *port) +{ + return false; +} + +static bool atmel_use_dma_tx(struct uart_port *port) +{ + return false; +} +#endif + +/* Enable or disable the rs485 support */ +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + + spin_lock(&port->lock); + + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + mode = UART_GET_MR(port); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + atmel_port->rs485 = *rs485conf; + + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, rs485conf->delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + if (atmel_use_dma_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + UART_PUT_MR(port, mode); + + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + + spin_unlock(&port->lock); + +} + +/* + * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. + */ +static u_int atmel_tx_empty(struct uart_port *port) +{ + return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0; +} + +/* + * Set state of the modem control output lines + */ +static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) +{ + unsigned int control = 0; + unsigned int mode; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + +#ifdef CONFIG_ARCH_AT91RM9200 + if (cpu_is_at91rm9200()) { + /* + * AT91RM9200 Errata #39: RTS0 is not internally connected + * to PA21. We need to drive the pin manually. + */ + if (port->mapbase == AT91RM9200_BASE_US0) { + if (mctrl & TIOCM_RTS) + at91_set_gpio_value(AT91_PIN_PA21, 0); + else + at91_set_gpio_value(AT91_PIN_PA21, 1); + } + } +#endif + + if (mctrl & TIOCM_RTS) + control |= ATMEL_US_RTSEN; + else + control |= ATMEL_US_RTSDIS; + + if (mctrl & TIOCM_DTR) + control |= ATMEL_US_DTREN; + else + control |= ATMEL_US_DTRDIS; + + UART_PUT_CR(port, control); + + /* Local loopback mode? */ + mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE; + if (mctrl & TIOCM_LOOP) + mode |= ATMEL_US_CHMODE_LOC_LOOP; + else + mode |= ATMEL_US_CHMODE_NORMAL; + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, + atmel_port->rs485.delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + UART_PUT_MR(port, mode); +} + +/* + * Get state of the modem control input lines + */ +static u_int atmel_get_mctrl(struct uart_port *port) +{ + unsigned int status, ret = 0; + + status = UART_GET_CSR(port); + + /* + * The control signals are active low. + */ + if (!(status & ATMEL_US_DCD)) + ret |= TIOCM_CD; + if (!(status & ATMEL_US_CTS)) + ret |= TIOCM_CTS; + if (!(status & ATMEL_US_DSR)) + ret |= TIOCM_DSR; + if (!(status & ATMEL_US_RI)) + ret |= TIOCM_RI; + + return ret; +} + +/* + * Stop transmitting. + */ +static void atmel_stop_tx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_tx(port)) { + /* disable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); + } + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_start_rx(port); +} + +/* + * Start transmitting. + */ +static void atmel_start_tx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_tx(port)) { + if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) + /* The transmitter is already running. Yes, we + really need this.*/ + return; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_stop_rx(port); + + /* re-enable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); + } + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); +} + +/* + * start receiving - port is in process of being opened. + */ +static void atmel_start_rx(struct uart_port *port) +{ + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + + if (atmel_use_dma_rx(port)) { + /* enable PDC controller */ + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + UART_PUT_IER(port, ATMEL_US_RXRDY); + } +} + +/* + * Stop receiving - port is in process of being closed. + */ +static void atmel_stop_rx(struct uart_port *port) +{ + if (atmel_use_dma_rx(port)) { + /* disable PDC receive */ + UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + } else { + UART_PUT_IDR(port, ATMEL_US_RXRDY); + } +} + +/* + * Enable modem status interrupts + */ +static void atmel_enable_ms(struct uart_port *port) +{ + UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC + | ATMEL_US_DCDIC | ATMEL_US_CTSIC); +} + +/* + * Control the transmission of a break signal + */ +static void atmel_break_ctl(struct uart_port *port, int break_state) +{ + if (break_state != 0) + UART_PUT_CR(port, ATMEL_US_STTBRK); /* start break */ + else + UART_PUT_CR(port, ATMEL_US_STPBRK); /* stop break */ +} + +/* + * Stores the incoming character in the ring buffer + */ +static void +atmel_buffer_rx_char(struct uart_port *port, unsigned int status, + unsigned int ch) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *ring = &atmel_port->rx_ring; + struct atmel_uart_char *c; + + if (!CIRC_SPACE(ring->head, ring->tail, ATMEL_SERIAL_RINGSIZE)) + /* Buffer overflow, ignore char */ + return; + + c = &((struct atmel_uart_char *)ring->buf)[ring->head]; + c->status = status; + c->ch = ch; + + /* Make sure the character is stored before we update head. */ + smp_wmb(); + + ring->head = (ring->head + 1) & (ATMEL_SERIAL_RINGSIZE - 1); +} + +/* + * Deal with parity, framing and overrun errors. + */ +static void atmel_pdc_rxerr(struct uart_port *port, unsigned int status) +{ + /* clear error */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + + if (status & ATMEL_US_RXBRK) { + /* ignore side-effect */ + status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); + port->icount.brk++; + } + if (status & ATMEL_US_PARE) + port->icount.parity++; + if (status & ATMEL_US_FRAME) + port->icount.frame++; + if (status & ATMEL_US_OVRE) + port->icount.overrun++; +} + +/* + * Characters received (called from interrupt handler) + */ +static void atmel_rx_chars(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status, ch; + + status = UART_GET_CSR(port); + while (status & ATMEL_US_RXRDY) { + ch = UART_GET_CHAR(port); + + /* + * note that the error handling code is + * out of the main execution path + */ + if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME + | ATMEL_US_OVRE | ATMEL_US_RXBRK) + || atmel_port->break_active)) { + + /* clear error */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + + if (status & ATMEL_US_RXBRK + && !atmel_port->break_active) { + atmel_port->break_active = 1; + UART_PUT_IER(port, ATMEL_US_RXBRK); + } else { + /* + * This is either the end-of-break + * condition or we've received at + * least one character without RXBRK + * being set. In both cases, the next + * RXBRK will indicate start-of-break. + */ + UART_PUT_IDR(port, ATMEL_US_RXBRK); + status &= ~ATMEL_US_RXBRK; + atmel_port->break_active = 0; + } + } + + atmel_buffer_rx_char(port, status, ch); + status = UART_GET_CSR(port); + } + + tasklet_schedule(&atmel_port->tasklet); +} + +/* + * Transmit characters (called from tasklet with TXRDY interrupt + * disabled) + */ +static void atmel_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!uart_circ_empty(xmit)) + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); +} + +/* + * receive interrupt handler. + */ +static void +atmel_handle_receive(struct uart_port *port, unsigned int pending) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_rx(port)) { + /* + * PDC receive. Just schedule the tasklet and let it + * figure out the details. + * + * TODO: We're not handling error flags correctly at + * the moment. + */ + if (pending & (ATMEL_US_ENDRX | ATMEL_US_TIMEOUT)) { + UART_PUT_IDR(port, (ATMEL_US_ENDRX + | ATMEL_US_TIMEOUT)); + tasklet_schedule(&atmel_port->tasklet); + } + + if (pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | + ATMEL_US_FRAME | ATMEL_US_PARE)) + atmel_pdc_rxerr(port, pending); + } + + /* Interrupt receive */ + if (pending & ATMEL_US_RXRDY) + atmel_rx_chars(port); + else if (pending & ATMEL_US_RXBRK) { + /* + * End of break detected. If it came along with a + * character, atmel_rx_chars will handle it. + */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + UART_PUT_IDR(port, ATMEL_US_RXBRK); + atmel_port->break_active = 0; + } +} + +/* + * transmit interrupt handler. (Transmit is IRQF_NODELAY safe) + */ +static void +atmel_handle_transmit(struct uart_port *port, unsigned int pending) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (pending & atmel_port->tx_done_mask) { + /* Either PDC or interrupt transmission */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + tasklet_schedule(&atmel_port->tasklet); + } +} + +/* + * status flags interrupt handler. + */ +static void +atmel_handle_status(struct uart_port *port, unsigned int pending, + unsigned int status) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC + | ATMEL_US_CTSIC)) { + atmel_port->irq_status = status; + tasklet_schedule(&atmel_port->tasklet); + } +} + +/* + * Interrupt handler + */ +static irqreturn_t atmel_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int status, pending, pass_counter = 0; + + do { + status = UART_GET_CSR(port); + pending = status & UART_GET_IMR(port); + if (!pending) + break; + + atmel_handle_receive(port, pending); + atmel_handle_status(port, pending, status); + atmel_handle_transmit(port, pending); + } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); + + return pass_counter ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * Called from tasklet with ENDTX and TXBUFE interrupts disabled. + */ +static void atmel_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *xmit = &port->state->xmit; + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + int count; + + /* nothing left to transmit? */ + if (UART_GET_TCR(port)) + return; + + xmit->tail += pdc->ofs; + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += pdc->ofs; + pdc->ofs = 0; + + /* more to transmit - setup next transfer */ + + /* disable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + dma_sync_single_for_device(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_TO_DEVICE); + + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + pdc->ofs = count; + + UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); + UART_PUT_TCR(port, count); + /* re-enable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void atmel_rx_from_ring(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *ring = &atmel_port->rx_ring; + unsigned int flg; + unsigned int status; + + while (ring->head != ring->tail) { + struct atmel_uart_char c; + + /* Make sure c is loaded after head. */ + smp_rmb(); + + c = ((struct atmel_uart_char *)ring->buf)[ring->tail]; + + ring->tail = (ring->tail + 1) & (ATMEL_SERIAL_RINGSIZE - 1); + + port->icount.rx++; + status = c.status; + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME + | ATMEL_US_OVRE | ATMEL_US_RXBRK))) { + if (status & ATMEL_US_RXBRK) { + /* ignore side-effect */ + status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); + + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + if (status & ATMEL_US_PARE) + port->icount.parity++; + if (status & ATMEL_US_FRAME) + port->icount.frame++; + if (status & ATMEL_US_OVRE) + port->icount.overrun++; + + status &= port->read_status_mask; + + if (status & ATMEL_US_RXBRK) + flg = TTY_BREAK; + else if (status & ATMEL_US_PARE) + flg = TTY_PARITY; + else if (status & ATMEL_US_FRAME) + flg = TTY_FRAME; + } + + + if (uart_handle_sysrq_char(port, c.ch)) + continue; + + uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg); + } + + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(port->state->port.tty); + spin_lock(&port->lock); +} + +static void atmel_rx_from_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_struct *tty = port->state->port.tty; + struct atmel_dma_buffer *pdc; + int rx_idx = atmel_port->pdc_rx_idx; + unsigned int head; + unsigned int tail; + unsigned int count; + + do { + /* Reset the UART timeout early so that we don't miss one */ + UART_PUT_CR(port, ATMEL_US_STTTO); + + pdc = &atmel_port->pdc_rx[rx_idx]; + head = UART_GET_RPR(port) - pdc->dma_addr; + tail = pdc->ofs; + + /* If the PDC has switched buffers, RPR won't contain + * any address within the current buffer. Since head + * is unsigned, we just need a one-way comparison to + * find out. + * + * In this case, we just need to consume the entire + * buffer and resubmit it for DMA. This will clear the + * ENDRX bit as well, so that we can safely re-enable + * all interrupts below. + */ + head = min(head, pdc->dma_size); + + if (likely(head != tail)) { + dma_sync_single_for_cpu(port->dev, pdc->dma_addr, + pdc->dma_size, DMA_FROM_DEVICE); + + /* + * head will only wrap around when we recycle + * the DMA buffer, and when that happens, we + * explicitly set tail to 0. So head will + * always be greater than tail. + */ + count = head - tail; + + tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); + + dma_sync_single_for_device(port->dev, pdc->dma_addr, + pdc->dma_size, DMA_FROM_DEVICE); + + port->icount.rx += count; + pdc->ofs = head; + } + + /* + * If the current buffer is full, we need to check if + * the next one contains any additional data. + */ + if (head >= pdc->dma_size) { + pdc->ofs = 0; + UART_PUT_RNPR(port, pdc->dma_addr); + UART_PUT_RNCR(port, pdc->dma_size); + + rx_idx = !rx_idx; + atmel_port->pdc_rx_idx = rx_idx; + } + } while (head >= pdc->dma_size); + + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(tty); + spin_lock(&port->lock); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); +} + +/* + * tasklet handling tty stuff outside the interrupt handler. + */ +static void atmel_tasklet_func(unsigned long data) +{ + struct uart_port *port = (struct uart_port *)data; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status; + unsigned int status_change; + + /* The interrupt handler does not take the lock */ + spin_lock(&port->lock); + + if (atmel_use_dma_tx(port)) + atmel_tx_dma(port); + else + atmel_tx_chars(port); + + status = atmel_port->irq_status; + status_change = status ^ atmel_port->irq_status_prev; + + if (status_change & (ATMEL_US_RI | ATMEL_US_DSR + | ATMEL_US_DCD | ATMEL_US_CTS)) { + /* TODO: All reads to CSR will clear these interrupts! */ + if (status_change & ATMEL_US_RI) + port->icount.rng++; + if (status_change & ATMEL_US_DSR) + port->icount.dsr++; + if (status_change & ATMEL_US_DCD) + uart_handle_dcd_change(port, !(status & ATMEL_US_DCD)); + if (status_change & ATMEL_US_CTS) + uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); + + wake_up_interruptible(&port->state->port.delta_msr_wait); + + atmel_port->irq_status_prev = status; + } + + if (atmel_use_dma_rx(port)) + atmel_rx_from_dma(port); + else + atmel_rx_from_ring(port); + + spin_unlock(&port->lock); +} + +/* + * Perform initialization and enable port for reception + */ +static int atmel_startup(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_struct *tty = port->state->port.tty; + int retval; + + /* + * Ensure that no interrupts are enabled otherwise when + * request_irq() is called we could get stuck trying to + * handle an unexpected interrupt + */ + UART_PUT_IDR(port, -1); + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, + tty ? tty->name : "atmel_serial", port); + if (retval) { + printk("atmel_serial: atmel_startup - Can't get irq\n"); + return retval; + } + + /* + * Initialize DMA (if necessary) + */ + if (atmel_use_dma_rx(port)) { + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); + if (pdc->buf == NULL) { + if (i != 0) { + dma_unmap_single(port->dev, + atmel_port->pdc_rx[0].dma_addr, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + kfree(atmel_port->pdc_rx[0].buf); + } + free_irq(port->irq, port); + return -ENOMEM; + } + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + pdc->dma_size = PDC_BUFFER_SIZE; + pdc->ofs = 0; + } + + atmel_port->pdc_rx_idx = 0; + + UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); + UART_PUT_RCR(port, PDC_BUFFER_SIZE); + + UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); + UART_PUT_RNCR(port, PDC_BUFFER_SIZE); + } + if (atmel_use_dma_tx(port)) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + struct circ_buf *xmit = &port->state->xmit; + + pdc->buf = xmit->buf; + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + pdc->dma_size = UART_XMIT_SIZE; + pdc->ofs = 0; + } + + /* + * If there is a specific "open" function (to register + * control line interrupts) + */ + if (atmel_open_hook) { + retval = atmel_open_hook(port); + if (retval) { + free_irq(port->irq, port); + return retval; + } + } + + /* Save current CSR for comparison in atmel_tasklet_func() */ + atmel_port->irq_status_prev = UART_GET_CSR(port); + atmel_port->irq_status = atmel_port->irq_status_prev; + + /* + * Finally, enable the serial port + */ + UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); + /* enable xmit & rcvr */ + UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + + if (atmel_use_dma_rx(port)) { + /* set UART timeout */ + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + /* enable PDC controller */ + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + /* enable receive only */ + UART_PUT_IER(port, ATMEL_US_RXRDY); + } + + return 0; +} + +/* + * Disable the port + */ +static void atmel_shutdown(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + /* + * Ensure everything is stopped. + */ + atmel_stop_rx(port); + atmel_stop_tx(port); + + /* + * Shut-down the DMA. + */ + if (atmel_use_dma_rx(port)) { + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_FROM_DEVICE); + kfree(pdc->buf); + } + } + if (atmel_use_dma_tx(port)) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_TO_DEVICE); + } + + /* + * Disable all interrupts, port and break condition. + */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + UART_PUT_IDR(port, -1); + + /* + * Free the interrupt + */ + free_irq(port->irq, port); + + /* + * If there is a specific "close" function (to unregister + * control line interrupts) + */ + if (atmel_close_hook) + atmel_close_hook(port); +} + +/* + * Flush any TX data submitted for DMA. Called when the TX circular + * buffer is reset. + */ +static void atmel_flush_buffer(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_tx(port)) { + UART_PUT_TCR(port, 0); + atmel_port->pdc_tx.ofs = 0; + } +} + +/* + * Power / Clock management. + */ +static void atmel_serial_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + switch (state) { + case 0: + /* + * Enable the peripheral clock for this serial port. + * This is called on uart_open() or a resume event. + */ + clk_enable(atmel_port->clk); + + /* re-enable interrupts if we disabled some on suspend */ + UART_PUT_IER(port, atmel_port->backup_imr); + break; + case 3: + /* Back up the interrupt mask and disable all interrupts */ + atmel_port->backup_imr = UART_GET_IMR(port); + UART_PUT_IDR(port, -1); + + /* + * Disable the peripheral clock for this serial port. + * This is called on uart_close() or a suspend event. + */ + clk_disable(atmel_port->clk); + break; + default: + printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); + } +} + +/* + * Change the port parameters + */ +static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int mode, imr, quot, baud; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + /* Get current mode register */ + mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL + | ATMEL_US_NBSTOP | ATMEL_US_PAR + | ATMEL_US_USMODE); + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + + if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */ + quot /= 8; + mode |= ATMEL_US_USCLKS_MCK_DIV8; + } + + /* byte size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + mode |= ATMEL_US_CHRL_5; + break; + case CS6: + mode |= ATMEL_US_CHRL_6; + break; + case CS7: + mode |= ATMEL_US_CHRL_7; + break; + default: + mode |= ATMEL_US_CHRL_8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + mode |= ATMEL_US_NBSTOP_2; + + /* parity */ + if (termios->c_cflag & PARENB) { + /* Mark or Space parity */ + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + mode |= ATMEL_US_PAR_MARK; + else + mode |= ATMEL_US_PAR_SPACE; + } else if (termios->c_cflag & PARODD) + mode |= ATMEL_US_PAR_ODD; + else + mode |= ATMEL_US_PAR_EVEN; + } else + mode |= ATMEL_US_PAR_NONE; + + /* hardware handshake (RTS/CTS) */ + if (termios->c_cflag & CRTSCTS) + mode |= ATMEL_US_USMODE_HWHS; + else + mode |= ATMEL_US_USMODE_NORMAL; + + spin_lock_irqsave(&port->lock, flags); + + port->read_status_mask = ATMEL_US_OVRE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= ATMEL_US_RXBRK; + + if (atmel_use_dma_rx(port)) + /* need to enable error interrupts */ + UART_PUT_IER(port, port->read_status_mask); + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= ATMEL_US_RXBRK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ATMEL_US_OVRE; + } + /* TODO: Ignore all characters if CREAD is set.*/ + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * save/disable interrupts. The tty layer will ensure that the + * transmitter is empty if requested by the caller, so there's + * no need to wait for it here. + */ + imr = UART_GET_IMR(port); + UART_PUT_IDR(port, -1); + + /* disable receiver and transmitter */ + UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, + atmel_port->rs485.delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + + /* set the parity, stop bits and data size */ + UART_PUT_MR(port, mode); + + /* set the baud rate */ + UART_PUT_BRGR(port, quot); + UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); + UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + + /* restore interrupts */ + UART_PUT_IER(port, imr); + + /* CTS flow-control and modem-status interrupts */ + if (UART_ENABLE_MS(port, termios->c_cflag)) + port->ops->enable_ms(port); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * Return string describing the specified port + */ +static const char *atmel_type(struct uart_port *port) +{ + return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void atmel_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + int size = pdev->resource[0].end - pdev->resource[0].start + 1; + + release_mem_region(port->mapbase, size); + + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int atmel_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + int size = pdev->resource[0].end - pdev->resource[0].start + 1; + + if (!request_mem_region(port->mapbase, size, "atmel_serial")) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, size); + if (port->membase == NULL) { + release_mem_region(port->mapbase, size); + return -ENOMEM; + } + } + + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void atmel_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_ATMEL; + atmel_request_port(port); + } +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)port->mapbase != ser->iomem_base) + ret = -EINVAL; + if (port->iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +#ifdef CONFIG_CONSOLE_POLL +static int atmel_poll_get_char(struct uart_port *port) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_RXRDY)) + cpu_relax(); + + return UART_GET_CHAR(port); +} + +static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) + cpu_relax(); + + UART_PUT_CHAR(port, ch); +} +#endif + +static int +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + atmel_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_atmel_uart_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + + +static struct uart_ops atmel_pops = { + .tx_empty = atmel_tx_empty, + .set_mctrl = atmel_set_mctrl, + .get_mctrl = atmel_get_mctrl, + .stop_tx = atmel_stop_tx, + .start_tx = atmel_start_tx, + .stop_rx = atmel_stop_rx, + .enable_ms = atmel_enable_ms, + .break_ctl = atmel_break_ctl, + .startup = atmel_startup, + .shutdown = atmel_shutdown, + .flush_buffer = atmel_flush_buffer, + .set_termios = atmel_set_termios, + .type = atmel_type, + .release_port = atmel_release_port, + .request_port = atmel_request_port, + .config_port = atmel_config_port, + .verify_port = atmel_verify_port, + .pm = atmel_serial_pm, + .ioctl = atmel_ioctl, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = atmel_poll_get_char, + .poll_put_char = atmel_poll_put_char, +#endif +}; + +/* + * Configure the port from the platform device resource info. + */ +static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, + struct platform_device *pdev) +{ + struct uart_port *port = &atmel_port->uart; + struct atmel_uart_data *data = pdev->dev.platform_data; + + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &atmel_pops; + port->fifosize = 1; + port->line = pdev->id; + port->dev = &pdev->dev; + port->mapbase = pdev->resource[0].start; + port->irq = pdev->resource[1].start; + + tasklet_init(&atmel_port->tasklet, atmel_tasklet_func, + (unsigned long)port); + + memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); + + if (data->regs) + /* Already mapped by setup code */ + port->membase = data->regs; + else { + port->flags |= UPF_IOREMAP; + port->membase = NULL; + } + + /* for console, the clock could already be configured */ + if (!atmel_port->clk) { + atmel_port->clk = clk_get(&pdev->dev, "usart"); + clk_enable(atmel_port->clk); + port->uartclk = clk_get_rate(atmel_port->clk); + clk_disable(atmel_port->clk); + /* only enable clock when USART is in use */ + } + + atmel_port->use_dma_rx = data->use_dma_rx; + atmel_port->use_dma_tx = data->use_dma_tx; + atmel_port->rs485 = data->rs485; + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + else if (atmel_use_dma_tx(port)) { + port->fifosize = PDC_BUFFER_SIZE; + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; + } else { + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } +} + +/* + * Register board-specific modem-control line handlers. + */ +void __init atmel_register_uart_fns(struct atmel_port_fns *fns) +{ + if (fns->enable_ms) + atmel_pops.enable_ms = fns->enable_ms; + if (fns->get_mctrl) + atmel_pops.get_mctrl = fns->get_mctrl; + if (fns->set_mctrl) + atmel_pops.set_mctrl = fns->set_mctrl; + atmel_open_hook = fns->open; + atmel_close_hook = fns->close; + atmel_pops.pm = fns->pm; + atmel_pops.set_wake = fns->set_wake; +} + +#ifdef CONFIG_SERIAL_ATMEL_CONSOLE +static void atmel_console_putchar(struct uart_port *port, int ch) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) + cpu_relax(); + UART_PUT_CHAR(port, ch); +} + +/* + * Interrupts are disabled on entering + */ +static void atmel_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = &atmel_ports[co->index].uart; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status, imr; + unsigned int pdc_tx; + + /* + * First, save IMR and then disable interrupts + */ + imr = UART_GET_IMR(port); + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); + + /* Store PDC transmit status and disable it */ + pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); + + uart_console_write(port, s, count, atmel_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore IMR + */ + do { + status = UART_GET_CSR(port); + } while (!(status & ATMEL_US_TXRDY)); + + /* Restore PDC transmit status */ + if (pdc_tx) + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); + + /* set interrupts back the way they were */ + UART_PUT_IER(port, imr); +} + +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init atmel_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + unsigned int mr, quot; + + /* + * If the baud rate generator isn't running, the port wasn't + * initialized by the boot loader. + */ + quot = UART_GET_BRGR(port) & ATMEL_US_CD; + if (!quot) + return; + + mr = UART_GET_MR(port) & ATMEL_US_CHRL; + if (mr == ATMEL_US_CHRL_8) + *bits = 8; + else + *bits = 7; + + mr = UART_GET_MR(port) & ATMEL_US_PAR; + if (mr == ATMEL_US_PAR_EVEN) + *parity = 'e'; + else if (mr == ATMEL_US_PAR_ODD) + *parity = 'o'; + + /* + * The serial core only rounds down when matching this to a + * supported baud rate. Make sure we don't end up slightly + * lower than one of those, as it would make us fall through + * to a much lower baud rate than we really want. + */ + *baud = port->uartclk / (16 * (quot - 1)); +} + +static int __init atmel_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &atmel_ports[co->index].uart; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (port->membase == NULL) { + /* Port not initialized yet - delay setup */ + return -ENODEV; + } + + clk_enable(atmel_ports[co->index].clk); + + UART_PUT_IDR(port, -1); + UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); + UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + atmel_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver atmel_uart; + +static struct console atmel_console = { + .name = ATMEL_DEVICENAME, + .write = atmel_console_write, + .device = uart_console_device, + .setup = atmel_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &atmel_uart, +}; + +#define ATMEL_CONSOLE_DEVICE (&atmel_console) + +/* + * Early console initialization (before VM subsystem initialized). + */ +static int __init atmel_console_init(void) +{ + if (atmel_default_console_device) { + add_preferred_console(ATMEL_DEVICENAME, + atmel_default_console_device->id, NULL); + atmel_init_port(&atmel_ports[atmel_default_console_device->id], + atmel_default_console_device); + register_console(&atmel_console); + } + + return 0; +} + +console_initcall(atmel_console_init); + +/* + * Late console initialization. + */ +static int __init atmel_late_console_init(void) +{ + if (atmel_default_console_device + && !(atmel_console.flags & CON_ENABLED)) + register_console(&atmel_console); + + return 0; +} + +core_initcall(atmel_late_console_init); + +static inline bool atmel_is_console_port(struct uart_port *port) +{ + return port->cons && port->cons->index == port->line; +} + +#else +#define ATMEL_CONSOLE_DEVICE NULL + +static inline bool atmel_is_console_port(struct uart_port *port) +{ + return false; +} +#endif + +static struct uart_driver atmel_uart = { + .owner = THIS_MODULE, + .driver_name = "atmel_serial", + .dev_name = ATMEL_DEVICENAME, + .major = SERIAL_ATMEL_MAJOR, + .minor = MINOR_START, + .nr = ATMEL_MAX_UART, + .cons = ATMEL_CONSOLE_DEVICE, +}; + +#ifdef CONFIG_PM +static bool atmel_serial_clk_will_stop(void) +{ +#ifdef CONFIG_ARCH_AT91 + return at91_suspend_entering_slow_clock(); +#else + return false; +#endif +} + +static int atmel_serial_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct uart_port *port = platform_get_drvdata(pdev); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_is_console_port(port) && console_suspend_enabled) { + /* Drain the TX shifter */ + while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) + cpu_relax(); + } + + /* we can not wake up if we're running on slow clock */ + atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); + if (atmel_serial_clk_will_stop()) + device_set_wakeup_enable(&pdev->dev, 0); + + uart_suspend_port(&atmel_uart, port); + + return 0; +} + +static int atmel_serial_resume(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + uart_resume_port(&atmel_uart, port); + device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup); + + return 0; +} +#else +#define atmel_serial_suspend NULL +#define atmel_serial_resume NULL +#endif + +static int __devinit atmel_serial_probe(struct platform_device *pdev) +{ + struct atmel_uart_port *port; + void *data; + int ret; + + BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); + + port = &atmel_ports[pdev->id]; + port->backup_imr = 0; + + atmel_init_port(port, pdev); + + if (!atmel_use_dma_rx(&port->uart)) { + ret = -ENOMEM; + data = kmalloc(sizeof(struct atmel_uart_char) + * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); + if (!data) + goto err_alloc_ring; + port->rx_ring.buf = data; + } + + ret = uart_add_one_port(&atmel_uart, &port->uart); + if (ret) + goto err_add_port; + +#ifdef CONFIG_SERIAL_ATMEL_CONSOLE + if (atmel_is_console_port(&port->uart) + && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { + /* + * The serial core enabled the clock for us, so undo + * the clk_enable() in atmel_console_setup() + */ + clk_disable(port->clk); + } +#endif + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, port); + + return 0; + +err_add_port: + kfree(port->rx_ring.buf); + port->rx_ring.buf = NULL; +err_alloc_ring: + if (!atmel_is_console_port(&port->uart)) { + clk_put(port->clk); + port->clk = NULL; + } + + return ret; +} + +static int __devexit 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); + int ret = 0; + + device_init_wakeup(&pdev->dev, 0); + platform_set_drvdata(pdev, NULL); + + ret = uart_remove_one_port(&atmel_uart, port); + + tasklet_kill(&atmel_port->tasklet); + kfree(atmel_port->rx_ring.buf); + + /* "port" is allocated statically, so we shouldn't free it */ + + clk_put(atmel_port->clk); + + return ret; +} + +static struct platform_driver atmel_serial_driver = { + .probe = atmel_serial_probe, + .remove = __devexit_p(atmel_serial_remove), + .suspend = atmel_serial_suspend, + .resume = atmel_serial_resume, + .driver = { + .name = "atmel_usart", + .owner = THIS_MODULE, + }, +}; + +static int __init atmel_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&atmel_uart); + if (ret) + return ret; + + ret = platform_driver_register(&atmel_serial_driver); + if (ret) + uart_unregister_driver(&atmel_uart); + + return ret; +} + +static void __exit atmel_serial_exit(void) +{ + platform_driver_unregister(&atmel_serial_driver); + uart_unregister_driver(&atmel_uart); +} + +module_init(atmel_serial_init); +module_exit(atmel_serial_exit); + +MODULE_AUTHOR("Rick Bronson"); +MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:atmel_usart"); diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c new file mode 100644 index 000000000000..a1a0e55d0807 --- /dev/null +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -0,0 +1,891 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Derived from many drivers using generic_serial interface. + * + * Copyright (C) 2008 Maxime Bizon + * + * Serial driver for BCM63xx integrated UART. + * + * Hardware flow control was _not_ tested since I only have RX/TX on + * my board. + */ + +#if defined(CONFIG_SERIAL_BCM63XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BCM63XX_NR_UARTS 2 + +static struct uart_port ports[BCM63XX_NR_UARTS]; + +/* + * rx interrupt mask / stat + * + * mask: + * - rx fifo full + * - rx fifo above threshold + * - rx fifo not empty for too long + */ +#define UART_RX_INT_MASK (UART_IR_MASK(UART_IR_RXOVER) | \ + UART_IR_MASK(UART_IR_RXTHRESH) | \ + UART_IR_MASK(UART_IR_RXTIMEOUT)) + +#define UART_RX_INT_STAT (UART_IR_STAT(UART_IR_RXOVER) | \ + UART_IR_STAT(UART_IR_RXTHRESH) | \ + UART_IR_STAT(UART_IR_RXTIMEOUT)) + +/* + * tx interrupt mask / stat + * + * mask: + * - tx fifo empty + * - tx fifo below threshold + */ +#define UART_TX_INT_MASK (UART_IR_MASK(UART_IR_TXEMPTY) | \ + UART_IR_MASK(UART_IR_TXTRESH)) + +#define UART_TX_INT_STAT (UART_IR_STAT(UART_IR_TXEMPTY) | \ + UART_IR_STAT(UART_IR_TXTRESH)) + +/* + * external input interrupt + * + * mask: any edge on CTS, DCD + */ +#define UART_EXTINP_INT_MASK (UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \ + UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) + +/* + * handy uart register accessor + */ +static inline unsigned int bcm_uart_readl(struct uart_port *port, + unsigned int offset) +{ + return bcm_readl(port->membase + offset); +} + +static inline void bcm_uart_writel(struct uart_port *port, + unsigned int value, unsigned int offset) +{ + bcm_writel(value, port->membase + offset); +} + +/* + * serial core request to check if uart tx fifo is empty + */ +static unsigned int bcm_uart_tx_empty(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; +} + +/* + * serial core request to set RTS and DTR pin state and loopback mode + */ +static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_MCTL_REG); + val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); + /* invert of written value is reflected on the pin */ + if (!(mctrl & TIOCM_DTR)) + val |= UART_MCTL_DTR_MASK; + if (!(mctrl & TIOCM_RTS)) + val |= UART_MCTL_RTS_MASK; + bcm_uart_writel(port, val, UART_MCTL_REG); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (mctrl & TIOCM_LOOP) + val |= UART_CTL_LOOPBACK_MASK; + else + val &= ~UART_CTL_LOOPBACK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * serial core request to return RI, CTS, DCD and DSR pin state + */ +static unsigned int bcm_uart_get_mctrl(struct uart_port *port) +{ + unsigned int val, mctrl; + + mctrl = 0; + val = bcm_uart_readl(port, UART_EXTINP_REG); + if (val & UART_EXTINP_RI_MASK) + mctrl |= TIOCM_RI; + if (val & UART_EXTINP_CTS_MASK) + mctrl |= TIOCM_CTS; + if (val & UART_EXTINP_DCD_MASK) + mctrl |= TIOCM_CD; + if (val & UART_EXTINP_DSR_MASK) + mctrl |= TIOCM_DSR; + return mctrl; +} + +/* + * serial core request to disable tx ASAP (used for flow control) + */ +static void bcm_uart_stop_tx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~(UART_CTL_TXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); + + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to (re)enable tx + */ +static void bcm_uart_start_tx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val |= UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); + + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_TXEN_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * serial core request to stop rx, called before port shutdown + */ +static void bcm_uart_stop_rx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_RX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to enable modem status interrupt reporting + */ +static void bcm_uart_enable_ms(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val |= UART_IR_MASK(UART_IR_EXTIP); + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to start/stop emitting break char + */ +static void bcm_uart_break_ctl(struct uart_port *port, int ctl) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&port->lock, flags); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (ctl) + val |= UART_CTL_XMITBRK_MASK; + else + val &= ~UART_CTL_XMITBRK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * return port type in string format + */ +static const char *bcm_uart_type(struct uart_port *port) +{ + return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL; +} + +/* + * read all chars in rx fifo and send them to core + */ +static void bcm_uart_do_rx(struct uart_port *port) +{ + struct tty_struct *tty; + unsigned int max_count; + + /* limit number of char read in interrupt, should not be + * higher than fifo size anyway since we're much faster than + * serial port */ + max_count = 32; + tty = port->state->port.tty; + do { + unsigned int iestat, c, cstat; + char flag; + + /* get overrun/fifo empty information from ier + * register */ + iestat = bcm_uart_readl(port, UART_IR_REG); + if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) + break; + + cstat = c = bcm_uart_readl(port, UART_FIFO_REG); + port->icount.rx++; + flag = TTY_NORMAL; + c &= 0xff; + + if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { + /* do stats first */ + if (cstat & UART_FIFO_BRKDET_MASK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + + if (cstat & UART_FIFO_PARERR_MASK) + port->icount.parity++; + if (cstat & UART_FIFO_FRAMEERR_MASK) + port->icount.frame++; + + /* update flag wrt read_status_mask */ + cstat &= port->read_status_mask; + if (cstat & UART_FIFO_BRKDET_MASK) + flag = TTY_BREAK; + if (cstat & UART_FIFO_FRAMEERR_MASK) + flag = TTY_FRAME; + if (cstat & UART_FIFO_PARERR_MASK) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + if ((cstat & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, c, flag); + + } while (--max_count); + + tty_flip_buffer_push(tty); +} + +/* + * fill tx fifo with chars to send, stop when fifo is about to be full + * or when all chars have been sent. + */ +static void bcm_uart_do_tx(struct uart_port *port) +{ + struct circ_buf *xmit; + unsigned int val, max_count; + + if (port->x_char) { + bcm_uart_writel(port, port->x_char, UART_FIFO_REG); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port)) { + bcm_uart_stop_tx(port); + return; + } + + xmit = &port->state->xmit; + if (uart_circ_empty(xmit)) + goto txq_empty; + + val = bcm_uart_readl(port, UART_MCTL_REG); + val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; + max_count = port->fifosize - val; + + while (max_count--) { + unsigned int c; + + c = xmit->buf[xmit->tail]; + bcm_uart_writel(port, c, UART_FIFO_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + goto txq_empty; + return; + +txq_empty: + /* nothing to send, disable transmit interrupt */ + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); + return; +} + +/* + * process uart interrupt + */ +static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port; + unsigned int irqstat; + + port = dev_id; + spin_lock(&port->lock); + + irqstat = bcm_uart_readl(port, UART_IR_REG); + if (irqstat & UART_RX_INT_STAT) + bcm_uart_do_rx(port); + + if (irqstat & UART_TX_INT_STAT) + bcm_uart_do_tx(port); + + if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { + unsigned int estat; + + estat = bcm_uart_readl(port, UART_EXTINP_REG); + if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) + uart_handle_cts_change(port, + estat & UART_EXTINP_CTS_MASK); + if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) + uart_handle_dcd_change(port, + estat & UART_EXTINP_DCD_MASK); + } + + spin_unlock(&port->lock); + return IRQ_HANDLED; +} + +/* + * enable rx & tx operation on uart + */ +static void bcm_uart_enable(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * disable rx & tx operation on uart + */ +static void bcm_uart_disable(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | + UART_CTL_RXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * clear all unread data in rx fifo and unsent data in tx fifo + */ +static void bcm_uart_flush(struct uart_port *port) +{ + unsigned int val; + + /* empty rx and tx fifo */ + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + /* read any pending char to make sure all irq status are + * cleared */ + (void)bcm_uart_readl(port, UART_FIFO_REG); +} + +/* + * serial core request to initialize uart and start rx operation + */ +static int bcm_uart_startup(struct uart_port *port) +{ + unsigned int val; + int ret; + + /* mask all irq and flush port */ + bcm_uart_disable(port); + bcm_uart_writel(port, 0, UART_IR_REG); + bcm_uart_flush(port); + + /* clear any pending external input interrupt */ + (void)bcm_uart_readl(port, UART_EXTINP_REG); + + /* set rx/tx fifo thresh to fifo half size */ + val = bcm_uart_readl(port, UART_MCTL_REG); + val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); + val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; + val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; + bcm_uart_writel(port, val, UART_MCTL_REG); + + /* set rx fifo timeout to 1 char time */ + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~UART_CTL_RXTMOUTCNT_MASK; + val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; + bcm_uart_writel(port, val, UART_CTL_REG); + + /* report any edge on dcd and cts */ + val = UART_EXTINP_INT_MASK; + val |= UART_EXTINP_DCD_NOSENSE_MASK; + val |= UART_EXTINP_CTS_NOSENSE_MASK; + bcm_uart_writel(port, val, UART_EXTINP_REG); + + /* register irq and enable rx interrupts */ + ret = request_irq(port->irq, bcm_uart_interrupt, 0, + bcm_uart_type(port), port); + if (ret) + return ret; + bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); + bcm_uart_enable(port); + return 0; +} + +/* + * serial core request to flush & disable uart + */ +static void bcm_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + bcm_uart_writel(port, 0, UART_IR_REG); + spin_unlock_irqrestore(&port->lock, flags); + + bcm_uart_disable(port); + bcm_uart_flush(port); + free_irq(port->irq, port); +} + +/* + * serial core request to change current uart setting + */ +static void bcm_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int ctl, baud, quot, ier; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable uart while changing speed */ + bcm_uart_disable(port); + bcm_uart_flush(port); + + /* update Control register */ + ctl = bcm_uart_readl(port, UART_CTL_REG); + ctl &= ~UART_CTL_BITSPERSYM_MASK; + + switch (new->c_cflag & CSIZE) { + case CS5: + ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); + break; + case CS6: + ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); + break; + case CS7: + ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); + break; + default: + ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); + break; + } + + ctl &= ~UART_CTL_STOPBITS_MASK; + if (new->c_cflag & CSTOPB) + ctl |= UART_CTL_STOPBITS_2; + else + ctl |= UART_CTL_STOPBITS_1; + + ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); + if (new->c_cflag & PARENB) + ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); + ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); + if (new->c_cflag & PARODD) + ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); + bcm_uart_writel(port, ctl, UART_CTL_REG); + + /* update Baudword register */ + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud) - 1; + bcm_uart_writel(port, quot, UART_BAUD_REG); + + /* update Interrupt register */ + ier = bcm_uart_readl(port, UART_IR_REG); + + ier &= ~UART_IR_MASK(UART_IR_EXTIP); + if (UART_ENABLE_MS(port, new->c_cflag)) + ier |= UART_IR_MASK(UART_IR_EXTIP); + + bcm_uart_writel(port, ier, UART_IR_REG); + + /* update read/ignore mask */ + port->read_status_mask = UART_FIFO_VALID_MASK; + if (new->c_iflag & INPCK) { + port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; + port->read_status_mask |= UART_FIFO_PARERR_MASK; + } + if (new->c_iflag & (BRKINT)) + port->read_status_mask |= UART_FIFO_BRKDET_MASK; + + port->ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_FIFO_PARERR_MASK; + if (new->c_iflag & IGNBRK) + port->ignore_status_mask |= UART_FIFO_BRKDET_MASK; + if (!(new->c_cflag & CREAD)) + port->ignore_status_mask |= UART_FIFO_VALID_MASK; + + uart_update_timeout(port, new->c_cflag, baud); + bcm_uart_enable(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * serial core request to claim uart iomem + */ +static int bcm_uart_request_port(struct uart_port *port) +{ + unsigned int size; + + size = RSET_UART_SIZE; + if (!request_mem_region(port->mapbase, size, "bcm63xx")) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, size); + return -EBUSY; + } + return 0; +} + +/* + * serial core request to release uart iomem + */ +static void bcm_uart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, RSET_UART_SIZE); + iounmap(port->membase); +} + +/* + * serial core request to do any port required autoconfiguration + */ +static void bcm_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + if (bcm_uart_request_port(port)) + return; + port->type = PORT_BCM63XX; + } +} + +/* + * serial core request to check that port information in serinfo are + * suitable + */ +static int bcm_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + if (port->type != PORT_BCM63XX) + return -EINVAL; + if (port->irq != serinfo->irq) + return -EINVAL; + if (port->iotype != serinfo->io_type) + return -EINVAL; + if (port->mapbase != (unsigned long)serinfo->iomem_base) + return -EINVAL; + return 0; +} + +/* serial core callbacks */ +static struct uart_ops bcm_uart_ops = { + .tx_empty = bcm_uart_tx_empty, + .get_mctrl = bcm_uart_get_mctrl, + .set_mctrl = bcm_uart_set_mctrl, + .start_tx = bcm_uart_start_tx, + .stop_tx = bcm_uart_stop_tx, + .stop_rx = bcm_uart_stop_rx, + .enable_ms = bcm_uart_enable_ms, + .break_ctl = bcm_uart_break_ctl, + .startup = bcm_uart_startup, + .shutdown = bcm_uart_shutdown, + .set_termios = bcm_uart_set_termios, + .type = bcm_uart_type, + .release_port = bcm_uart_release_port, + .request_port = bcm_uart_request_port, + .config_port = bcm_uart_config_port, + .verify_port = bcm_uart_verify_port, +}; + + + +#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int tmout; + + /* Wait up to 10ms for the character(s) to be sent. */ + tmout = 10000; + while (--tmout) { + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + if (val & UART_IR_STAT(UART_IR_TXEMPTY)) + break; + udelay(1); + } + + /* Wait up to 1s for flow control if necessary */ + if (port->flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) { + unsigned int val; + + val = bcm_uart_readl(port, UART_EXTINP_REG); + if (val & UART_EXTINP_CTS_MASK) + break; + udelay(1); + } + } +} + +/* + * output given char + */ +static void bcm_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + bcm_uart_writel(port, ch, UART_FIFO_REG); +} + +/* + * console core request to output given string + */ +static void bcm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + unsigned long flags; + int locked; + + port = &ports[co->index]; + + local_irq_save(flags); + if (port->sysrq) { + /* bcm_uart_interrupt() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + /* call helper to deal with \r\n */ + uart_console_write(port, s, count, bcm_console_putchar); + + /* and wait for char to be transmitted */ + wait_for_xmitr(port); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +/* + * console core request to setup given console, find matching uart + * port and setup it. + */ +static int bcm_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= BCM63XX_NR_UARTS) + return -EINVAL; + port = &ports[co->index]; + if (!port->membase) + return -ENODEV; + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver bcm_uart_driver; + +static struct console bcm63xx_console = { + .name = "ttyS", + .write = bcm_console_write, + .device = uart_console_device, + .setup = bcm_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bcm_uart_driver, +}; + +static int __init bcm63xx_console_init(void) +{ + register_console(&bcm63xx_console); + return 0; +} + +console_initcall(bcm63xx_console_init); + +#define BCM63XX_CONSOLE (&bcm63xx_console) +#else +#define BCM63XX_CONSOLE NULL +#endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */ + +static struct uart_driver bcm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "bcm63xx_uart", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = BCM63XX_NR_UARTS, + .cons = BCM63XX_CONSOLE, +}; + +/* + * platform driver probe/remove callback + */ +static int __devinit bcm_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem, *res_irq; + struct uart_port *port; + struct clk *clk; + int ret; + + if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) + return -EINVAL; + + if (ports[pdev->id].membase) + return -EBUSY; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) + return -ENODEV; + + clk = clk_get(&pdev->dev, "periph"); + if (IS_ERR(clk)) + return -ENODEV; + + port = &ports[pdev->id]; + memset(port, 0, sizeof(*port)); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->irq = res_irq->start; + port->ops = &bcm_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + port->fifosize = 16; + port->uartclk = clk_get_rate(clk) / 2; + port->line = pdev->id; + clk_put(clk); + + ret = uart_add_one_port(&bcm_uart_driver, port); + if (ret) { + ports[pdev->id].membase = 0; + return ret; + } + platform_set_drvdata(pdev, port); + return 0; +} + +static int __devexit bcm_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&bcm_uart_driver, port); + platform_set_drvdata(pdev, NULL); + /* mark port as free */ + ports[pdev->id].membase = 0; + return 0; +} + +/* + * platform driver stuff + */ +static struct platform_driver bcm_uart_platform_driver = { + .probe = bcm_uart_probe, + .remove = __devexit_p(bcm_uart_remove), + .driver = { + .owner = THIS_MODULE, + .name = "bcm63xx_uart", + }, +}; + +static int __init bcm_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&bcm_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&bcm_uart_platform_driver); + if (ret) + uart_unregister_driver(&bcm_uart_driver); + + return ret; +} + +static void __exit bcm_uart_exit(void) +{ + platform_driver_unregister(&bcm_uart_platform_driver); + uart_unregister_driver(&bcm_uart_driver); +} + +module_init(bcm_uart_init); +module_exit(bcm_uart_exit); + +MODULE_AUTHOR("Maxime Bizon "); +MODULE_DESCRIPTION("Broadcom 63 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) +#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) +#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) +#include + +#ifdef CONFIG_SERIAL_BFIN_MODULE +# undef CONFIG_EARLY_PRINTK +#endif + +#ifdef CONFIG_SERIAL_BFIN_MODULE +# undef CONFIG_EARLY_PRINTK +#endif + +/* UART name and device definitions */ +#define BFIN_SERIAL_DEV_NAME "ttyBF" +#define BFIN_SERIAL_MAJOR 204 +#define BFIN_SERIAL_MINOR 64 + +static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS]; + +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + +# ifndef CONFIG_SERIAL_BFIN_PIO +# error KGDB only support UART in PIO mode. +# endif + +static int kgdboc_port_line; +static int kgdboc_break_enabled; +#endif +/* + * Setup for console. Argument comes from the menuconfig + */ +#define DMA_RX_XCOUNT 512 +#define DMA_RX_YCOUNT (PAGE_SIZE / DMA_RX_XCOUNT) + +#define DMA_RX_FLUSH_JIFFIES (HZ / 50) + +#ifdef CONFIG_SERIAL_BFIN_DMA +static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart); +#else +static void bfin_serial_tx_chars(struct bfin_serial_port *uart); +#endif + +static void bfin_serial_reset_irda(struct uart_port *port); + +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) +static unsigned int bfin_serial_get_mctrl(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + if (uart->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (UART_GET_CTS(uart)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + if (uart->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + UART_ENABLE_RTS(uart); + else + UART_DISABLE_RTS(uart); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + unsigned int status; + + status = bfin_serial_get_mctrl(&uart->port); + uart_handle_cts_change(&uart->port, status & TIOCM_CTS); +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + uart->scts = 1; + UART_CLEAR_SCTS(uart); + UART_CLEAR_IER(uart, EDSSI); +#endif + + return IRQ_HANDLED; +} +#else +static unsigned int bfin_serial_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} +#endif + +/* + * interrupts are disabled on entry + */ +static void bfin_serial_stop_tx(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; +#ifdef CONFIG_SERIAL_BFIN_DMA + struct circ_buf *xmit = &uart->port.state->xmit; +#endif + + while (!(UART_GET_LSR(uart) & TEMT)) + cpu_relax(); + +#ifdef CONFIG_SERIAL_BFIN_DMA + disable_dma(uart->tx_dma_channel); + xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx += uart->tx_count; + uart->tx_count = 0; + uart->tx_done = 1; +#else +#ifdef CONFIG_BF54x + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); +#endif + UART_CLEAR_IER(uart, ETBEI); +#endif +} + +/* + * port is locked and interrupts are disabled + */ +static void bfin_serial_start_tx(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + struct tty_struct *tty = uart->port.state->port.tty; + +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { + uart->scts = 0; + uart_handle_cts_change(&uart->port, uart->scts); + } +#endif + + /* + * To avoid losting RX interrupt, we reset IR function + * before sending data. + */ + if (tty->termios->c_line == N_IRDA) + bfin_serial_reset_irda(port); + +#ifdef CONFIG_SERIAL_BFIN_DMA + if (uart->tx_done) + bfin_serial_dma_tx_chars(uart); +#else + UART_SET_IER(uart, ETBEI); + bfin_serial_tx_chars(uart); +#endif +} + +/* + * Interrupts are enabled + */ +static void bfin_serial_stop_rx(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + UART_CLEAR_IER(uart, ERBFI); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void bfin_serial_enable_ms(struct uart_port *port) +{ +} + + +#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO) +# define UART_GET_ANOMALY_THRESHOLD(uart) ((uart)->anomaly_threshold) +# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v)) +#else +# define UART_GET_ANOMALY_THRESHOLD(uart) 0 +# define UART_SET_ANOMALY_THRESHOLD(uart, v) +#endif + +#ifdef CONFIG_SERIAL_BFIN_PIO +static void bfin_serial_rx_chars(struct bfin_serial_port *uart) +{ + struct tty_struct *tty = NULL; + unsigned int status, ch, flg; + static struct timeval anomaly_start = { .tv_sec = 0 }; + + status = UART_GET_LSR(uart); + UART_CLEAR_LSR(uart); + + ch = UART_GET_CHAR(uart); + uart->port.icount.rx++; + +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + if (kgdb_connected && kgdboc_port_line == uart->port.line + && kgdboc_break_enabled) + if (ch == 0x3) {/* Ctrl + C */ + kgdb_breakpoint(); + return; + } + + if (!uart->port.state || !uart->port.state->port.tty) + return; +#endif + tty = uart->port.state->port.tty; + + if (ANOMALY_05000363) { + /* The BF533 (and BF561) family of processors have a nice anomaly + * where they continuously generate characters for a "single" break. + * We have to basically ignore this flood until the "next" valid + * character comes across. Due to the nature of the flood, it is + * not possible to reliably catch bytes that are sent too quickly + * after this break. So application code talking to the Blackfin + * which sends a break signal must allow at least 1.5 character + * times after the end of the break for things to stabilize. This + * timeout was picked as it must absolutely be larger than 1 + * character time +/- some percent. So 1.5 sounds good. All other + * Blackfin families operate properly. Woo. + */ + if (anomaly_start.tv_sec) { + struct timeval curr; + suseconds_t usecs; + + if ((~ch & (~ch + 1)) & 0xff) + goto known_good_char; + + do_gettimeofday(&curr); + if (curr.tv_sec - anomaly_start.tv_sec > 1) + goto known_good_char; + + usecs = 0; + if (curr.tv_sec != anomaly_start.tv_sec) + usecs += USEC_PER_SEC; + usecs += curr.tv_usec - anomaly_start.tv_usec; + + if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) + goto known_good_char; + + if (ch) + anomaly_start.tv_sec = 0; + else + anomaly_start = curr; + + return; + + known_good_char: + status &= ~BI; + anomaly_start.tv_sec = 0; + } + } + + if (status & BI) { + if (ANOMALY_05000363) + if (bfin_revid() < 5) + do_gettimeofday(&anomaly_start); + uart->port.icount.brk++; + if (uart_handle_break(&uart->port)) + goto ignore_char; + status &= ~(PE | FE); + } + if (status & PE) + uart->port.icount.parity++; + if (status & OE) + uart->port.icount.overrun++; + if (status & FE) + uart->port.icount.frame++; + + status &= uart->port.read_status_mask; + + if (status & BI) + flg = TTY_BREAK; + else if (status & PE) + flg = TTY_PARITY; + else if (status & FE) + flg = TTY_FRAME; + else + flg = TTY_NORMAL; + + if (uart_handle_sysrq_char(&uart->port, ch)) + goto ignore_char; + + uart_insert_char(&uart->port, status, OE, ch, flg); + + ignore_char: + tty_flip_buffer_push(tty); +} + +static void bfin_serial_tx_chars(struct bfin_serial_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { +#ifdef CONFIG_BF54x + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); +#endif + /* Anomaly notes: + * 05000215 - we always clear ETBEI within last UART TX + * interrupt to end a string. It is always set + * when start a new tx. + */ + UART_CLEAR_IER(uart, ETBEI); + return; + } + + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + + while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { + UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); +} + +static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + + spin_lock(&uart->port.lock); + while (UART_GET_LSR(uart) & DR) + bfin_serial_rx_chars(uart); + spin_unlock(&uart->port.lock); + + return IRQ_HANDLED; +} + +static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { + uart->scts = 0; + uart_handle_cts_change(&uart->port, uart->scts); + } +#endif + spin_lock(&uart->port.lock); + if (UART_GET_LSR(uart) & THRE) + bfin_serial_tx_chars(uart); + spin_unlock(&uart->port.lock); + + return IRQ_HANDLED; +} +#endif + +#ifdef CONFIG_SERIAL_BFIN_DMA +static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) +{ + struct circ_buf *xmit = &uart->port.state->xmit; + + uart->tx_done = 0; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { + uart->tx_count = 0; + uart->tx_done = 1; + return; + } + + if (uart->port.x_char) { + UART_PUT_CHAR(uart, uart->port.x_char); + uart->port.icount.tx++; + uart->port.x_char = 0; + } + + uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) + uart->tx_count = UART_XMIT_SIZE - xmit->tail; + blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail), + (unsigned long)(xmit->buf+xmit->tail+uart->tx_count)); + set_dma_config(uart->tx_dma_channel, + set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, + INTR_ON_BUF, + DIMENSION_LINEAR, + DATA_SIZE_8, + DMA_SYNC_RESTART)); + set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); + set_dma_x_count(uart->tx_dma_channel, uart->tx_count); + set_dma_x_modify(uart->tx_dma_channel, 1); + SSYNC(); + enable_dma(uart->tx_dma_channel); + + UART_SET_IER(uart, ETBEI); +} + +static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) +{ + struct tty_struct *tty = uart->port.state->port.tty; + int i, flg, status; + + status = UART_GET_LSR(uart); + UART_CLEAR_LSR(uart); + + uart->port.icount.rx += + CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, + UART_XMIT_SIZE); + + if (status & BI) { + uart->port.icount.brk++; + if (uart_handle_break(&uart->port)) + goto dma_ignore_char; + status &= ~(PE | FE); + } + if (status & PE) + uart->port.icount.parity++; + if (status & OE) + uart->port.icount.overrun++; + if (status & FE) + uart->port.icount.frame++; + + status &= uart->port.read_status_mask; + + if (status & BI) + flg = TTY_BREAK; + else if (status & PE) + flg = TTY_PARITY; + else if (status & FE) + flg = TTY_FRAME; + else + flg = TTY_NORMAL; + + for (i = uart->rx_dma_buf.tail; ; i++) { + if (i >= UART_XMIT_SIZE) + i = 0; + if (i == uart->rx_dma_buf.head) + break; + if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) + uart_insert_char(&uart->port, status, OE, + uart->rx_dma_buf.buf[i], flg); + } + + dma_ignore_char: + tty_flip_buffer_push(tty); +} + +void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) +{ + int x_pos, pos; + + dma_disable_irq(uart->tx_dma_channel); + dma_disable_irq(uart->rx_dma_channel); + spin_lock_bh(&uart->port.lock); + + /* 2D DMA RX buffer ring is used. Because curr_y_count and + * curr_x_count can't be read as an atomic operation, + * curr_y_count should be read before curr_x_count. When + * curr_x_count is read, curr_y_count may already indicate + * next buffer line. But, the position calculated here is + * still indicate the old line. The wrong position data may + * be smaller than current buffer tail, which cause garbages + * are received if it is not prohibit. + */ + uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); + x_pos = get_dma_curr_xcount(uart->rx_dma_channel); + uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; + if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) + uart->rx_dma_nrows = 0; + x_pos = DMA_RX_XCOUNT - x_pos; + if (x_pos == DMA_RX_XCOUNT) + x_pos = 0; + + pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos; + /* Ignore receiving data if new position is in the same line of + * current buffer tail and small. + */ + if (pos > uart->rx_dma_buf.tail || + uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { + uart->rx_dma_buf.head = pos; + bfin_serial_dma_rx_chars(uart); + uart->rx_dma_buf.tail = uart->rx_dma_buf.head; + } + + spin_unlock_bh(&uart->port.lock); + dma_enable_irq(uart->tx_dma_channel); + dma_enable_irq(uart->rx_dma_channel); + + mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); +} + +static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + struct circ_buf *xmit = &uart->port.state->xmit; + +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) { + uart->scts = 0; + uart_handle_cts_change(&uart->port, uart->scts); + } +#endif + + spin_lock(&uart->port.lock); + if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) { + disable_dma(uart->tx_dma_channel); + clear_dma_irqstat(uart->tx_dma_channel); + /* Anomaly notes: + * 05000215 - we always clear ETBEI within last UART TX + * interrupt to end a string. It is always set + * when start a new tx. + */ + UART_CLEAR_IER(uart, ETBEI); + xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1); + uart->port.icount.tx += uart->tx_count; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uart->port); + + bfin_serial_dma_tx_chars(uart); + } + + spin_unlock(&uart->port.lock); + return IRQ_HANDLED; +} + +static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id) +{ + struct bfin_serial_port *uart = dev_id; + unsigned short irqstat; + int x_pos, pos; + + spin_lock(&uart->port.lock); + irqstat = get_dma_curr_irqstat(uart->rx_dma_channel); + clear_dma_irqstat(uart->rx_dma_channel); + + uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); + x_pos = get_dma_curr_xcount(uart->rx_dma_channel); + uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; + if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) + uart->rx_dma_nrows = 0; + + pos = uart->rx_dma_nrows * DMA_RX_XCOUNT; + if (pos > uart->rx_dma_buf.tail || + uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { + uart->rx_dma_buf.head = pos; + bfin_serial_dma_rx_chars(uart); + uart->rx_dma_buf.tail = uart->rx_dma_buf.head; + } + + spin_unlock(&uart->port.lock); + + return IRQ_HANDLED; +} +#endif + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int bfin_serial_tx_empty(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned short lsr; + + lsr = UART_GET_LSR(uart); + if (lsr & TEMT) + return TIOCSER_TEMT; + else + return 0; +} + +static void bfin_serial_break_ctl(struct uart_port *port, int break_state) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + u16 lcr = UART_GET_LCR(uart); + if (break_state) + lcr |= SB; + else + lcr &= ~SB; + UART_PUT_LCR(uart, lcr); + SSYNC(); +} + +static int bfin_serial_startup(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + +#ifdef CONFIG_SERIAL_BFIN_DMA + dma_addr_t dma_handle; + + if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) { + printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n"); + return -EBUSY; + } + + if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) { + printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n"); + free_dma(uart->rx_dma_channel); + return -EBUSY; + } + + set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart); + set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart); + + uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA); + uart->rx_dma_buf.head = 0; + uart->rx_dma_buf.tail = 0; + uart->rx_dma_nrows = 0; + + set_dma_config(uart->rx_dma_channel, + set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO, + INTR_ON_ROW, DIMENSION_2D, + DATA_SIZE_8, + DMA_SYNC_RESTART)); + set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT); + set_dma_x_modify(uart->rx_dma_channel, 1); + set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT); + set_dma_y_modify(uart->rx_dma_channel, 1); + set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf); + enable_dma(uart->rx_dma_channel); + + uart->rx_dma_timer.data = (unsigned long)(uart); + uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout; + uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES; + add_timer(&(uart->rx_dma_timer)); +#else +# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled) + kgdboc_break_enabled = 0; + else { +# endif + if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED, + "BFIN_UART_RX", uart)) { + printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n"); + return -EBUSY; + } + + if (request_irq + (uart->port.irq+1, bfin_serial_tx_int, IRQF_DISABLED, + "BFIN_UART_TX", uart)) { + printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n"); + free_irq(uart->port.irq, uart); + return -EBUSY; + } + +# ifdef CONFIG_BF54x + { + /* + * UART2 and UART3 on BF548 share interrupt PINs and DMA + * controllers with SPORT2 and SPORT3. UART rx and tx + * interrupts are generated in PIO mode only when configure + * their peripheral mapping registers properly, which means + * request corresponding DMA channels in PIO mode as well. + */ + unsigned uart_dma_ch_rx, uart_dma_ch_tx; + + switch (uart->port.irq) { + case IRQ_UART3_RX: + uart_dma_ch_rx = CH_UART3_RX; + uart_dma_ch_tx = CH_UART3_TX; + break; + case IRQ_UART2_RX: + uart_dma_ch_rx = CH_UART2_RX; + uart_dma_ch_tx = CH_UART2_TX; + break; + default: + uart_dma_ch_rx = uart_dma_ch_tx = 0; + break; + }; + + if (uart_dma_ch_rx && + request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { + printk(KERN_NOTICE"Fail to attach UART interrupt\n"); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, uart); + return -EBUSY; + } + if (uart_dma_ch_tx && + request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) { + printk(KERN_NOTICE "Fail to attach UART interrupt\n"); + free_dma(uart_dma_ch_rx); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, uart); + return -EBUSY; + } + } +# endif +# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + } +# endif +#endif + +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->cts_pin >= 0) { + if (request_irq(gpio_to_irq(uart->cts_pin), + bfin_serial_mctrl_cts_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_DISABLED, "BFIN_UART_CTS", uart)) { + uart->cts_pin = -1; + pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); + } + } + if (uart->rts_pin >= 0) { + gpio_direction_output(uart->rts_pin, 0); + } +#endif +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->cts_pin >= 0 && request_irq(uart->status_irq, + bfin_serial_mctrl_cts_int, + IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) { + uart->cts_pin = -1; + pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); + } + + /* CTS RTS PINs are negative assertive. */ + UART_PUT_MCR(uart, ACTS); + UART_SET_IER(uart, EDSSI); +#endif + + UART_SET_IER(uart, ERBFI); + return 0; +} + +static void bfin_serial_shutdown(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + +#ifdef CONFIG_SERIAL_BFIN_DMA + disable_dma(uart->tx_dma_channel); + free_dma(uart->tx_dma_channel); + disable_dma(uart->rx_dma_channel); + free_dma(uart->rx_dma_channel); + del_timer(&(uart->rx_dma_timer)); + dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0); +#else +#ifdef CONFIG_BF54x + switch (uart->port.irq) { + case IRQ_UART3_RX: + free_dma(CH_UART3_RX); + free_dma(CH_UART3_TX); + break; + case IRQ_UART2_RX: + free_dma(CH_UART2_RX); + free_dma(CH_UART2_TX); + break; + default: + break; + }; +#endif + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq+1, uart); +#endif + +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->cts_pin >= 0) + free_irq(gpio_to_irq(uart->cts_pin), uart); +#endif +#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS + if (uart->cts_pin >= 0) + free_irq(uart->status_irq, uart); +#endif +} + +static void +bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned long flags; + unsigned int baud, quot; + unsigned short val, ier, lcr = 0; + + switch (termios->c_cflag & CSIZE) { + case CS8: + lcr = WLS(8); + break; + case CS7: + lcr = WLS(7); + break; + case CS6: + lcr = WLS(6); + break; + case CS5: + lcr = WLS(5); + break; + default: + printk(KERN_ERR "%s: word lengh not supported\n", + __func__); + } + + /* Anomaly notes: + * 05000231 - STOP bit is always set to 1 whatever the user is set. + */ + if (termios->c_cflag & CSTOPB) { + if (ANOMALY_05000231) + printk(KERN_WARNING "STOP bits other than 1 is not " + "supported in case of anomaly 05000231.\n"); + else + lcr |= STB; + } + if (termios->c_cflag & PARENB) + lcr |= PEN; + if (!(termios->c_cflag & PARODD)) + lcr |= EPS; + if (termios->c_cflag & CMSPAR) + lcr |= STP; + + spin_lock_irqsave(&uart->port.lock, flags); + + port->read_status_mask = OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= (FE | PE); + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= BI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= FE | PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= OE; + } + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + /* If discipline is not IRDA, apply ANOMALY_05000230 */ + if (termios->c_line != N_IRDA) + quot -= ANOMALY_05000230; + + UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); + + /* Disable UART */ + ier = UART_GET_IER(uart); + UART_DISABLE_INTS(uart); + + /* Set DLAB in LCR to Access DLL and DLH */ + UART_SET_DLAB(uart); + + UART_PUT_DLL(uart, quot & 0xFF); + UART_PUT_DLH(uart, (quot >> 8) & 0xFF); + SSYNC(); + + /* Clear DLAB in LCR to Access THR RBR IER */ + UART_CLEAR_DLAB(uart); + + UART_PUT_LCR(uart, lcr); + + /* Enable UART */ + UART_ENABLE_INTS(uart, ier); + + val = UART_GET_GCTL(uart); + val |= UCEN; + UART_PUT_GCTL(uart, val); + + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&uart->port.lock, flags); +} + +static const char *bfin_serial_type(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void bfin_serial_release_port(struct uart_port *port) +{ +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int bfin_serial_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void bfin_serial_config_port(struct uart_port *port, int flags) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + if (flags & UART_CONFIG_TYPE && + bfin_serial_request_port(&uart->port) == 0) + uart->port.type = PORT_BFIN; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_BFIN and PORT_UNKNOWN + */ +static int +bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return 0; +} + +/* + * Enable the IrDA function if tty->ldisc.num is N_IRDA. + * In other cases, disable IrDA function. + */ +static void bfin_serial_set_ldisc(struct uart_port *port, int ld) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned short val; + + switch (ld) { + case N_IRDA: + val = UART_GET_GCTL(uart); + val |= (IREN | RPOLC); + UART_PUT_GCTL(uart, val); + break; + default: + val = UART_GET_GCTL(uart); + val &= ~(IREN | RPOLC); + UART_PUT_GCTL(uart, val); + } +} + +static void bfin_serial_reset_irda(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned short val; + + val = UART_GET_GCTL(uart); + val &= ~(IREN | RPOLC); + UART_PUT_GCTL(uart, val); + SSYNC(); + val |= (IREN | RPOLC); + UART_PUT_GCTL(uart, val); + SSYNC(); +} + +#ifdef CONFIG_CONSOLE_POLL +/* Anomaly notes: + * 05000099 - Because we only use THRE in poll_put and DR in poll_get, + * losing other bits of UART_LSR is not a problem here. + */ +static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + + while (!(UART_GET_LSR(uart) & THRE)) + cpu_relax(); + + UART_CLEAR_DLAB(uart); + UART_PUT_CHAR(uart, (unsigned char)chr); +} + +static int bfin_serial_poll_get_char(struct uart_port *port) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + unsigned char chr; + + while (!(UART_GET_LSR(uart) & DR)) + cpu_relax(); + + UART_CLEAR_DLAB(uart); + chr = UART_GET_CHAR(uart); + + return chr; +} +#endif + +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) +static void bfin_kgdboc_port_shutdown(struct uart_port *port) +{ + if (kgdboc_break_enabled) { + kgdboc_break_enabled = 0; + bfin_serial_shutdown(port); + } +} + +static int bfin_kgdboc_port_startup(struct uart_port *port) +{ + kgdboc_port_line = port->line; + kgdboc_break_enabled = !bfin_serial_startup(port); + return 0; +} +#endif + +static struct uart_ops bfin_serial_pops = { + .tx_empty = bfin_serial_tx_empty, + .set_mctrl = bfin_serial_set_mctrl, + .get_mctrl = bfin_serial_get_mctrl, + .stop_tx = bfin_serial_stop_tx, + .start_tx = bfin_serial_start_tx, + .stop_rx = bfin_serial_stop_rx, + .enable_ms = bfin_serial_enable_ms, + .break_ctl = bfin_serial_break_ctl, + .startup = bfin_serial_startup, + .shutdown = bfin_serial_shutdown, + .set_termios = bfin_serial_set_termios, + .set_ldisc = bfin_serial_set_ldisc, + .type = bfin_serial_type, + .release_port = bfin_serial_release_port, + .request_port = bfin_serial_request_port, + .config_port = bfin_serial_config_port, + .verify_port = bfin_serial_verify_port, +#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ + defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) + .kgdboc_port_startup = bfin_kgdboc_port_startup, + .kgdboc_port_shutdown = bfin_kgdboc_port_shutdown, +#endif +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = bfin_serial_poll_put_char, + .poll_get_char = bfin_serial_poll_get_char, +#endif +}; + +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, + int *parity, int *bits) +{ + unsigned short status; + + status = UART_GET_IER(uart) & (ERBFI | ETBEI); + if (status == (ERBFI | ETBEI)) { + /* ok, the port was enabled */ + u16 lcr, dlh, dll; + + lcr = UART_GET_LCR(uart); + + *parity = 'n'; + if (lcr & PEN) { + if (lcr & EPS) + *parity = 'e'; + else + *parity = 'o'; + } + switch (lcr & 0x03) { + case 0: *bits = 5; break; + case 1: *bits = 6; break; + case 2: *bits = 7; break; + case 3: *bits = 8; break; + } + /* Set DLAB in LCR to Access DLL and DLH */ + UART_SET_DLAB(uart); + + dll = UART_GET_DLL(uart); + dlh = UART_GET_DLH(uart); + + /* Clear DLAB in LCR to Access THR RBR IER */ + UART_CLEAR_DLAB(uart); + + *baud = get_sclk() / (16*(dll | dlh << 8)); + } + pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits); +} + +static struct uart_driver bfin_serial_reg; + +static void bfin_serial_console_putchar(struct uart_port *port, int ch) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + while (!(UART_GET_LSR(uart) & THRE)) + barrier(); + UART_PUT_CHAR(uart, ch); +} + +#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || + defined (CONFIG_EARLY_PRINTK) */ + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE +#define CLASS_BFIN_CONSOLE "bfin-console" +/* + * Interrupts are disabled on entering + */ +static void +bfin_serial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct bfin_serial_port *uart = bfin_serial_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&uart->port.lock, flags); + uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); + spin_unlock_irqrestore(&uart->port.lock, flags); + +} + +static int __init +bfin_serial_console_setup(struct console *co, char *options) +{ + struct bfin_serial_port *uart; + int baud = 57600; + int bits = 8; + int parity = 'n'; +# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) + int flow = 'r'; +# else + int flow = 'n'; +# endif + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index < 0 || co->index >= BFIN_UART_NR_PORTS) + return -ENODEV; + + uart = bfin_serial_ports[co->index]; + if (!uart) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + bfin_serial_console_get_options(uart, &baud, &parity, &bits); + + return uart_set_options(&uart->port, co, baud, parity, bits, flow); +} + +static struct console bfin_serial_console = { + .name = BFIN_SERIAL_DEV_NAME, + .write = bfin_serial_console_write, + .device = uart_console_device, + .setup = bfin_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bfin_serial_reg, +}; +#define BFIN_SERIAL_CONSOLE &bfin_serial_console +#else +#define BFIN_SERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_BFIN_CONSOLE */ + +#ifdef CONFIG_EARLY_PRINTK +static struct bfin_serial_port bfin_earlyprintk_port; +#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk" + +/* + * Interrupts are disabled on entering + */ +static void +bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count) +{ + unsigned long flags; + + if (bfin_earlyprintk_port.port.line != co->index) + return; + + spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags); + uart_console_write(&bfin_earlyprintk_port.port, s, count, + bfin_serial_console_putchar); + spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags); +} + +/* + * This should have a .setup or .early_setup in it, but then things get called + * without the command line options, and the baud rate gets messed up - so + * don't let the common infrastructure play with things. (see calls to setup + * & earlysetup in ./kernel/printk.c:register_console() + */ +static struct __initdata console bfin_early_serial_console = { + .name = "early_BFuart", + .write = bfin_earlyprintk_console_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bfin_serial_reg, +}; +#endif + +static struct uart_driver bfin_serial_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = BFIN_SERIAL_DEV_NAME, + .major = BFIN_SERIAL_MAJOR, + .minor = BFIN_SERIAL_MINOR, + .nr = BFIN_UART_NR_PORTS, + .cons = BFIN_SERIAL_CONSOLE, +}; + +static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + return uart_suspend_port(&bfin_serial_reg, &uart->port); +} + +static int bfin_serial_resume(struct platform_device *pdev) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + return uart_resume_port(&bfin_serial_reg, &uart->port); +} + +static int bfin_serial_probe(struct platform_device *pdev) +{ + struct resource *res; + struct bfin_serial_port *uart = NULL; + int ret = 0; + + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n"); + return -ENOENT; + } + + if (bfin_serial_ports[pdev->id] == NULL) { + + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) { + dev_err(&pdev->dev, + "fail to malloc bfin_serial_port\n"); + return -ENOMEM; + } + bfin_serial_ports[pdev->id] = uart; + +#ifdef CONFIG_EARLY_PRINTK + if (!(bfin_earlyprintk_port.port.membase + && bfin_earlyprintk_port.port.line == pdev->id)) { + /* + * If the peripheral PINs of current port is allocated + * in earlyprintk probe stage, don't do it again. + */ +#endif + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + goto out_error_free_mem; + } +#ifdef CONFIG_EARLY_PRINTK + } +#endif + + spin_lock_init(&uart->port.lock); + uart->port.uartclk = get_sclk(); + uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE; + uart->port.ops = &bfin_serial_pops; + uart->port.line = pdev->id; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + uart->port.membase = ioremap(res->start, + res->end - res->start); + if (!uart->port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + uart->port.mapbase = res->start; + + uart->port.irq = platform_get_irq(pdev, 0); + if (uart->port.irq < 0) { + dev_err(&pdev->dev, "No uart RX/TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + uart->status_irq = platform_get_irq(pdev, 1); + if (uart->status_irq < 0) { + dev_err(&pdev->dev, "No uart status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + +#ifdef CONFIG_SERIAL_BFIN_DMA + uart->tx_done = 1; + uart->tx_count = 0; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No uart TX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->tx_dma_channel = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res == NULL) { + dev_err(&pdev->dev, "No uart RX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->rx_dma_channel = res->start; + + init_timer(&(uart->rx_dma_timer)); +#endif + +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + uart->cts_pin = -1; + else + uart->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + uart->rts_pin = -1; + else + uart->rts_pin = res->start; +# if defined(CONFIG_SERIAL_BFIN_CTSRTS) + if (uart->rts_pin >= 0) + gpio_request(uart->rts_pin, DRIVER_NAME); +# endif +#endif + } + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + uart = bfin_serial_ports[pdev->id]; + uart->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, uart); + ret = uart_add_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + } +#endif + + if (!ret) + return 0; + + if (uart) { +out_error_unmap: + iounmap(uart->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; + } + + return ret; +} + +static int __devexit bfin_serial_remove(struct platform_device *pdev) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (uart) { + uart_remove_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->rts_pin >= 0) + gpio_free(uart->rts_pin); +#endif + iounmap(uart->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; + } + + return 0; +} + +static struct platform_driver bfin_serial_driver = { + .probe = bfin_serial_probe, + .remove = __devexit_p(bfin_serial_remove), + .suspend = bfin_serial_suspend, + .resume = bfin_serial_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) +static __initdata struct early_platform_driver early_bfin_serial_driver = { + .class_str = CLASS_BFIN_CONSOLE, + .pdrv = &bfin_serial_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init bfin_serial_rs_console_init(void) +{ + early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME); + + early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0); + + register_console(&bfin_serial_console); + + return 0; +} +console_initcall(bfin_serial_rs_console_init); +#endif + +#ifdef CONFIG_EARLY_PRINTK +/* + * Memory can't be allocated dynamically during earlyprink init stage. + * So, do individual probe for earlyprink with a static uart port variable. + */ +static int bfin_earlyprintk_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n"); + return -ENOENT; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + return ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + bfin_earlyprintk_port.port.membase = ioremap(res->start, + res->end - res->start); + if (!bfin_earlyprintk_port.port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + bfin_earlyprintk_port.port.mapbase = res->start; + bfin_earlyprintk_port.port.line = pdev->id; + bfin_earlyprintk_port.port.uartclk = get_sclk(); + bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE; + spin_lock_init(&bfin_earlyprintk_port.port.lock); + + return 0; + +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + + return ret; +} + +static struct platform_driver bfin_earlyprintk_driver = { + .probe = bfin_earlyprintk_probe, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { + .class_str = CLASS_BFIN_EARLYPRINTK, + .pdrv = &bfin_earlyprintk_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +struct console __init *bfin_earlyserial_init(unsigned int port, + unsigned int cflag) +{ + struct ktermios t; + char port_name[20]; + + if (port < 0 || port >= BFIN_UART_NR_PORTS) + return NULL; + + /* + * Only probe resource of the given port in earlyprintk boot arg. + * The expected port id should be indicated in port name string. + */ + snprintf(port_name, 20, DRIVER_NAME ".%d", port); + early_platform_driver_register(&early_bfin_earlyprintk_driver, + port_name); + early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0); + + if (!bfin_earlyprintk_port.port.membase) + return NULL; + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + /* + * If we are using early serial, don't let the normal console rewind + * log buffer, since that causes things to be printed multiple times + */ + bfin_serial_console.flags &= ~CON_PRINTBUFFER; +#endif + + bfin_early_serial_console.index = port; + t.c_cflag = cflag; + t.c_iflag = 0; + t.c_oflag = 0; + t.c_lflag = ICANON; + t.c_line = port; + bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t); + + return &bfin_early_serial_console; +} +#endif /* CONFIG_EARLY_PRINTK */ + +static int __init bfin_serial_init(void) +{ + int ret; + + pr_info("Blackfin serial driver\n"); + + ret = uart_register_driver(&bfin_serial_reg); + if (ret) { + pr_err("failed to register %s:%d\n", + bfin_serial_reg.driver_name, ret); + } + + ret = platform_driver_register(&bfin_serial_driver); + if (ret) { + pr_err("fail to register bfin uart\n"); + uart_unregister_driver(&bfin_serial_reg); + } + + return ret; +} + +static void __exit bfin_serial_exit(void) +{ + platform_driver_unregister(&bfin_serial_driver); + uart_unregister_driver(&bfin_serial_reg); +} + + +module_init(bfin_serial_init); +module_exit(bfin_serial_exit); + +MODULE_AUTHOR("Sonic Zhang, Aubrey Li"); +MODULE_DESCRIPTION("Blackfin generic serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR); +MODULE_ALIAS("platform:bfin-uart"); diff --git a/drivers/tty/serial/bfin_sport_uart.c b/drivers/tty/serial/bfin_sport_uart.c new file mode 100644 index 000000000000..e95c524d9d18 --- /dev/null +++ b/drivers/tty/serial/bfin_sport_uart.c @@ -0,0 +1,935 @@ +/* + * Blackfin On-Chip Sport Emulated UART Driver + * + * Copyright 2006-2009 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +/* + * This driver and the hardware supported are in term of EE-191 of ADI. + * http://www.analog.com/static/imported-files/application_notes/EE191.pdf + * This application note describe how to implement a UART on a Sharc DSP, + * but this driver is implemented on Blackfin Processor. + * Transmit Frame Sync is not used by this driver to transfer data out. + */ + +/* #define DEBUG */ + +#define DRV_NAME "bfin-sport-uart" +#define DEVICE_NAME "ttySS" +#define pr_fmt(fmt) DRV_NAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "bfin_sport_uart.h" + +struct sport_uart_port { + struct uart_port port; + int err_irq; + unsigned short csize; + unsigned short rxmask; + unsigned short txmask1; + unsigned short txmask2; + unsigned char stopb; +/* unsigned char parib; */ +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int cts_pin; + int rts_pin; +#endif +}; + +static int sport_uart_tx_chars(struct sport_uart_port *up); +static void sport_stop_tx(struct uart_port *port); + +static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) +{ + pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value, + up->txmask1, up->txmask2); + + /* Place Start and Stop bits */ + __asm__ __volatile__ ( + "%[val] <<= 1;" + "%[val] = %[val] & %[mask1];" + "%[val] = %[val] | %[mask2];" + : [val]"+d"(value) + : [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2) + : "ASTAT" + ); + pr_debug("%s value:%x\n", __func__, value); + + SPORT_PUT_TX(up, value); +} + +static inline unsigned char rx_one_byte(struct sport_uart_port *up) +{ + unsigned int value; + unsigned char extract; + u32 tmp_mask1, tmp_mask2, tmp_shift, tmp; + + if ((up->csize + up->stopb) > 7) + value = SPORT_GET_RX32(up); + else + value = SPORT_GET_RX(up); + + pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value, + up->csize, up->rxmask); + + /* Extract data */ + __asm__ __volatile__ ( + "%[extr] = 0;" + "%[mask1] = %[rxmask];" + "%[mask2] = 0x0200(Z);" + "%[shift] = 0;" + "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];" + ".Lloop_s:" + "%[tmp] = extract(%[val], %[mask1].L)(Z);" + "%[tmp] <<= %[shift];" + "%[extr] = %[extr] | %[tmp];" + "%[mask1] = %[mask1] - %[mask2];" + ".Lloop_e:" + "%[shift] += 1;" + : [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp), + [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2) + : [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize) + : "ASTAT", "LB0", "LC0", "LT0" + ); + + pr_debug(" extract:%x\n", extract); + return extract; +} + +static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate) +{ + int tclkdiv, rclkdiv; + unsigned int sclk = get_sclk(); + + /* Set TCR1 and TCR2, TFSR is not enabled for uart */ + SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK)); + SPORT_PUT_TCR2(up, size + 1); + pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); + + /* Set RCR1 and RCR2 */ + SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); + SPORT_PUT_RCR2(up, (size + 1) * 2 - 1); + pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); + + tclkdiv = sclk / (2 * baud_rate) - 1; + /* The actual uart baud rate of devices vary between +/-2%. The sport + * RX sample rate should be faster than the double of the worst case, + * otherwise, wrong data are received. So, set sport RX clock to be + * 3% faster. + */ + rclkdiv = sclk / (2 * baud_rate * 2 * 97 / 100) - 1; + SPORT_PUT_TCLKDIV(up, tclkdiv); + SPORT_PUT_RCLKDIV(up, rclkdiv); + SSYNC(); + pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n", + __func__, sclk, baud_rate, tclkdiv, rclkdiv); + + return 0; +} + +static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) +{ + struct sport_uart_port *up = dev_id; + struct tty_struct *tty = up->port.state->port.tty; + unsigned int ch; + + spin_lock(&up->port.lock); + + while (SPORT_GET_STAT(up) & RXNE) { + ch = rx_one_byte(up); + up->port.icount.rx++; + + if (!uart_handle_sysrq_char(&up->port, ch)) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } + tty_flip_buffer_push(tty); + + spin_unlock(&up->port.lock); + + return IRQ_HANDLED; +} + +static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) +{ + struct sport_uart_port *up = dev_id; + + spin_lock(&up->port.lock); + sport_uart_tx_chars(up); + spin_unlock(&up->port.lock); + + return IRQ_HANDLED; +} + +static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) +{ + struct sport_uart_port *up = dev_id; + struct tty_struct *tty = up->port.state->port.tty; + unsigned int stat = SPORT_GET_STAT(up); + + spin_lock(&up->port.lock); + + /* Overflow in RX FIFO */ + if (stat & ROVF) { + up->port.icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */ + } + /* These should not happen */ + if (stat & (TOVF | TUVF | RUVF)) { + pr_err("SPORT Error:%s %s %s\n", + (stat & TOVF) ? "TX overflow" : "", + (stat & TUVF) ? "TX underflow" : "", + (stat & RUVF) ? "RX underflow" : ""); + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); + } + SSYNC(); + + spin_unlock(&up->port.lock); + return IRQ_HANDLED; +} + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (SPORT_UART_GET_CTS(up)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + SPORT_UART_ENABLE_RTS(up); + else + SPORT_UART_DISABLE_RTS(up); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id) +{ + struct sport_uart_port *up = (struct sport_uart_port *)dev_id; + unsigned int status; + + status = sport_get_mctrl(&up->port); + uart_handle_cts_change(&up->port, status & TIOCM_CTS); + + return IRQ_HANDLED; +} +#else +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); + return TIOCM_CTS | TIOCM_CD | TIOCM_DSR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + pr_debug("%s enter\n", __func__); +} +#endif + +/* Reqeust IRQ, Setup clock */ +static int sport_startup(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + int ret; + + pr_debug("%s enter\n", __func__); + ret = request_irq(up->port.irq, sport_uart_rx_irq, 0, + "SPORT_UART_RX", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT RX interrupt\n"); + return ret; + } + + ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0, + "SPORT_UART_TX", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT TX interrupt\n"); + goto fail1; + } + + ret = request_irq(up->err_irq, sport_uart_err_irq, 0, + "SPORT_UART_STATUS", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT status interrupt\n"); + goto fail2; + } + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) { + if (request_irq(gpio_to_irq(up->cts_pin), + sport_mctrl_cts_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { + up->cts_pin = -1; + dev_info(port->dev, "Unable to attach BlackFin UART \ + over SPORT CTS interrupt. So, disable it.\n"); + } + } + if (up->rts_pin >= 0) + gpio_direction_output(up->rts_pin, 0); +#endif + + return 0; + fail2: + free_irq(up->port.irq+1, up); + fail1: + free_irq(up->port.irq, up); + + return ret; +} + +/* + * sport_uart_tx_chars + * + * ret 1 means need to enable sport. + * ret 0 means do nothing. + */ +static int sport_uart_tx_chars(struct sport_uart_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + + if (SPORT_GET_STAT(up) & TXF) + return 0; + + if (up->port.x_char) { + tx_one_byte(up, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return 1; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + /* The waiting loop to stop SPORT TX from TX interrupt is + * too long. This may block SPORT RX interrupts and cause + * RX FIFO overflow. So, do stop sport TX only after the last + * char in TX FIFO is moved into the shift register. + */ + if (SPORT_GET_STAT(up) & TXHRE) + sport_stop_tx(&up->port); + return 0; + } + + while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { + tx_one_byte(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); + up->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + return 1; +} + +static unsigned int sport_tx_empty(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + unsigned int stat; + + stat = SPORT_GET_STAT(up); + pr_debug("%s stat:%04x\n", __func__, stat); + if (stat & TXHRE) { + return TIOCSER_TEMT; + } else + return 0; +} + +static void sport_stop_tx(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __func__); + + if (!(SPORT_GET_TCR1(up) & TSPEN)) + return; + + /* Although the hold register is empty, last byte is still in shift + * register and not sent out yet. So, put a dummy data into TX FIFO. + * Then, sport tx stops when last byte is shift out and the dummy + * data is moved into the shift register. + */ + SPORT_PUT_TX(up, 0xffff); + while (!(SPORT_GET_STAT(up) & TXHRE)) + cpu_relax(); + + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SSYNC(); + + return; +} + +static void sport_start_tx(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __func__); + + /* Write data into SPORT FIFO before enable SPROT to transmit */ + if (sport_uart_tx_chars(up)) { + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + } + + pr_debug("%s exit\n", __func__); +} + +static void sport_stop_rx(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __func__); + /* Disable sport to stop rx */ + SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); + SSYNC(); +} + +static void sport_enable_ms(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); +} + +static void sport_break_ctl(struct uart_port *port, int break_state) +{ + pr_debug("%s enter\n", __func__); +} + +static void sport_shutdown(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + dev_dbg(port->dev, "%s enter\n", __func__); + + /* Disable sport */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); + SSYNC(); + + free_irq(up->port.irq, up); + free_irq(up->port.irq+1, up); + free_irq(up->err_irq, up); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) + free_irq(gpio_to_irq(up->cts_pin), up); +#endif +} + +static const char *sport_type(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __func__); + return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL; +} + +static void sport_release_port(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); +} + +static int sport_request_port(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); + return 0; +} + +static void sport_config_port(struct uart_port *port, int flags) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + pr_debug("%s enter\n", __func__); + up->port.type = PORT_BFIN_SPORT; +} + +static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + pr_debug("%s enter\n", __func__); + return 0; +} + +static void sport_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + unsigned long flags; + int i; + + pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag); + + switch (termios->c_cflag & CSIZE) { + case CS8: + up->csize = 8; + break; + case CS7: + up->csize = 7; + break; + case CS6: + up->csize = 6; + break; + case CS5: + up->csize = 5; + break; + default: + pr_warning("requested word length not supported\n"); + } + + if (termios->c_cflag & CSTOPB) { + up->stopb = 1; + } + if (termios->c_cflag & PARENB) { + pr_warning("PAREN bits is not supported yet\n"); + /* up->parib = 1; */ + } + + spin_lock_irqsave(&up->port.lock, flags); + + port->read_status_mask = 0; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + + /* RX extract mask */ + up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); + /* TX masks, 8 bit data and 1 bit stop for example: + * mask1 = b#0111111110 + * mask2 = b#1000000000 + */ + for (i = 0, up->txmask1 = 0; i < up->csize; i++) + up->txmask1 |= (1<txmask2 = (1<stopb) { + ++i; + up->txmask2 |= (1<txmask1 <<= 1; + up->txmask2 <<= 1; + /* uart baud rate */ + port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); + + /* Disable UART */ + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); + + sport_uart_setup(up, up->csize + up->stopb, port->uartclk); + + /* driver TX line high after config, one dummy data is + * necessary to stop sport after shift one byte + */ + SPORT_PUT_TX(up, 0xffff); + SPORT_PUT_TX(up, 0xffff); + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + while (!(SPORT_GET_STAT(up) & TXHRE)) + cpu_relax(); + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SSYNC(); + + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, port->uartclk); + + /* Enable sport rx */ + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN); + SSYNC(); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +struct uart_ops sport_uart_ops = { + .tx_empty = sport_tx_empty, + .set_mctrl = sport_set_mctrl, + .get_mctrl = sport_get_mctrl, + .stop_tx = sport_stop_tx, + .start_tx = sport_start_tx, + .stop_rx = sport_stop_rx, + .enable_ms = sport_enable_ms, + .break_ctl = sport_break_ctl, + .startup = sport_startup, + .shutdown = sport_shutdown, + .set_termios = sport_set_termios, + .type = sport_type, + .release_port = sport_release_port, + .request_port = sport_request_port, + .config_port = sport_config_port, + .verify_port = sport_verify_port, +}; + +#define BFIN_SPORT_UART_MAX_PORTS 4 + +static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console" + +static int __init +sport_uart_console_setup(struct console *co, char *options) +{ + struct sport_uart_port *up; + int baud = 57600; + int bits = 8; + int parity = 'n'; +# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int flow = 'r'; +# else + int flow = 'n'; +# endif + + /* Check whether an invalid uart number has been specified */ + if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) + return -ENODEV; + + up = bfin_sport_uart_ports[co->index]; + if (!up) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static void sport_uart_console_putchar(struct uart_port *port, int ch) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + while (SPORT_GET_STAT(up) & TXF) + barrier(); + + tx_one_byte(up, ch); +} + +/* + * Interrupts are disabled on entering + */ +static void +sport_uart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct sport_uart_port *up = bfin_sport_uart_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + if (SPORT_GET_TCR1(up) & TSPEN) + uart_console_write(&up->port, s, count, sport_uart_console_putchar); + else { + /* dummy data to start sport */ + while (SPORT_GET_STAT(up) & TXF) + barrier(); + SPORT_PUT_TX(up, 0xffff); + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + + uart_console_write(&up->port, s, count, sport_uart_console_putchar); + + /* Although the hold register is empty, last byte is still in shift + * register and not sent out yet. So, put a dummy data into TX FIFO. + * Then, sport tx stops when last byte is shift out and the dummy + * data is moved into the shift register. + */ + while (SPORT_GET_STAT(up) & TXF) + barrier(); + SPORT_PUT_TX(up, 0xffff); + while (!(SPORT_GET_STAT(up) & TXHRE)) + barrier(); + + /* Stop sport tx transfer */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SSYNC(); + } + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver sport_uart_reg; + +static struct console sport_uart_console = { + .name = DEVICE_NAME, + .write = sport_uart_console_write, + .device = uart_console_device, + .setup = sport_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sport_uart_reg, +}; + +#define SPORT_UART_CONSOLE (&sport_uart_console) +#else +#define SPORT_UART_CONSOLE NULL +#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */ + + +static struct uart_driver sport_uart_reg = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = DEVICE_NAME, + .major = 204, + .minor = 84, + .nr = BFIN_SPORT_UART_MAX_PORTS, + .cons = SPORT_UART_CONSOLE, +}; + +#ifdef CONFIG_PM +static int sport_uart_suspend(struct device *dev) +{ + struct sport_uart_port *sport = dev_get_drvdata(dev); + + dev_dbg(dev, "%s enter\n", __func__); + if (sport) + uart_suspend_port(&sport_uart_reg, &sport->port); + + return 0; +} + +static int sport_uart_resume(struct device *dev) +{ + struct sport_uart_port *sport = dev_get_drvdata(dev); + + dev_dbg(dev, "%s enter\n", __func__); + if (sport) + uart_resume_port(&sport_uart_reg, &sport->port); + + return 0; +} + +static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { + .suspend = sport_uart_suspend, + .resume = sport_uart_resume, +}; +#endif + +static int __devinit sport_uart_probe(struct platform_device *pdev) +{ + struct resource *res; + struct sport_uart_port *sport; + int ret = 0; + + dev_dbg(&pdev->dev, "%s enter\n", __func__); + + if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) { + dev_err(&pdev->dev, "Wrong sport uart platform device id.\n"); + return -ENOENT; + } + + if (bfin_sport_uart_ports[pdev->id] == NULL) { + bfin_sport_uart_ports[pdev->id] = + kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL); + sport = bfin_sport_uart_ports[pdev->id]; + if (!sport) { + dev_err(&pdev->dev, + "Fail to malloc sport_uart_port\n"); + return -ENOMEM; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRV_NAME); + if (ret) { + dev_err(&pdev->dev, + "Fail to request SPORT peripherals\n"); + goto out_error_free_mem; + } + + spin_lock_init(&sport->port.lock); + sport->port.fifosize = SPORT_TX_FIFO_SIZE, + sport->port.ops = &sport_uart_ops; + sport->port.line = pdev->id; + sport->port.iotype = UPIO_MEM; + sport->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + sport->port.membase = ioremap(res->start, resource_size(res)); + if (!sport->port.membase) { + dev_err(&pdev->dev, "Cannot map sport IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + sport->port.mapbase = res->start; + + sport->port.irq = platform_get_irq(pdev, 0); + if (sport->port.irq < 0) { + dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + sport->err_irq = platform_get_irq(pdev, 1); + if (sport->err_irq < 0) { + dev_err(&pdev->dev, "No sport status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + sport->cts_pin = -1; + else + sport->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + sport->rts_pin = -1; + else + sport->rts_pin = res->start; + + if (sport->rts_pin >= 0) + gpio_request(sport->rts_pin, DRV_NAME); +#endif + } + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + sport = bfin_sport_uart_ports[pdev->id]; + sport->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, sport); + ret = uart_add_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE + } +#endif + if (!ret) + return 0; + + if (sport) { +out_error_unmap: + iounmap(sport->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(sport); + bfin_sport_uart_ports[pdev->id] = NULL; + } + + return ret; +} + +static int __devexit sport_uart_remove(struct platform_device *pdev) +{ + struct sport_uart_port *sport = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "%s enter\n", __func__); + dev_set_drvdata(&pdev->dev, NULL); + + if (sport) { + uart_remove_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (sport->rts_pin >= 0) + gpio_free(sport->rts_pin); +#endif + iounmap(sport->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(sport); + bfin_sport_uart_ports[pdev->id] = NULL; + } + + return 0; +} + +static struct platform_driver sport_uart_driver = { + .probe = sport_uart_probe, + .remove = __devexit_p(sport_uart_remove), + .driver = { + .name = DRV_NAME, +#ifdef CONFIG_PM + .pm = &bfin_sport_uart_dev_pm_ops, +#endif + }, +}; + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +static __initdata struct early_platform_driver early_sport_uart_driver = { + .class_str = CLASS_BFIN_SPORT_CONSOLE, + .pdrv = &sport_uart_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init sport_uart_rs_console_init(void) +{ + early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); + + early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE, + BFIN_SPORT_UART_MAX_PORTS, 0); + + register_console(&sport_uart_console); + + return 0; +} +console_initcall(sport_uart_rs_console_init); +#endif + +static int __init sport_uart_init(void) +{ + int ret; + + pr_info("Blackfin uart over sport driver\n"); + + ret = uart_register_driver(&sport_uart_reg); + if (ret) { + pr_err("failed to register %s:%d\n", + sport_uart_reg.driver_name, ret); + return ret; + } + + ret = platform_driver_register(&sport_uart_driver); + if (ret) { + pr_err("failed to register sport uart driver:%d\n", ret); + uart_unregister_driver(&sport_uart_reg); + } + + return ret; +} +module_init(sport_uart_init); + +static void __exit sport_uart_exit(void) +{ + platform_driver_unregister(&sport_uart_driver); + uart_unregister_driver(&sport_uart_reg); +} +module_exit(sport_uart_exit); + +MODULE_AUTHOR("Sonic Zhang, Roy Huang"); +MODULE_DESCRIPTION("Blackfin serial over SPORT driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/bfin_sport_uart.h b/drivers/tty/serial/bfin_sport_uart.h new file mode 100644 index 000000000000..6d06ce1d5675 --- /dev/null +++ b/drivers/tty/serial/bfin_sport_uart.h @@ -0,0 +1,86 @@ +/* + * Blackfin On-Chip Sport Emulated UART Driver + * + * Copyright 2006-2008 Analog Devices Inc. + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Licensed under the GPL-2 or later. + */ + +/* + * This driver and the hardware supported are in term of EE-191 of ADI. + * http://www.analog.com/static/imported-files/application_notes/EE191.pdf + * This application note describe how to implement a UART on a Sharc DSP, + * but this driver is implemented on Blackfin Processor. + * Transmit Frame Sync is not used by this driver to transfer data out. + */ + +#ifndef _BFIN_SPORT_UART_H +#define _BFIN_SPORT_UART_H + +#define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ +#define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ +#define OFFSET_TCLKDIV 0x08 /* Transmit Serial Clock Divider Register */ +#define OFFSET_TFSDIV 0x0C /* Transmit Frame Sync Divider Register */ +#define OFFSET_TX 0x10 /* Transmit Data Register */ +#define OFFSET_RX 0x18 /* Receive Data Register */ +#define OFFSET_RCR1 0x20 /* Receive Configuration 1 Register */ +#define OFFSET_RCR2 0x24 /* Receive Configuration 2 Register */ +#define OFFSET_RCLKDIV 0x28 /* Receive Serial Clock Divider Register */ +#define OFFSET_RFSDIV 0x2c /* Receive Frame Sync Divider Register */ +#define OFFSET_STAT 0x30 /* Status Register */ + +#define SPORT_GET_TCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR1)) +#define SPORT_GET_TCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_TCR2)) +#define SPORT_GET_TCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TCLKDIV)) +#define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) +#define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) +#define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) +/* + * If another interrupt fires while doing a 32-bit read from RX FIFO, + * a fake RX underflow error will be generated. So disable interrupts + * to prevent interruption while reading the FIFO. + */ +#define SPORT_GET_RX32(sport) \ +({ \ + unsigned int __ret; \ + if (ANOMALY_05000473) \ + local_irq_disable(); \ + __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ + if (ANOMALY_05000473) \ + local_irq_enable(); \ + __ret; \ +}) +#define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) +#define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) +#define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) +#define SPORT_GET_RFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RFSDIV)) +#define SPORT_GET_STAT(sport) bfin_read16(((sport)->port.membase + OFFSET_STAT)) + +#define SPORT_PUT_TCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR1), v) +#define SPORT_PUT_TCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCR2), v) +#define SPORT_PUT_TCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TCLKDIV), v) +#define SPORT_PUT_TFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TFSDIV), v) +#define SPORT_PUT_TX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_TX), v) +#define SPORT_PUT_RX(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RX), v) +#define SPORT_PUT_RCR1(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR1), v) +#define SPORT_PUT_RCR2(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCR2), v) +#define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) +#define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) +#define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) + +#define SPORT_TX_FIFO_SIZE 8 + +#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin) +#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1) +#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0) + +#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS) +# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS +#endif + +#endif /* _BFIN_SPORT_UART_H */ diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c new file mode 100644 index 000000000000..b6acd19b458e --- /dev/null +++ b/drivers/tty/serial/clps711x.c @@ -0,0 +1,579 @@ +/* + * linux/drivers/char/clps711x.c + * + * Driver for CLPS711x serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * 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 + */ + +#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 UART_ANY_ERR (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR) + +#define tx_enabled(port) ((port)->unused[0]) + +static void clps711xuart_stop_tx(struct uart_port *port) +{ + if (tx_enabled(port)) { + disable_irq(TX_IRQ(port)); + tx_enabled(port) = 0; + } +} + +static void clps711xuart_start_tx(struct uart_port *port) +{ + if (!tx_enabled(port)) { + enable_irq(TX_IRQ(port)); + tx_enabled(port) = 1; + } +} + +static void clps711xuart_stop_rx(struct uart_port *port) +{ + disable_irq(RX_IRQ(port)); +} + +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; + unsigned int status, ch, flg; + + status = clps_readl(SYSFLG(port)); + while (!(status & SYSFLG_URXFE)) { + ch = clps_readl(UARTDR(port)); + + 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) + port->icount.parity++; + else if (ch & UARTDR_FRMERR) + port->icount.frame++; + if (ch & UARTDR_OVERR) + port->icount.overrun++; + + ch &= port->read_status_mask; + + if (ch & UARTDR_PARERR) + flg = TTY_PARITY; + else if (ch & UARTDR_FRMERR) + flg = TTY_FRAME; + +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + /* + * CHECK: does overrun affect the current character? + * ASSUMPTION: it does not. + */ + uart_insert_char(port, ch, UARTDR_OVERR, ch, flg); + + ignore_char: + status = clps_readl(SYSFLG(port)); + } + tty_flip_buffer_push(tty); + return IRQ_HANDLED; +} + +static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + clps_writel(port->x_char, UARTDR(port)); + port->icount.tx++; + port->x_char = 0; + return IRQ_HANDLED; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + clps711xuart_stop_tx(port); + return IRQ_HANDLED; + } + + count = port->fifosize >> 1; + do { + clps_writel(xmit->buf[xmit->tail], UARTDR(port)); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + clps711xuart_stop_tx(port); + + return IRQ_HANDLED; +} + +static unsigned int clps711xuart_tx_empty(struct uart_port *port) +{ + unsigned int status = clps_readl(SYSFLG(port)); + return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT; +} + +static unsigned int clps711xuart_get_mctrl(struct uart_port *port) +{ + unsigned int port_addr; + unsigned int result = 0; + unsigned int status; + + port_addr = SYSFLG(port); + if (port_addr == SYSFLG1) { + status = clps_readl(SYSFLG1); + if (status & SYSFLG1_DCD) + result |= TIOCM_CAR; + if (status & SYSFLG1_DSR) + result |= TIOCM_DSR; + if (status & SYSFLG1_CTS) + result |= TIOCM_CTS; + } + + return result; +} + +static void +clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl) +{ +} + +static void clps711xuart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int ubrlcr; + + spin_lock_irqsave(&port->lock, flags); + ubrlcr = clps_readl(UBRLCR(port)); + if (break_state == -1) + ubrlcr |= UBRLCR_BREAK; + else + ubrlcr &= ~UBRLCR_BREAK; + clps_writel(ubrlcr, UBRLCR(port)); + spin_unlock_irqrestore(&port->lock, flags); +} + +static int clps711xuart_startup(struct uart_port *port) +{ + unsigned int syscon; + int retval; + + tx_enabled(port) = 1; + + /* + * 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; + } + + /* + * enable the port + */ + syscon = clps_readl(SYSCON(port)); + syscon |= SYSCON_UARTEN; + clps_writel(syscon, SYSCON(port)); + + return 0; +} + +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 */ + + /* + * 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)); +} + +static void +clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int ubrlcr, baud, quot; + unsigned long flags; + + /* + * We don't implement CREAD. + */ + 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); + quot = uart_get_divisor(port, baud); + + switch (termios->c_cflag & CSIZE) { + case CS5: + ubrlcr = UBRLCR_WRDLEN5; + break; + case CS6: + ubrlcr = UBRLCR_WRDLEN6; + break; + case CS7: + ubrlcr = UBRLCR_WRDLEN7; + break; + default: // CS8 + 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)) + ubrlcr |= UBRLCR_EVENPRT; + } + if (port->fifosize > 1) + ubrlcr |= UBRLCR_FIFOEN; + + spin_lock_irqsave(&port->lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = UARTDR_OVERR; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR; + + /* + * Characters to ignore + */ + 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; + } + + quot -= 1; + + clps_writel(ubrlcr | quot, UBRLCR(port)); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *clps711xuart_type(struct uart_port *port) +{ + return port->type == PORT_CLPS711X ? "CLPS711x" : NULL; +} + +/* + * Configure/autoconfigure the port. + */ +static void clps711xuart_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 int clps711xuart_request_port(struct uart_port *port) +{ + return 0; +} + +static struct uart_ops clps711x_pops = { + .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 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) +{ + while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF) + barrier(); + clps_writel(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) +{ + struct uart_port *port = clps711x_ports + co->index; + unsigned int status, syscon; + + /* + * 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); + + /* + * Finally, wait for transmitter to become empty + * and restore the uart state. + */ + do { + status = clps_readl(SYSFLG(port)); + } while (status & SYSFLG_UBUSY); + + clps_writel(syscon, SYSCON(port)); +} + +static void __init +clps711xuart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) { + unsigned int ubrlcr, quot; + + ubrlcr = clps_readl(UBRLCR(port)); + + *parity = 'n'; + if (ubrlcr & UBRLCR_PRTEN) { + if (ubrlcr & UBRLCR_EVENPRT) + *parity = 'e'; + else + *parity = 'o'; + } + + if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7) + *bits = 7; + else + *bits = 8; + + quot = ubrlcr & UBRLCR_BAUD_MASK; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init clps711xuart_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); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + clps711xuart_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 __init clps711xuart_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: CLPS711x driver\n"); + + ret = uart_register_driver(&clps711x_reg); + if (ret) + return ret; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&clps711x_reg, &clps711x_ports[i]); + + return 0; +} + +static void __exit clps711xuart_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]); + + uart_unregister_driver(&clps711x_reg); +} + +module_init(clps711xuart_init); +module_exit(clps711xuart_exit); + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("CLPS-711x generic serial driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR); diff --git a/drivers/tty/serial/cpm_uart/Makefile b/drivers/tty/serial/cpm_uart/Makefile new file mode 100644 index 000000000000..e072724ea754 --- /dev/null +++ b/drivers/tty/serial/cpm_uart/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the Motorola 8xx FEC ethernet controller +# + +obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o + +# Select the correct platform objects. +cpm_uart-objs-$(CONFIG_CPM2) += cpm_uart_cpm2.o +cpm_uart-objs-$(CONFIG_8xx) += cpm_uart_cpm1.o + +cpm_uart-objs := cpm_uart_core.o $(cpm_uart-objs-y) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart.h b/drivers/tty/serial/cpm_uart/cpm_uart.h new file mode 100644 index 000000000000..b754dcf0fda5 --- /dev/null +++ b/drivers/tty/serial/cpm_uart/cpm_uart.h @@ -0,0 +1,147 @@ +/* + * linux/drivers/serial/cpm_uart.h + * + * Driver for CPM (SCC/SMC) serial ports + * + * Copyright (C) 2004 Freescale Semiconductor, Inc. + * + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ +#ifndef CPM_UART_H +#define CPM_UART_H + +#include +#include + +#if defined(CONFIG_CPM2) +#include "cpm_uart_cpm2.h" +#elif defined(CONFIG_8xx) +#include "cpm_uart_cpm1.h" +#endif + +#define SERIAL_CPM_MAJOR 204 +#define SERIAL_CPM_MINOR 46 + +#define IS_SMC(pinfo) (pinfo->flags & FLAG_SMC) +#define IS_DISCARDING(pinfo) (pinfo->flags & FLAG_DISCARDING) +#define FLAG_DISCARDING 0x00000004 /* when set, don't discard */ +#define FLAG_SMC 0x00000002 +#define FLAG_CONSOLE 0x00000001 + +#define UART_SMC1 fsid_smc1_uart +#define UART_SMC2 fsid_smc2_uart +#define UART_SCC1 fsid_scc1_uart +#define UART_SCC2 fsid_scc2_uart +#define UART_SCC3 fsid_scc3_uart +#define UART_SCC4 fsid_scc4_uart + +#define UART_NR fs_uart_nr + +#define RX_NUM_FIFO 4 +#define RX_BUF_SIZE 32 +#define TX_NUM_FIFO 4 +#define TX_BUF_SIZE 32 + +#define SCC_WAIT_CLOSING 100 + +#define GPIO_CTS 0 +#define GPIO_RTS 1 +#define GPIO_DCD 2 +#define GPIO_DSR 3 +#define GPIO_DTR 4 +#define GPIO_RI 5 + +#define NUM_GPIOS (GPIO_RI+1) + +struct uart_cpm_port { + struct uart_port port; + u16 rx_nrfifos; + u16 rx_fifosize; + u16 tx_nrfifos; + u16 tx_fifosize; + smc_t __iomem *smcp; + smc_uart_t __iomem *smcup; + scc_t __iomem *sccp; + scc_uart_t __iomem *sccup; + cbd_t __iomem *rx_bd_base; + cbd_t __iomem *rx_cur; + cbd_t __iomem *tx_bd_base; + cbd_t __iomem *tx_cur; + unsigned char *tx_buf; + unsigned char *rx_buf; + u32 flags; + struct clk *clk; + u8 brg; + uint dp_addr; + void *mem_addr; + dma_addr_t dma_addr; + u32 mem_size; + /* wait on close if needed */ + int wait_closing; + /* value to combine with opcode to form cpm command */ + u32 command; + int gpios[NUM_GPIOS]; +}; + +extern int cpm_uart_nr; +extern struct uart_cpm_port cpm_uart_ports[UART_NR]; + +/* these are located in their respective files */ +void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd); +void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, + struct device_node *np); +void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram); +int cpm_uart_init_portdesc(void); +int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con); +void cpm_uart_freebuf(struct uart_cpm_port *pinfo); + +void smc1_lineif(struct uart_cpm_port *pinfo); +void smc2_lineif(struct uart_cpm_port *pinfo); +void scc1_lineif(struct uart_cpm_port *pinfo); +void scc2_lineif(struct uart_cpm_port *pinfo); +void scc3_lineif(struct uart_cpm_port *pinfo); +void scc4_lineif(struct uart_cpm_port *pinfo); + +/* + virtual to phys transtalion +*/ +static inline unsigned long cpu2cpm_addr(void *addr, + struct uart_cpm_port *pinfo) +{ + int offset; + u32 val = (u32)addr; + u32 mem = (u32)pinfo->mem_addr; + /* sane check */ + if (likely(val >= mem && val < mem + pinfo->mem_size)) { + offset = val - mem; + return pinfo->dma_addr + offset; + } + /* something nasty happened */ + BUG(); + return 0; +} + +static inline void *cpm2cpu_addr(unsigned long addr, + struct uart_cpm_port *pinfo) +{ + int offset; + u32 val = addr; + u32 dma = (u32)pinfo->dma_addr; + /* sane check */ + if (likely(val >= dma && val < dma + pinfo->mem_size)) { + offset = val - dma; + return pinfo->mem_addr + offset; + } + /* something nasty happened */ + BUG(); + return NULL; +} + + +#endif /* CPM_UART_H */ diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c new file mode 100644 index 000000000000..8692ff98fc07 --- /dev/null +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -0,0 +1,1443 @@ +/* + * linux/drivers/serial/cpm_uart.c + * + * Driver for CPM (SCC/SMC) serial ports; core driver + * + * Based on arch/ppc/cpm2_io/uart.c by Dan Malek + * Based on ppc8xx.c by Thomas Gleixner + * Based on drivers/serial/amba.c by Russell King + * + * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) + * Pantelis Antoniou (panto@intracom.gr) (CPM1) + * + * Copyright (C) 2004, 2007 Freescale Semiconductor, Inc. + * (C) 2004 Intracom, S.A. + * (C) 2005-2006 MontaVista Software, Inc. + * Vitaly Bordug + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_CPM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include + +#include "cpm_uart.h" + + +/**************************************************************/ + +static int cpm_uart_tx_pump(struct uart_port *port); +static void cpm_uart_init_smc(struct uart_cpm_port *pinfo); +static void cpm_uart_init_scc(struct uart_cpm_port *pinfo); +static void cpm_uart_initbd(struct uart_cpm_port *pinfo); + +/**************************************************************/ + +#define HW_BUF_SPD_THRESHOLD 9600 + +/* + * Check, if transmit buffers are processed +*/ +static unsigned int cpm_uart_tx_empty(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + cbd_t __iomem *bdp = pinfo->tx_bd_base; + int ret = 0; + + while (1) { + if (in_be16(&bdp->cbd_sc) & BD_SC_READY) + break; + + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) { + ret = TIOCSER_TEMT; + break; + } + bdp++; + } + + pr_debug("CPM uart[%d]:tx_empty: %d\n", port->line, ret); + + return ret; +} + +static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + if (pinfo->gpios[GPIO_RTS] >= 0) + gpio_set_value(pinfo->gpios[GPIO_RTS], !(mctrl & TIOCM_RTS)); + + if (pinfo->gpios[GPIO_DTR] >= 0) + gpio_set_value(pinfo->gpios[GPIO_DTR], !(mctrl & TIOCM_DTR)); +} + +static unsigned int cpm_uart_get_mctrl(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + unsigned int mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + if (pinfo->gpios[GPIO_CTS] >= 0) { + if (gpio_get_value(pinfo->gpios[GPIO_CTS])) + mctrl &= ~TIOCM_CTS; + } + + if (pinfo->gpios[GPIO_DSR] >= 0) { + if (gpio_get_value(pinfo->gpios[GPIO_DSR])) + mctrl &= ~TIOCM_DSR; + } + + if (pinfo->gpios[GPIO_DCD] >= 0) { + if (gpio_get_value(pinfo->gpios[GPIO_DCD])) + mctrl &= ~TIOCM_CAR; + } + + if (pinfo->gpios[GPIO_RI] >= 0) { + if (!gpio_get_value(pinfo->gpios[GPIO_RI])) + mctrl |= TIOCM_RNG; + } + + return mctrl; +} + +/* + * Stop transmitter + */ +static void cpm_uart_stop_tx(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + smc_t __iomem *smcp = pinfo->smcp; + scc_t __iomem *sccp = pinfo->sccp; + + pr_debug("CPM uart[%d]:stop tx\n", port->line); + + if (IS_SMC(pinfo)) + clrbits8(&smcp->smc_smcm, SMCM_TX); + else + clrbits16(&sccp->scc_sccm, UART_SCCM_TX); +} + +/* + * Start transmitter + */ +static void cpm_uart_start_tx(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + smc_t __iomem *smcp = pinfo->smcp; + scc_t __iomem *sccp = pinfo->sccp; + + pr_debug("CPM uart[%d]:start tx\n", port->line); + + if (IS_SMC(pinfo)) { + if (in_8(&smcp->smc_smcm) & SMCM_TX) + return; + } else { + if (in_be16(&sccp->scc_sccm) & UART_SCCM_TX) + return; + } + + if (cpm_uart_tx_pump(port) != 0) { + if (IS_SMC(pinfo)) { + setbits8(&smcp->smc_smcm, SMCM_TX); + } else { + setbits16(&sccp->scc_sccm, UART_SCCM_TX); + } + } +} + +/* + * Stop receiver + */ +static void cpm_uart_stop_rx(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + smc_t __iomem *smcp = pinfo->smcp; + scc_t __iomem *sccp = pinfo->sccp; + + pr_debug("CPM uart[%d]:stop rx\n", port->line); + + if (IS_SMC(pinfo)) + clrbits8(&smcp->smc_smcm, SMCM_RX); + else + clrbits16(&sccp->scc_sccm, UART_SCCM_RX); +} + +/* + * Enable Modem status interrupts + */ +static void cpm_uart_enable_ms(struct uart_port *port) +{ + pr_debug("CPM uart[%d]:enable ms\n", port->line); +} + +/* + * Generate a break. + */ +static void cpm_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + pr_debug("CPM uart[%d]:break ctrl, break_state: %d\n", port->line, + break_state); + + if (break_state) + cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); + else + cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX); +} + +/* + * Transmit characters, refill buffer descriptor, if possible + */ +static void cpm_uart_int_tx(struct uart_port *port) +{ + pr_debug("CPM uart[%d]:TX INT\n", port->line); + + cpm_uart_tx_pump(port); +} + +#ifdef CONFIG_CONSOLE_POLL +static int serial_polled; +#endif + +/* + * Receive characters + */ +static void cpm_uart_int_rx(struct uart_port *port) +{ + int i; + unsigned char ch; + u8 *cp; + struct tty_struct *tty = port->state->port.tty; + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + cbd_t __iomem *bdp; + u16 status; + unsigned int flg; + + pr_debug("CPM uart[%d]:RX INT\n", port->line); + + /* Just loop through the closed BDs and copy the characters into + * the buffer. + */ + bdp = pinfo->rx_cur; + for (;;) { +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return; + } +#endif + /* get status */ + status = in_be16(&bdp->cbd_sc); + /* If this one is empty, return happy */ + if (status & BD_SC_EMPTY) + break; + + /* get number of characters, and check spce in flip-buffer */ + i = in_be16(&bdp->cbd_datlen); + + /* If we have not enough room in tty flip buffer, then we try + * later, which will be the next rx-interrupt or a timeout + */ + if(tty_buffer_request_room(tty, i) < i) { + printk(KERN_WARNING "No room in flip buffer\n"); + return; + } + + /* get pointer */ + cp = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); + + /* loop through the buffer */ + while (i-- > 0) { + ch = *cp++; + port->icount.rx++; + flg = TTY_NORMAL; + + if (status & + (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV)) + goto handle_error; + if (uart_handle_sysrq_char(port, ch)) + continue; +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return; + } +#endif + error_return: + tty_insert_flip_char(tty, ch, flg); + + } /* End while (i--) */ + + /* This BD is ready to be used again. Clear status. get next */ + clrbits16(&bdp->cbd_sc, BD_SC_BR | BD_SC_FR | BD_SC_PR | + BD_SC_OV | BD_SC_ID); + setbits16(&bdp->cbd_sc, BD_SC_EMPTY); + + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = pinfo->rx_bd_base; + else + bdp++; + + } /* End for (;;) */ + + /* Write back buffer pointer */ + pinfo->rx_cur = bdp; + + /* activate BH processing */ + tty_flip_buffer_push(tty); + + return; + + /* Error processing */ + + handle_error: + /* Statistics */ + if (status & BD_SC_BR) + port->icount.brk++; + if (status & BD_SC_PR) + port->icount.parity++; + if (status & BD_SC_FR) + port->icount.frame++; + if (status & BD_SC_OV) + port->icount.overrun++; + + /* Mask out ignored conditions */ + status &= port->read_status_mask; + + /* Handle the remaining ones */ + if (status & BD_SC_BR) + flg = TTY_BREAK; + else if (status & BD_SC_PR) + flg = TTY_PARITY; + else if (status & BD_SC_FR) + flg = TTY_FRAME; + + /* overrun does not affect the current character ! */ + if (status & BD_SC_OV) { + ch = 0; + flg = TTY_OVERRUN; + /* We skip this buffer */ + /* CHECK: Is really nothing senseful there */ + /* ASSUMPTION: it contains nothing valid */ + i = 0; + } +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +/* + * Asynchron mode interrupt handler + */ +static irqreturn_t cpm_uart_int(int irq, void *data) +{ + u8 events; + struct uart_port *port = data; + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + smc_t __iomem *smcp = pinfo->smcp; + scc_t __iomem *sccp = pinfo->sccp; + + pr_debug("CPM uart[%d]:IRQ\n", port->line); + + if (IS_SMC(pinfo)) { + events = in_8(&smcp->smc_smce); + out_8(&smcp->smc_smce, events); + if (events & SMCM_BRKE) + uart_handle_break(port); + if (events & SMCM_RX) + cpm_uart_int_rx(port); + if (events & SMCM_TX) + cpm_uart_int_tx(port); + } else { + events = in_be16(&sccp->scc_scce); + out_be16(&sccp->scc_scce, events); + if (events & UART_SCCM_BRKE) + uart_handle_break(port); + if (events & UART_SCCM_RX) + cpm_uart_int_rx(port); + if (events & UART_SCCM_TX) + cpm_uart_int_tx(port); + } + return (events) ? IRQ_HANDLED : IRQ_NONE; +} + +static int cpm_uart_startup(struct uart_port *port) +{ + int retval; + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + pr_debug("CPM uart[%d]:startup\n", port->line); + + /* If the port is not the console, make sure rx is disabled. */ + if (!(pinfo->flags & FLAG_CONSOLE)) { + /* Disable UART rx */ + if (IS_SMC(pinfo)) { + clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN); + clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX); + } else { + clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR); + clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX); + } + cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); + } + /* Install interrupt handler. */ + retval = request_irq(port->irq, cpm_uart_int, 0, "cpm_uart", port); + if (retval) + return retval; + + /* Startup rx-int */ + if (IS_SMC(pinfo)) { + setbits8(&pinfo->smcp->smc_smcm, SMCM_RX); + setbits16(&pinfo->smcp->smc_smcmr, (SMCMR_REN | SMCMR_TEN)); + } else { + setbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX); + setbits32(&pinfo->sccp->scc_gsmrl, (SCC_GSMRL_ENR | SCC_GSMRL_ENT)); + } + + return 0; +} + +inline void cpm_uart_wait_until_send(struct uart_cpm_port *pinfo) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(pinfo->wait_closing); +} + +/* + * Shutdown the uart + */ +static void cpm_uart_shutdown(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + pr_debug("CPM uart[%d]:shutdown\n", port->line); + + /* free interrupt handler */ + free_irq(port->irq, port); + + /* If the port is not the console, disable Rx and Tx. */ + if (!(pinfo->flags & FLAG_CONSOLE)) { + /* Wait for all the BDs marked sent */ + while(!cpm_uart_tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(2); + } + + if (pinfo->wait_closing) + cpm_uart_wait_until_send(pinfo); + + /* Stop uarts */ + if (IS_SMC(pinfo)) { + smc_t __iomem *smcp = pinfo->smcp; + clrbits16(&smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); + clrbits8(&smcp->smc_smcm, SMCM_RX | SMCM_TX); + } else { + scc_t __iomem *sccp = pinfo->sccp; + clrbits32(&sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); + clrbits16(&sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); + } + + /* Shut them really down and reinit buffer descriptors */ + if (IS_SMC(pinfo)) { + out_be16(&pinfo->smcup->smc_brkcr, 0); + cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); + } else { + out_be16(&pinfo->sccup->scc_brkcr, 0); + cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX); + } + + cpm_uart_initbd(pinfo); + } +} + +static void cpm_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + int baud; + unsigned long flags; + u16 cval, scval, prev_mode; + int bits, sbits; + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + smc_t __iomem *smcp = pinfo->smcp; + scc_t __iomem *sccp = pinfo->sccp; + + pr_debug("CPM uart[%d]:set_termios\n", port->line); + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + if (baud <= HW_BUF_SPD_THRESHOLD || + (pinfo->port.state && pinfo->port.state->port.tty->low_latency)) + pinfo->rx_fifosize = 1; + else + pinfo->rx_fifosize = RX_BUF_SIZE; + + /* Character length programmed into the mode register is the + * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, + * 1 or 2 stop bits, minus 1. + * The value 'bits' counts this for us. + */ + cval = 0; + scval = 0; + + /* byte size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + bits = 5; + break; + case CS6: + bits = 6; + break; + case CS7: + bits = 7; + break; + case CS8: + bits = 8; + break; + /* Never happens, but GCC is too dumb to figure it out */ + default: + bits = 8; + break; + } + sbits = bits - 5; + + if (termios->c_cflag & CSTOPB) { + cval |= SMCMR_SL; /* Two stops */ + scval |= SCU_PSMR_SL; + bits++; + } + + if (termios->c_cflag & PARENB) { + cval |= SMCMR_PEN; + scval |= SCU_PSMR_PEN; + bits++; + if (!(termios->c_cflag & PARODD)) { + cval |= SMCMR_PM_EVEN; + scval |= (SCU_PSMR_REVP | SCU_PSMR_TEVP); + } + } + + /* + * Update the timeout + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Set up parity check flag + */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV); + if (termios->c_iflag & INPCK) + port->read_status_mask |= BD_SC_FR | BD_SC_PR; + if ((termios->c_iflag & BRKINT) || (termios->c_iflag & PARMRK)) + port->read_status_mask |= BD_SC_BR; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= BD_SC_PR | BD_SC_FR; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= BD_SC_BR; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= BD_SC_OV; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + port->read_status_mask &= ~BD_SC_EMPTY; + + spin_lock_irqsave(&port->lock, flags); + + /* Start bit has not been added (so don't, because we would just + * subtract it later), and we need to add one for the number of + * stops bits (there is always at least one). + */ + bits++; + if (IS_SMC(pinfo)) { + /* + * MRBLR can be changed while an SMC/SCC is operating only + * if it is done in a single bus cycle with one 16-bit move + * (not two 8-bit bus cycles back-to-back). This occurs when + * the cp shifts control to the next RxBD, so the change does + * not take effect immediately. To guarantee the exact RxBD + * on which the change occurs, change MRBLR only while the + * SMC/SCC receiver is disabled. + */ + out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize); + + /* Set the mode register. We want to keep a copy of the + * enables, because we want to put them back if they were + * present. + */ + prev_mode = in_be16(&smcp->smc_smcmr) & (SMCMR_REN | SMCMR_TEN); + /* Output in *one* operation, so we don't interrupt RX/TX if they + * were already enabled. */ + out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval | + SMCMR_SM_UART | prev_mode); + } else { + out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); + out_be16(&sccp->scc_psmr, (sbits << 12) | scval); + } + + if (pinfo->clk) + clk_set_rate(pinfo->clk, baud); + else + cpm_set_brg(pinfo->brg - 1, baud); + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *cpm_uart_type(struct uart_port *port) +{ + pr_debug("CPM uart[%d]:uart_type\n", port->line); + + return port->type == PORT_CPM ? "CPM UART" : NULL; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int cpm_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + + pr_debug("CPM uart[%d]:verify_port\n", port->line); + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= nr_irqs) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +/* + * Transmit characters, refill buffer descriptor, if possible + */ +static int cpm_uart_tx_pump(struct uart_port *port) +{ + cbd_t __iomem *bdp; + u8 *p; + int count; + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + struct circ_buf *xmit = &port->state->xmit; + + /* Handle xon/xoff */ + if (port->x_char) { + /* Pick next descriptor and fill from buffer */ + bdp = pinfo->tx_cur; + + p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); + + *p++ = port->x_char; + + out_be16(&bdp->cbd_datlen, 1); + setbits16(&bdp->cbd_sc, BD_SC_READY); + /* Get next BD. */ + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = pinfo->tx_bd_base; + else + bdp++; + pinfo->tx_cur = bdp; + + port->icount.tx++; + port->x_char = 0; + return 1; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + cpm_uart_stop_tx(port); + return 0; + } + + /* Pick next descriptor and fill from buffer */ + bdp = pinfo->tx_cur; + + while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) && + xmit->tail != xmit->head) { + count = 0; + p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); + while (count < pinfo->tx_fifosize) { + *p++ = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + count++; + if (xmit->head == xmit->tail) + break; + } + out_be16(&bdp->cbd_datlen, count); + setbits16(&bdp->cbd_sc, BD_SC_READY); + /* Get next BD. */ + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = pinfo->tx_bd_base; + else + bdp++; + } + pinfo->tx_cur = bdp; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) { + cpm_uart_stop_tx(port); + return 0; + } + + return 1; +} + +/* + * init buffer descriptors + */ +static void cpm_uart_initbd(struct uart_cpm_port *pinfo) +{ + int i; + u8 *mem_addr; + cbd_t __iomem *bdp; + + pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + mem_addr = pinfo->mem_addr; + bdp = pinfo->rx_cur = pinfo->rx_bd_base; + for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) { + out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); + out_be16(&bdp->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT); + mem_addr += pinfo->rx_fifosize; + } + + out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); + out_be16(&bdp->cbd_sc, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize); + bdp = pinfo->tx_cur = pinfo->tx_bd_base; + for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) { + out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); + out_be16(&bdp->cbd_sc, BD_SC_INTRPT); + mem_addr += pinfo->tx_fifosize; + } + + out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo)); + out_be16(&bdp->cbd_sc, BD_SC_WRAP | BD_SC_INTRPT); +} + +static void cpm_uart_init_scc(struct uart_cpm_port *pinfo) +{ + scc_t __iomem *scp; + scc_uart_t __iomem *sup; + + pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line); + + scp = pinfo->sccp; + sup = pinfo->sccup; + + /* Store address */ + out_be16(&pinfo->sccup->scc_genscc.scc_rbase, + (u8 __iomem *)pinfo->rx_bd_base - DPRAM_BASE); + out_be16(&pinfo->sccup->scc_genscc.scc_tbase, + (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE); + + /* Set up the uart parameters in the + * parameter ram. + */ + + cpm_set_scc_fcr(sup); + + out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); + out_be16(&sup->scc_maxidl, pinfo->rx_fifosize); + out_be16(&sup->scc_brkcr, 1); + out_be16(&sup->scc_parec, 0); + out_be16(&sup->scc_frmec, 0); + out_be16(&sup->scc_nosec, 0); + out_be16(&sup->scc_brkec, 0); + out_be16(&sup->scc_uaddr1, 0); + out_be16(&sup->scc_uaddr2, 0); + out_be16(&sup->scc_toseq, 0); + out_be16(&sup->scc_char1, 0x8000); + out_be16(&sup->scc_char2, 0x8000); + out_be16(&sup->scc_char3, 0x8000); + out_be16(&sup->scc_char4, 0x8000); + out_be16(&sup->scc_char5, 0x8000); + out_be16(&sup->scc_char6, 0x8000); + out_be16(&sup->scc_char7, 0x8000); + out_be16(&sup->scc_char8, 0x8000); + out_be16(&sup->scc_rccm, 0xc0ff); + + /* Send the CPM an initialize command. + */ + cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + out_be32(&scp->scc_gsmrh, 0); + out_be32(&scp->scc_gsmrl, + SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + + /* Enable rx interrupts and clear all pending events. */ + out_be16(&scp->scc_sccm, 0); + out_be16(&scp->scc_scce, 0xffff); + out_be16(&scp->scc_dsr, 0x7e7e); + out_be16(&scp->scc_psmr, 0x3000); + + setbits32(&scp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); +} + +static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) +{ + smc_t __iomem *sp; + smc_uart_t __iomem *up; + + pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line); + + sp = pinfo->smcp; + up = pinfo->smcup; + + /* Store address */ + out_be16(&pinfo->smcup->smc_rbase, + (u8 __iomem *)pinfo->rx_bd_base - DPRAM_BASE); + out_be16(&pinfo->smcup->smc_tbase, + (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE); + +/* + * In case SMC1 is being relocated... + */ +#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH) + out_be16(&up->smc_rbptr, in_be16(&pinfo->smcup->smc_rbase)); + out_be16(&up->smc_tbptr, in_be16(&pinfo->smcup->smc_tbase)); + out_be32(&up->smc_rstate, 0); + out_be32(&up->smc_tstate, 0); + out_be16(&up->smc_brkcr, 1); /* number of break chars */ + out_be16(&up->smc_brkec, 0); +#endif + + /* Set up the uart parameters in the + * parameter ram. + */ + cpm_set_smc_fcr(up); + + /* Using idle character time requires some additional tuning. */ + out_be16(&up->smc_mrblr, pinfo->rx_fifosize); + out_be16(&up->smc_maxidl, pinfo->rx_fifosize); + out_be16(&up->smc_brklen, 0); + out_be16(&up->smc_brkec, 0); + out_be16(&up->smc_brkcr, 1); + + cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX); + + /* Set UART mode, 8 bit, no parity, one stop. + * Enable receive and transmit. + */ + out_be16(&sp->smc_smcmr, smcr_mk_clen(9) | SMCMR_SM_UART); + + /* Enable only rx interrupts clear all pending events. */ + out_8(&sp->smc_smcm, 0); + out_8(&sp->smc_smce, 0xff); + + setbits16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN); +} + +/* + * Initialize port. This is called from early_console stuff + * so we have to be careful here ! + */ +static int cpm_uart_request_port(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + int ret; + + pr_debug("CPM uart[%d]:request port\n", port->line); + + if (pinfo->flags & FLAG_CONSOLE) + return 0; + + if (IS_SMC(pinfo)) { + clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX); + clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); + } else { + clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); + clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); + } + + ret = cpm_uart_allocbuf(pinfo, 0); + + if (ret) + return ret; + + cpm_uart_initbd(pinfo); + if (IS_SMC(pinfo)) + cpm_uart_init_smc(pinfo); + else + cpm_uart_init_scc(pinfo); + + return 0; +} + +static void cpm_uart_release_port(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + if (!(pinfo->flags & FLAG_CONSOLE)) + cpm_uart_freebuf(pinfo); +} + +/* + * Configure/autoconfigure the port. + */ +static void cpm_uart_config_port(struct uart_port *port, int flags) +{ + pr_debug("CPM uart[%d]:config_port\n", port->line); + + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_CPM; + cpm_uart_request_port(port); + } +} + +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_CPM_CONSOLE) +/* + * Write a string to the serial port + * Note that this is called with interrupts already disabled + */ +static void cpm_uart_early_write(struct uart_cpm_port *pinfo, + const char *string, u_int count) +{ + unsigned int i; + cbd_t __iomem *bdp, *bdbase; + unsigned char *cpm_outp_addr; + + /* Get the address of the host memory buffer. + */ + bdp = pinfo->tx_cur; + bdbase = pinfo->tx_bd_base; + + /* + * Now, do each character. This is not as bad as it looks + * since this is a holding FIFO and not a transmitting FIFO. + * We could add the complexity of filling the entire transmit + * buffer, but we would just wait longer between accesses...... + */ + for (i = 0; i < count; i++, string++) { + /* Wait for transmitter fifo to empty. + * Ready indicates output is ready, and xmt is doing + * that, not that it is ready for us to send. + */ + while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) + ; + + /* Send the character out. + * If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), + pinfo); + *cpm_outp_addr = *string; + + out_be16(&bdp->cbd_datlen, 1); + setbits16(&bdp->cbd_sc, BD_SC_READY); + + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + + /* if a LF, also do CR... */ + if (*string == 10) { + while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) + ; + + cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), + pinfo); + *cpm_outp_addr = 13; + + out_be16(&bdp->cbd_datlen, 1); + setbits16(&bdp->cbd_sc, BD_SC_READY); + + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) + ; + + pinfo->tx_cur = bdp; +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +/* Serial polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +#define GDB_BUF_SIZE 512 /* power of 2, please */ + +static char poll_buf[GDB_BUF_SIZE]; +static char *pollp; +static int poll_chars; + +static int poll_wait_key(char *obuf, struct uart_cpm_port *pinfo) +{ + u_char c, *cp; + volatile cbd_t *bdp; + int i; + + /* Get the address of the host memory buffer. + */ + bdp = pinfo->rx_cur; + while (bdp->cbd_sc & BD_SC_EMPTY) + ; + + /* If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); + + if (obuf) { + i = c = bdp->cbd_datlen; + while (i-- > 0) + *obuf++ = *cp++; + } else + c = *cp; + bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID); + bdp->cbd_sc |= BD_SC_EMPTY; + + if (bdp->cbd_sc & BD_SC_WRAP) + bdp = pinfo->rx_bd_base; + else + bdp++; + pinfo->rx_cur = (cbd_t *)bdp; + + return (int)c; +} + +static int cpm_get_poll_char(struct uart_port *port) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + + if (!serial_polled) { + serial_polled = 1; + poll_chars = 0; + } + if (poll_chars <= 0) { + poll_chars = poll_wait_key(poll_buf, pinfo); + pollp = poll_buf; + } + poll_chars--; + return *pollp++; +} + +static void cpm_put_poll_char(struct uart_port *port, + unsigned char c) +{ + struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; + static char ch[2]; + + ch[0] = (char)c; + cpm_uart_early_write(pinfo, ch, 1); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static struct uart_ops cpm_uart_pops = { + .tx_empty = cpm_uart_tx_empty, + .set_mctrl = cpm_uart_set_mctrl, + .get_mctrl = cpm_uart_get_mctrl, + .stop_tx = cpm_uart_stop_tx, + .start_tx = cpm_uart_start_tx, + .stop_rx = cpm_uart_stop_rx, + .enable_ms = cpm_uart_enable_ms, + .break_ctl = cpm_uart_break_ctl, + .startup = cpm_uart_startup, + .shutdown = cpm_uart_shutdown, + .set_termios = cpm_uart_set_termios, + .type = cpm_uart_type, + .release_port = cpm_uart_release_port, + .request_port = cpm_uart_request_port, + .config_port = cpm_uart_config_port, + .verify_port = cpm_uart_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = cpm_get_poll_char, + .poll_put_char = cpm_put_poll_char, +#endif +}; + +struct uart_cpm_port cpm_uart_ports[UART_NR]; + +static int cpm_uart_init_port(struct device_node *np, + struct uart_cpm_port *pinfo) +{ + const u32 *data; + void __iomem *mem, *pram; + int len; + int ret; + int i; + + data = of_get_property(np, "clock", NULL); + if (data) { + struct clk *clk = clk_get(NULL, (const char*)data); + if (!IS_ERR(clk)) + pinfo->clk = clk; + } + if (!pinfo->clk) { + data = of_get_property(np, "fsl,cpm-brg", &len); + if (!data || len != 4) { + printk(KERN_ERR "CPM UART %s has no/invalid " + "fsl,cpm-brg property.\n", np->name); + return -EINVAL; + } + pinfo->brg = *data; + } + + data = of_get_property(np, "fsl,cpm-command", &len); + if (!data || len != 4) { + printk(KERN_ERR "CPM UART %s has no/invalid " + "fsl,cpm-command property.\n", np->name); + return -EINVAL; + } + pinfo->command = *data; + + mem = of_iomap(np, 0); + if (!mem) + return -ENOMEM; + + if (of_device_is_compatible(np, "fsl,cpm1-scc-uart") || + of_device_is_compatible(np, "fsl,cpm2-scc-uart")) { + pinfo->sccp = mem; + pinfo->sccup = pram = cpm_uart_map_pram(pinfo, np); + } else if (of_device_is_compatible(np, "fsl,cpm1-smc-uart") || + of_device_is_compatible(np, "fsl,cpm2-smc-uart")) { + pinfo->flags |= FLAG_SMC; + pinfo->smcp = mem; + pinfo->smcup = pram = cpm_uart_map_pram(pinfo, np); + } else { + ret = -ENODEV; + goto out_mem; + } + + if (!pram) { + ret = -ENOMEM; + goto out_mem; + } + + pinfo->tx_nrfifos = TX_NUM_FIFO; + pinfo->tx_fifosize = TX_BUF_SIZE; + pinfo->rx_nrfifos = RX_NUM_FIFO; + pinfo->rx_fifosize = RX_BUF_SIZE; + + pinfo->port.uartclk = ppc_proc_freq; + pinfo->port.mapbase = (unsigned long)mem; + pinfo->port.type = PORT_CPM; + pinfo->port.ops = &cpm_uart_pops, + pinfo->port.iotype = UPIO_MEM; + pinfo->port.fifosize = pinfo->tx_nrfifos * pinfo->tx_fifosize; + spin_lock_init(&pinfo->port.lock); + + pinfo->port.irq = of_irq_to_resource(np, 0, NULL); + if (pinfo->port.irq == NO_IRQ) { + ret = -EINVAL; + goto out_pram; + } + + for (i = 0; i < NUM_GPIOS; i++) + pinfo->gpios[i] = of_get_gpio(np, i); + +#ifdef CONFIG_PPC_EARLY_DEBUG_CPM + udbg_putc = NULL; +#endif + + return cpm_uart_request_port(&pinfo->port); + +out_pram: + cpm_uart_unmap_pram(pinfo, pram); +out_mem: + iounmap(mem); + return ret; +} + +#ifdef CONFIG_SERIAL_CPM_CONSOLE +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * Note that this is called with interrupts already disabled + */ +static void cpm_uart_console_write(struct console *co, const char *s, + u_int count) +{ + struct uart_cpm_port *pinfo = &cpm_uart_ports[co->index]; + unsigned long flags; + int nolock = oops_in_progress; + + if (unlikely(nolock)) { + local_irq_save(flags); + } else { + spin_lock_irqsave(&pinfo->port.lock, flags); + } + + cpm_uart_early_write(pinfo, s, count); + + if (unlikely(nolock)) { + local_irq_restore(flags); + } else { + spin_unlock_irqrestore(&pinfo->port.lock, flags); + } +} + + +static int __init cpm_uart_console_setup(struct console *co, char *options) +{ + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + struct uart_cpm_port *pinfo; + struct uart_port *port; + + struct device_node *np = NULL; + int i = 0; + + if (co->index >= UART_NR) { + printk(KERN_ERR "cpm_uart: console index %d too high\n", + co->index); + return -ENODEV; + } + + do { + np = of_find_node_by_type(np, "serial"); + if (!np) + return -ENODEV; + + if (!of_device_is_compatible(np, "fsl,cpm1-smc-uart") && + !of_device_is_compatible(np, "fsl,cpm1-scc-uart") && + !of_device_is_compatible(np, "fsl,cpm2-smc-uart") && + !of_device_is_compatible(np, "fsl,cpm2-scc-uart")) + i--; + } while (i++ != co->index); + + pinfo = &cpm_uart_ports[co->index]; + + pinfo->flags |= FLAG_CONSOLE; + port = &pinfo->port; + + ret = cpm_uart_init_port(np, pinfo); + of_node_put(np); + if (ret) + return ret; + + if (options) { + uart_parse_options(options, &baud, &parity, &bits, &flow); + } else { + if ((baud = uart_baudrate()) == -1) + baud = 9600; + } + + if (IS_SMC(pinfo)) { + out_be16(&pinfo->smcup->smc_brkcr, 0); + cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX); + clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX); + clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN); + } else { + out_be16(&pinfo->sccup->scc_brkcr, 0); + cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX); + clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX); + clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT); + } + + ret = cpm_uart_allocbuf(pinfo, 1); + + if (ret) + return ret; + + cpm_uart_initbd(pinfo); + + if (IS_SMC(pinfo)) + cpm_uart_init_smc(pinfo); + else + cpm_uart_init_scc(pinfo); + + uart_set_options(port, co, baud, parity, bits, flow); + cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX); + + return 0; +} + +static struct uart_driver cpm_reg; +static struct console cpm_scc_uart_console = { + .name = "ttyCPM", + .write = cpm_uart_console_write, + .device = uart_console_device, + .setup = cpm_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &cpm_reg, +}; + +static int __init cpm_uart_console_init(void) +{ + register_console(&cpm_scc_uart_console); + return 0; +} + +console_initcall(cpm_uart_console_init); + +#define CPM_UART_CONSOLE &cpm_scc_uart_console +#else +#define CPM_UART_CONSOLE NULL +#endif + +static struct uart_driver cpm_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyCPM", + .dev_name = "ttyCPM", + .major = SERIAL_CPM_MAJOR, + .minor = SERIAL_CPM_MINOR, + .cons = CPM_UART_CONSOLE, + .nr = UART_NR, +}; + +static int probe_index; + +static int __devinit cpm_uart_probe(struct platform_device *ofdev, + const struct of_device_id *match) +{ + int index = probe_index++; + struct uart_cpm_port *pinfo = &cpm_uart_ports[index]; + int ret; + + pinfo->port.line = index; + + if (index >= UART_NR) + return -ENODEV; + + dev_set_drvdata(&ofdev->dev, pinfo); + + /* initialize the device pointer for the port */ + pinfo->port.dev = &ofdev->dev; + + ret = cpm_uart_init_port(ofdev->dev.of_node, pinfo); + if (ret) + return ret; + + return uart_add_one_port(&cpm_reg, &pinfo->port); +} + +static int __devexit 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); +} + +static struct of_device_id cpm_uart_match[] = { + { + .compatible = "fsl,cpm1-smc-uart", + }, + { + .compatible = "fsl,cpm1-scc-uart", + }, + { + .compatible = "fsl,cpm2-smc-uart", + }, + { + .compatible = "fsl,cpm2-scc-uart", + }, + {} +}; + +static struct of_platform_driver cpm_uart_driver = { + .driver = { + .name = "cpm_uart", + .owner = THIS_MODULE, + .of_match_table = cpm_uart_match, + }, + .probe = cpm_uart_probe, + .remove = cpm_uart_remove, + }; + +static int __init cpm_uart_init(void) +{ + int ret = uart_register_driver(&cpm_reg); + if (ret) + return ret; + + ret = of_register_platform_driver(&cpm_uart_driver); + if (ret) + uart_unregister_driver(&cpm_reg); + + return ret; +} + +static void __exit cpm_uart_exit(void) +{ + of_unregister_platform_driver(&cpm_uart_driver); + uart_unregister_driver(&cpm_reg); +} + +module_init(cpm_uart_init); +module_exit(cpm_uart_exit); + +MODULE_AUTHOR("Kumar Gala/Antoniou Pantelis"); +MODULE_DESCRIPTION("CPM SCC/SMC port driver $Revision: 0.01 $"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV(SERIAL_CPM_MAJOR, SERIAL_CPM_MINOR); diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c new file mode 100644 index 000000000000..3fc1d66e32c6 --- /dev/null +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c @@ -0,0 +1,138 @@ +/* + * linux/drivers/serial/cpm_uart.c + * + * Driver for CPM (SCC/SMC) serial ports; CPM1 definitions + * + * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) + * Pantelis Antoniou (panto@intracom.gr) (CPM1) + * + * Copyright (C) 2004 Freescale Semiconductor, Inc. + * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug + * + * 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 +#include + +#include +#include +#include + +#include +#include + +#include + +#include "cpm_uart.h" + +/**************************************************************/ + +void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd) +{ + cpm_command(port->command, cmd); +} + +void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, + struct device_node *np) +{ + return of_iomap(np, 1); +} + +void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram) +{ + iounmap(pram); +} + +/* + * Allocate DP-Ram and memory buffers. We need to allocate a transmit and + * receive buffer descriptors from dual port ram, and a character + * buffer area from host mem. If we are allocating for the console we need + * to do it from bootmem + */ +int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) +{ + int dpmemsz, memsz; + u8 *dp_mem; + unsigned long dp_offset; + u8 *mem_addr; + dma_addr_t dma_addr = 0; + + pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line); + + dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); + dp_offset = cpm_dpalloc(dpmemsz, 8); + if (IS_ERR_VALUE(dp_offset)) { + printk(KERN_ERR + "cpm_uart_cpm1.c: could not allocate buffer descriptors\n"); + return -ENOMEM; + } + dp_mem = cpm_dpram_addr(dp_offset); + + memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + + L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); + if (is_con) { + /* was hostalloc but changed cause it blows away the */ + /* large tlb mapping when pinning the kernel area */ + mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8)); + dma_addr = (u32)cpm_dpram_phys(mem_addr); + } else + mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr, + GFP_KERNEL); + + if (mem_addr == NULL) { + cpm_dpfree(dp_offset); + printk(KERN_ERR + "cpm_uart_cpm1.c: could not allocate coherent memory\n"); + return -ENOMEM; + } + + pinfo->dp_addr = dp_offset; + pinfo->mem_addr = mem_addr; /* virtual address*/ + pinfo->dma_addr = dma_addr; /* physical address*/ + pinfo->mem_size = memsz; + + pinfo->rx_buf = mem_addr; + pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos + * pinfo->rx_fifosize); + + pinfo->rx_bd_base = (cbd_t __iomem __force *)dp_mem; + pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos; + + return 0; +} + +void cpm_uart_freebuf(struct uart_cpm_port *pinfo) +{ + dma_free_coherent(pinfo->port.dev, L1_CACHE_ALIGN(pinfo->rx_nrfifos * + pinfo->rx_fifosize) + + L1_CACHE_ALIGN(pinfo->tx_nrfifos * + pinfo->tx_fifosize), pinfo->mem_addr, + pinfo->dma_addr); + + cpm_dpfree(pinfo->dp_addr); +} diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h new file mode 100644 index 000000000000..10eecd6af6d4 --- /dev/null +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h @@ -0,0 +1,34 @@ +/* + * linux/drivers/serial/cpm_uart/cpm_uart_cpm1.h + * + * Driver for CPM (SCC/SMC) serial ports + * + * definitions for cpm1 + * + */ + +#ifndef CPM_UART_CPM1_H +#define CPM_UART_CPM1_H + +#include + +static inline void cpm_set_brg(int brg, int baud) +{ + cpm_setbrg(brg, baud); +} + +static inline void cpm_set_scc_fcr(scc_uart_t __iomem * sup) +{ + out_8(&sup->scc_genscc.scc_rfcr, SMC_EB); + out_8(&sup->scc_genscc.scc_tfcr, SMC_EB); +} + +static inline void cpm_set_smc_fcr(smc_uart_t __iomem * up) +{ + out_8(&up->smc_rfcr, SMC_EB); + out_8(&up->smc_tfcr, SMC_EB); +} + +#define DPRAM_BASE ((u8 __iomem __force *)cpm_dpram_addr(0)) + +#endif diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c new file mode 100644 index 000000000000..814ac006393f --- /dev/null +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c @@ -0,0 +1,174 @@ +/* + * linux/drivers/serial/cpm_uart_cpm2.c + * + * Driver for CPM (SCC/SMC) serial ports; CPM2 definitions + * + * Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2) + * Pantelis Antoniou (panto@intracom.gr) (CPM1) + * + * Copyright (C) 2004 Freescale Semiconductor, Inc. + * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug + * + * 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 +#include + +#include +#include +#include +#include + +#include +#include + +#include "cpm_uart.h" + +/**************************************************************/ + +void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd) +{ + cpm_command(port->command, cmd); +} + +void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, + struct device_node *np) +{ + void __iomem *pram; + unsigned long offset; + struct resource res; + resource_size_t len; + + /* Don't remap parameter RAM if it has already been initialized + * during console setup. + */ + if (IS_SMC(port) && port->smcup) + return port->smcup; + else if (!IS_SMC(port) && port->sccup) + return port->sccup; + + if (of_address_to_resource(np, 1, &res)) + return NULL; + + len = resource_size(&res); + pram = ioremap(res.start, len); + if (!pram) + return NULL; + + if (!IS_SMC(port)) + return pram; + + if (len != 2) { + printk(KERN_WARNING "cpm_uart[%d]: device tree references " + "SMC pram, using boot loader/wrapper pram mapping. " + "Please fix your device tree to reference the pram " + "base register instead.\n", + port->port.line); + return pram; + } + + offset = cpm_dpalloc(PROFF_SMC_SIZE, 64); + out_be16(pram, offset); + iounmap(pram); + return cpm_muram_addr(offset); +} + +void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram) +{ + if (!IS_SMC(port)) + iounmap(pram); +} + +/* + * Allocate DP-Ram and memory buffers. We need to allocate a transmit and + * receive buffer descriptors from dual port ram, and a character + * buffer area from host mem. If we are allocating for the console we need + * to do it from bootmem + */ +int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) +{ + int dpmemsz, memsz; + u8 __iomem *dp_mem; + unsigned long dp_offset; + u8 *mem_addr; + dma_addr_t dma_addr = 0; + + pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line); + + dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos); + dp_offset = cpm_dpalloc(dpmemsz, 8); + if (IS_ERR_VALUE(dp_offset)) { + printk(KERN_ERR + "cpm_uart_cpm.c: could not allocate buffer descriptors\n"); + return -ENOMEM; + } + + dp_mem = cpm_dpram_addr(dp_offset); + + memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + + L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); + if (is_con) { + mem_addr = kzalloc(memsz, GFP_NOWAIT); + dma_addr = virt_to_bus(mem_addr); + } + else + mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr, + GFP_KERNEL); + + if (mem_addr == NULL) { + cpm_dpfree(dp_offset); + printk(KERN_ERR + "cpm_uart_cpm.c: could not allocate coherent memory\n"); + return -ENOMEM; + } + + pinfo->dp_addr = dp_offset; + pinfo->mem_addr = mem_addr; + pinfo->dma_addr = dma_addr; + pinfo->mem_size = memsz; + + pinfo->rx_buf = mem_addr; + pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos + * pinfo->rx_fifosize); + + pinfo->rx_bd_base = (cbd_t __iomem *)dp_mem; + pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos; + + return 0; +} + +void cpm_uart_freebuf(struct uart_cpm_port *pinfo) +{ + dma_free_coherent(pinfo->port.dev, L1_CACHE_ALIGN(pinfo->rx_nrfifos * + pinfo->rx_fifosize) + + L1_CACHE_ALIGN(pinfo->tx_nrfifos * + pinfo->tx_fifosize), (void __force *)pinfo->mem_addr, + pinfo->dma_addr); + + cpm_dpfree(pinfo->dp_addr); +} diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h new file mode 100644 index 000000000000..7194c63dcf5f --- /dev/null +++ b/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h @@ -0,0 +1,34 @@ +/* + * linux/drivers/serial/cpm_uart/cpm_uart_cpm2.h + * + * Driver for CPM (SCC/SMC) serial ports + * + * definitions for cpm2 + * + */ + +#ifndef CPM_UART_CPM2_H +#define CPM_UART_CPM2_H + +#include + +static inline void cpm_set_brg(int brg, int baud) +{ + cpm_setbrg(brg, baud); +} + +static inline void cpm_set_scc_fcr(scc_uart_t __iomem *sup) +{ + out_8(&sup->scc_genscc.scc_rfcr, CPMFCR_GBL | CPMFCR_EB); + out_8(&sup->scc_genscc.scc_tfcr, CPMFCR_GBL | CPMFCR_EB); +} + +static inline void cpm_set_smc_fcr(smc_uart_t __iomem *up) +{ + out_8(&up->smc_rfcr, CPMFCR_GBL | CPMFCR_EB); + out_8(&up->smc_tfcr, CPMFCR_GBL | CPMFCR_EB); +} + +#define DPRAM_BASE ((u8 __iomem __force *)cpm_dpram_addr(0)) + +#endif diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c new file mode 100644 index 000000000000..bcc31f2140ac --- /dev/null +++ b/drivers/tty/serial/crisv10.c @@ -0,0 +1,4573 @@ +/* + * Serial port driver for the ETRAX 100LX chip + * + * Copyright (C) 1998-2007 Axis Communications AB + * + * Many, many authors. Based once upon a time on serial.c for 16x50. + * + */ + +static char *serial_version = "$Revision: 1.25 $"; + +#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 + +#include + +/* non-arch dependent serial structures are in linux/serial.h */ +#include +/* while we keep our own stuff (struct e100_serial) in a local .h file */ +#include "crisv10.h" +#include +#include + +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +#ifndef CONFIG_ETRAX_FAST_TIMER +#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER" +#endif +#endif + +#if defined(CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS) && \ + (CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS == 0) +#error "RX_TIMEOUT_TICKS == 0 not allowed, use 1" +#endif + +#if defined(CONFIG_ETRAX_RS485_ON_PA) && defined(CONFIG_ETRAX_RS485_ON_PORT_G) +#error "Disable either CONFIG_ETRAX_RS485_ON_PA or CONFIG_ETRAX_RS485_ON_PORT_G" +#endif + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) +#include "serial_compat.h" +#endif + +struct tty_driver *serial_driver; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +//#define SERIAL_DEBUG_INTR +//#define SERIAL_DEBUG_OPEN +//#define SERIAL_DEBUG_FLOW +//#define SERIAL_DEBUG_DATA +//#define SERIAL_DEBUG_THROTTLE +//#define SERIAL_DEBUG_IO /* Debug for Extra control and status pins */ +//#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */ + +/* Enable this to use serial interrupts to handle when you + expect the first received event on the serial port to + be an error, break or similar. Used to be able to flash IRMA + from eLinux */ +#define SERIAL_HANDLE_EARLY_ERRORS + +/* Currently 16 descriptors x 128 bytes = 2048 bytes */ +#define SERIAL_DESCR_BUF_SIZE 256 + +#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */ +#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE + +/* We don't want to load the system with massive fast timer interrupt + * on high baudrates so limit it to 250 us (4kHz) */ +#define MIN_FLUSH_TIME_USEC 250 + +/* Add an x here to log a lot of timer stuff */ +#define TIMERD(x) +/* Debug details of interrupt handling */ +#define DINTR1(x) /* irq on/off, errors */ +#define DINTR2(x) /* tx and rx */ +/* Debug flip buffer stuff */ +#define DFLIP(x) +/* Debug flow control and overview of data flow */ +#define DFLOW(x) +#define DBAUD(x) +#define DLOG_INT_TRIG(x) + +//#define DEBUG_LOG_INCLUDED +#ifndef DEBUG_LOG_INCLUDED +#define DEBUG_LOG(line, string, value) +#else +struct debug_log_info +{ + unsigned long time; + unsigned long timer_data; +// int line; + const char *string; + int value; +}; +#define DEBUG_LOG_SIZE 4096 + +struct debug_log_info debug_log[DEBUG_LOG_SIZE]; +int debug_log_pos = 0; + +#define DEBUG_LOG(_line, _string, _value) do { \ + if ((_line) == SERIAL_DEBUG_LINE) {\ + debug_log_func(_line, _string, _value); \ + }\ +}while(0) + +void debug_log_func(int line, const char *string, int value) +{ + if (debug_log_pos < DEBUG_LOG_SIZE) { + debug_log[debug_log_pos].time = jiffies; + debug_log[debug_log_pos].timer_data = *R_TIMER_DATA; +// debug_log[debug_log_pos].line = line; + debug_log[debug_log_pos].string = string; + debug_log[debug_log_pos].value = value; + debug_log_pos++; + } + /*printk(string, value);*/ +} +#endif + +#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS +/* Default number of timer ticks before flushing rx fifo + * When using "little data, low latency applications: use 0 + * When using "much data applications (PPP)" use ~5 + */ +#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 +#endif + +unsigned long timer_data_to_ns(unsigned long timer_data); + +static void change_speed(struct e100_serial *info); +static void rs_throttle(struct tty_struct * tty); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); +static int rs_write(struct tty_struct *tty, + const unsigned char *buf, int count); +#ifdef CONFIG_ETRAX_RS485 +static int e100_write_rs485(struct tty_struct *tty, + const unsigned char *buf, int count); +#endif +static int get_lsr_info(struct e100_serial *info, unsigned int *value); + + +#define DEF_BAUD 115200 /* 115.2 kbit/s */ +#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ +/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ +#define DEF_TX 0x80 /* or SERIAL_CTRL_B */ + +/* offsets from R_SERIALx_CTRL */ + +#define REG_DATA 0 +#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */ +#define REG_TR_DATA 0 +#define REG_STATUS 1 +#define REG_TR_CTRL 1 +#define REG_REC_CTRL 2 +#define REG_BAUD 3 +#define REG_XOFF 4 /* this is a 32 bit register */ + +/* The bitfields are the same for all serial ports */ +#define SER_RXD_MASK IO_MASK(R_SERIAL0_STATUS, rxd) +#define SER_DATA_AVAIL_MASK IO_MASK(R_SERIAL0_STATUS, data_avail) +#define SER_FRAMING_ERR_MASK IO_MASK(R_SERIAL0_STATUS, framing_err) +#define SER_PAR_ERR_MASK IO_MASK(R_SERIAL0_STATUS, par_err) +#define SER_OVERRUN_MASK IO_MASK(R_SERIAL0_STATUS, overrun) + +#define SER_ERROR_MASK (SER_OVERRUN_MASK | SER_PAR_ERR_MASK | SER_FRAMING_ERR_MASK) + +/* Values for info->errorcode */ +#define ERRCODE_SET_BREAK (TTY_BREAK) +#define ERRCODE_INSERT 0x100 +#define ERRCODE_INSERT_BREAK (ERRCODE_INSERT | TTY_BREAK) + +#define FORCE_EOP(info) *R_SET_EOP = 1U << info->iseteop; + +/* + * General note regarding the use of IO_* macros in this file: + * + * We will use the bits defined for DMA channel 6 when using various + * IO_* macros (e.g. IO_STATE, IO_MASK, IO_EXTRACT) and _assume_ they are + * the same for all channels (which of course they are). + * + * We will also use the bits defined for serial port 0 when writing commands + * to the different ports, as these bits too are the same for all ports. + */ + + +/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */ +static const unsigned long e100_ser_int_mask = 0 +#ifdef CONFIG_ETRAX_SERIAL_PORT0 +| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1 +| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2 +| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready) +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3 +| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready) +#endif +; +unsigned long r_alt_ser_baudrate_shadow = 0; + +/* this is the data for the four serial ports in the etrax100 */ +/* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */ +/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */ + +static struct e100_serial rs_table[] = { + { .baud = DEF_BAUD, + .ioport = (unsigned char *)R_SERIAL0_CTRL, + .irq = 1U << 12, /* uses DMA 6 and 7 */ + .oclrintradr = R_DMA_CH6_CLR_INTR, + .ofirstadr = R_DMA_CH6_FIRST, + .ocmdadr = R_DMA_CH6_CMD, + .ostatusadr = R_DMA_CH6_STATUS, + .iclrintradr = R_DMA_CH7_CLR_INTR, + .ifirstadr = R_DMA_CH7_FIRST, + .icmdadr = R_DMA_CH7_CMD, + .idescradr = R_DMA_CH7_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 2, + .dma_owner = dma_ser0, + .io_if = if_serial_0, +#ifdef CONFIG_ETRAX_SERIAL_PORT0 + .enabled = 1, +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT + .dma_out_enabled = 1, + .dma_out_nbr = SER0_TX_DMA_NBR, + .dma_out_irq_nbr = SER0_DMA_TX_IRQ_NBR, + .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_description = "serial 0 dma tr", +#else + .dma_out_enabled = 0, + .dma_out_nbr = UINT_MAX, + .dma_out_irq_nbr = 0, + .dma_out_irq_flags = 0, + .dma_out_irq_description = NULL, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN + .dma_in_enabled = 1, + .dma_in_nbr = SER0_RX_DMA_NBR, + .dma_in_irq_nbr = SER0_DMA_RX_IRQ_NBR, + .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_description = "serial 0 dma rec", +#else + .dma_in_enabled = 0, + .dma_in_nbr = UINT_MAX, + .dma_in_irq_nbr = 0, + .dma_in_irq_flags = 0, + .dma_in_irq_description = NULL, +#endif +#else + .enabled = 0, + .io_if_description = NULL, + .dma_out_enabled = 0, + .dma_in_enabled = 0 +#endif + +}, /* ttyS0 */ +#ifndef CONFIG_SVINTO_SIM + { .baud = DEF_BAUD, + .ioport = (unsigned char *)R_SERIAL1_CTRL, + .irq = 1U << 16, /* uses DMA 8 and 9 */ + .oclrintradr = R_DMA_CH8_CLR_INTR, + .ofirstadr = R_DMA_CH8_FIRST, + .ocmdadr = R_DMA_CH8_CMD, + .ostatusadr = R_DMA_CH8_STATUS, + .iclrintradr = R_DMA_CH9_CLR_INTR, + .ifirstadr = R_DMA_CH9_FIRST, + .icmdadr = R_DMA_CH9_CMD, + .idescradr = R_DMA_CH9_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 3, + .dma_owner = dma_ser1, + .io_if = if_serial_1, +#ifdef CONFIG_ETRAX_SERIAL_PORT1 + .enabled = 1, + .io_if_description = "ser1", +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT + .dma_out_enabled = 1, + .dma_out_nbr = SER1_TX_DMA_NBR, + .dma_out_irq_nbr = SER1_DMA_TX_IRQ_NBR, + .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_description = "serial 1 dma tr", +#else + .dma_out_enabled = 0, + .dma_out_nbr = UINT_MAX, + .dma_out_irq_nbr = 0, + .dma_out_irq_flags = 0, + .dma_out_irq_description = NULL, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN + .dma_in_enabled = 1, + .dma_in_nbr = SER1_RX_DMA_NBR, + .dma_in_irq_nbr = SER1_DMA_RX_IRQ_NBR, + .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_description = "serial 1 dma rec", +#else + .dma_in_enabled = 0, + .dma_in_enabled = 0, + .dma_in_nbr = UINT_MAX, + .dma_in_irq_nbr = 0, + .dma_in_irq_flags = 0, + .dma_in_irq_description = NULL, +#endif +#else + .enabled = 0, + .io_if_description = NULL, + .dma_in_irq_nbr = 0, + .dma_out_enabled = 0, + .dma_in_enabled = 0 +#endif +}, /* ttyS1 */ + + { .baud = DEF_BAUD, + .ioport = (unsigned char *)R_SERIAL2_CTRL, + .irq = 1U << 4, /* uses DMA 2 and 3 */ + .oclrintradr = R_DMA_CH2_CLR_INTR, + .ofirstadr = R_DMA_CH2_FIRST, + .ocmdadr = R_DMA_CH2_CMD, + .ostatusadr = R_DMA_CH2_STATUS, + .iclrintradr = R_DMA_CH3_CLR_INTR, + .ifirstadr = R_DMA_CH3_FIRST, + .icmdadr = R_DMA_CH3_CMD, + .idescradr = R_DMA_CH3_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 0, + .dma_owner = dma_ser2, + .io_if = if_serial_2, +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + .enabled = 1, + .io_if_description = "ser2", +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT + .dma_out_enabled = 1, + .dma_out_nbr = SER2_TX_DMA_NBR, + .dma_out_irq_nbr = SER2_DMA_TX_IRQ_NBR, + .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_description = "serial 2 dma tr", +#else + .dma_out_enabled = 0, + .dma_out_nbr = UINT_MAX, + .dma_out_irq_nbr = 0, + .dma_out_irq_flags = 0, + .dma_out_irq_description = NULL, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN + .dma_in_enabled = 1, + .dma_in_nbr = SER2_RX_DMA_NBR, + .dma_in_irq_nbr = SER2_DMA_RX_IRQ_NBR, + .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_description = "serial 2 dma rec", +#else + .dma_in_enabled = 0, + .dma_in_nbr = UINT_MAX, + .dma_in_irq_nbr = 0, + .dma_in_irq_flags = 0, + .dma_in_irq_description = NULL, +#endif +#else + .enabled = 0, + .io_if_description = NULL, + .dma_out_enabled = 0, + .dma_in_enabled = 0 +#endif + }, /* ttyS2 */ + + { .baud = DEF_BAUD, + .ioport = (unsigned char *)R_SERIAL3_CTRL, + .irq = 1U << 8, /* uses DMA 4 and 5 */ + .oclrintradr = R_DMA_CH4_CLR_INTR, + .ofirstadr = R_DMA_CH4_FIRST, + .ocmdadr = R_DMA_CH4_CMD, + .ostatusadr = R_DMA_CH4_STATUS, + .iclrintradr = R_DMA_CH5_CLR_INTR, + .ifirstadr = R_DMA_CH5_FIRST, + .icmdadr = R_DMA_CH5_CMD, + .idescradr = R_DMA_CH5_DESCR, + .flags = STD_FLAGS, + .rx_ctrl = DEF_RX, + .tx_ctrl = DEF_TX, + .iseteop = 1, + .dma_owner = dma_ser3, + .io_if = if_serial_3, +#ifdef CONFIG_ETRAX_SERIAL_PORT3 + .enabled = 1, + .io_if_description = "ser3", +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT + .dma_out_enabled = 1, + .dma_out_nbr = SER3_TX_DMA_NBR, + .dma_out_irq_nbr = SER3_DMA_TX_IRQ_NBR, + .dma_out_irq_flags = IRQF_DISABLED, + .dma_out_irq_description = "serial 3 dma tr", +#else + .dma_out_enabled = 0, + .dma_out_nbr = UINT_MAX, + .dma_out_irq_nbr = 0, + .dma_out_irq_flags = 0, + .dma_out_irq_description = NULL, +#endif +#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN + .dma_in_enabled = 1, + .dma_in_nbr = SER3_RX_DMA_NBR, + .dma_in_irq_nbr = SER3_DMA_RX_IRQ_NBR, + .dma_in_irq_flags = IRQF_DISABLED, + .dma_in_irq_description = "serial 3 dma rec", +#else + .dma_in_enabled = 0, + .dma_in_nbr = UINT_MAX, + .dma_in_irq_nbr = 0, + .dma_in_irq_flags = 0, + .dma_in_irq_description = NULL +#endif +#else + .enabled = 0, + .io_if_description = NULL, + .dma_out_enabled = 0, + .dma_in_enabled = 0 +#endif + } /* ttyS3 */ +#endif +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial)) + +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +static struct fast_timer fast_timers[NR_PORTS]; +#endif + +#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY +#define PROCSTAT(x) x +struct ser_statistics_type { + int overrun_cnt; + int early_errors_cnt; + int ser_ints_ok_cnt; + int errors_cnt; + unsigned long int processing_flip; + unsigned long processing_flip_still_room; + unsigned long int timeout_flush_cnt; + int rx_dma_ints; + int tx_dma_ints; + int rx_tot; + int tx_tot; +}; + +static struct ser_statistics_type ser_stat[NR_PORTS]; + +#else + +#define PROCSTAT(x) + +#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */ + +/* RS-485 */ +#if defined(CONFIG_ETRAX_RS485) +#ifdef CONFIG_ETRAX_FAST_TIMER +static struct fast_timer fast_timers_rs485[NR_PORTS]; +#endif +#if defined(CONFIG_ETRAX_RS485_ON_PA) +static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT; +#endif +#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) +static int rs485_port_g_bit = CONFIG_ETRAX_RS485_ON_PORT_G_BIT; +#endif +#endif + +/* Info and macros needed for each ports extra control/status signals. */ +#define E100_STRUCT_PORT(line, pinname) \ + ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ + (R_PORT_PA_DATA): ( \ + (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ + (R_PORT_PB_DATA):&dummy_ser[line])) + +#define E100_STRUCT_SHADOW(line, pinname) \ + ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ + (&port_pa_data_shadow): ( \ + (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \ + (&port_pb_data_shadow):&dummy_ser[line])) +#define E100_STRUCT_MASK(line, pinname) \ + ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \ + (1<= 0)? \ + (1< 3.3V to RS-232 driver -> -12V on RS-232 level + * inactive = 1 -> 0V to RS-232 driver -> +12V on RS-232 level + * + * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip + */ + +/* Output */ +#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK) +/* Input */ +#define E100_CTS_GET(info) ((info)->ioport[REG_STATUS] & E100_CTS_MASK) + +/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */ +/* Is an output */ +#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].dtr_shadow) & e100_modem_pins[(info)->line].dtr_mask) + +/* Normally inputs */ +#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].ri_port) & e100_modem_pins[(info)->line].ri_mask) +#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].cd_port) & e100_modem_pins[(info)->line].cd_mask) + +/* Input */ +#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask) + + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the memcpy_fromfs blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +static DEFINE_MUTEX(tmp_buf_mutex); + +/* Calculate the chartime depending on baudrate, numbor of bits etc. */ +static void update_char_time(struct e100_serial * info) +{ + tcflag_t cflags = info->port.tty->termios->c_cflag; + int bits; + + /* calc. number of bits / data byte */ + /* databits + startbit and 1 stopbit */ + if ((cflags & CSIZE) == CS7) + bits = 9; + else + bits = 10; + + if (cflags & CSTOPB) /* 2 stopbits ? */ + bits++; + + if (cflags & PARENB) /* parity bit ? */ + bits++; + + /* calc timeout */ + info->char_time_usec = ((bits * 1000000) / info->baud) + 1; + info->flush_time_usec = 4*info->char_time_usec; + if (info->flush_time_usec < MIN_FLUSH_TIME_USEC) + info->flush_time_usec = MIN_FLUSH_TIME_USEC; + +} + +/* + * This function maps from the Bxxxx defines in asm/termbits.h into real + * baud rates. + */ + +static int +cflag_to_baud(unsigned int cflag) +{ + static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400 }; + + static int ext_baud_table[] = { + 0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000, + 0, 0, 0, 0, 0, 0, 0, 0 }; + + if (cflag & CBAUDEX) + return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX]; + else + return baud_table[cflag & CBAUD]; +} + +/* and this maps to an etrax100 hardware baud constant */ + +static unsigned char +cflag_to_etrax_baud(unsigned int cflag) +{ + char retval; + + static char baud_table[] = { + -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 }; + + static char ext_baud_table[] = { + -1, 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 }; + + if (cflag & CBAUDEX) + retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX]; + else + retval = baud_table[cflag & CBAUD]; + + if (retval < 0) { + printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag); + retval = 5; /* choose default 9600 instead */ + } + + return retval | (retval << 4); /* choose same for both TX and RX */ +} + + +/* Various static support functions */ + +/* Functions to set or clear DTR/RTS on the requested line */ +/* It is complicated by the fact that RTS is a serial port register, while + * DTR might not be implemented in the HW at all, and if it is, it can be on + * any general port. + */ + + +static inline void +e100_dtr(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + unsigned char mask = e100_modem_pins[info->line].dtr_mask; + +#ifdef SERIAL_DEBUG_IO + printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask); + printk("ser%i shadow before 0x%02X get: %i\n", + info->line, *e100_modem_pins[info->line].dtr_shadow, + E100_DTR_GET(info)); +#endif + /* DTR is active low */ + { + unsigned long flags; + + local_irq_save(flags); + *e100_modem_pins[info->line].dtr_shadow &= ~mask; + *e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow; + local_irq_restore(flags); + } + +#ifdef SERIAL_DEBUG_IO + printk("ser%i shadow after 0x%02X get: %i\n", + info->line, *e100_modem_pins[info->line].dtr_shadow, + E100_DTR_GET(info)); +#endif +#endif +} + +/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive + * 0=0V , 1=3.3V + */ +static inline void +e100_rts(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + unsigned long flags; + local_irq_save(flags); + info->rx_ctrl &= ~E100_RTS_MASK; + info->rx_ctrl |= (set ? 0 : E100_RTS_MASK); /* RTS is active low */ + info->ioport[REG_REC_CTRL] = info->rx_ctrl; + local_irq_restore(flags); +#ifdef SERIAL_DEBUG_IO + printk("ser%i rts %i\n", info->line, set); +#endif +#endif +} + + +/* If this behaves as a modem, RI and CD is an output */ +static inline void +e100_ri_out(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + /* RI is active low */ + { + unsigned char mask = e100_modem_pins[info->line].ri_mask; + unsigned long flags; + + local_irq_save(flags); + *e100_modem_pins[info->line].ri_shadow &= ~mask; + *e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow; + local_irq_restore(flags); + } +#endif +} +static inline void +e100_cd_out(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + /* CD is active low */ + { + unsigned char mask = e100_modem_pins[info->line].cd_mask; + unsigned long flags; + + local_irq_save(flags); + *e100_modem_pins[info->line].cd_shadow &= ~mask; + *e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow; + local_irq_restore(flags); + } +#endif +} + +static inline void +e100_disable_rx(struct e100_serial *info) +{ +#ifndef CONFIG_SVINTO_SIM + /* disable the receiver */ + info->ioport[REG_REC_CTRL] = + (info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); +#endif +} + +static inline void +e100_enable_rx(struct e100_serial *info) +{ +#ifndef CONFIG_SVINTO_SIM + /* enable the receiver */ + info->ioport[REG_REC_CTRL] = + (info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable)); +#endif +} + +/* the rx DMA uses both the dma_descr and the dma_eop interrupts */ + +static inline void +e100_disable_rxdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("rxdma_irq(%d): 0\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line)); + *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3); +} + +static inline void +e100_enable_rxdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("rxdma_irq(%d): 1\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line)); + *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3); +} + +/* the tx DMA uses only dma_descr interrupt */ + +static void e100_disable_txdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("txdma_irq(%d): 0\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line)); + *R_IRQ_MASK2_CLR = info->irq; +} + +static void e100_enable_txdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("txdma_irq(%d): 1\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line)); + *R_IRQ_MASK2_SET = info->irq; +} + +static void e100_disable_txdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + /* Disable output DMA channel for the serial port in question + * ( set to something other than serialX) + */ + local_irq_save(flags); + DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line)); + if (info->line == 0) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) == + IO_STATE(R_GEN_CONFIG, dma6, serial0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused); + } + } else if (info->line == 1) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) == + IO_STATE(R_GEN_CONFIG, dma8, serial1)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb); + } + } else if (info->line == 2) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) == + IO_STATE(R_GEN_CONFIG, dma2, serial2)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0); + } + } else if (info->line == 3) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) == + IO_STATE(R_GEN_CONFIG, dma4, serial3)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1); + } + } + *R_GEN_CONFIG = genconfig_shadow; + local_irq_restore(flags); +} + + +static void e100_enable_txdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + local_irq_save(flags); + DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line)); + /* Enable output DMA channel for the serial port in question */ + if (info->line == 0) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0); + } else if (info->line == 1) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1); + } else if (info->line == 2) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2); + } else if (info->line == 3) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma4); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3); + } + *R_GEN_CONFIG = genconfig_shadow; + local_irq_restore(flags); +} + +static void e100_disable_rxdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + /* Disable input DMA channel for the serial port in question + * ( set to something other than serialX) + */ + local_irq_save(flags); + if (info->line == 0) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) == + IO_STATE(R_GEN_CONFIG, dma7, serial0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused); + } + } else if (info->line == 1) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) == + IO_STATE(R_GEN_CONFIG, dma9, serial1)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb); + } + } else if (info->line == 2) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) == + IO_STATE(R_GEN_CONFIG, dma3, serial2)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0); + } + } else if (info->line == 3) { + if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) == + IO_STATE(R_GEN_CONFIG, dma5, serial3)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1); + } + } + *R_GEN_CONFIG = genconfig_shadow; + local_irq_restore(flags); +} + + +static void e100_enable_rxdma_channel(struct e100_serial *info) +{ + unsigned long flags; + + local_irq_save(flags); + /* Enable input DMA channel for the serial port in question */ + if (info->line == 0) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma7); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0); + } else if (info->line == 1) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma9); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1); + } else if (info->line == 2) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma3); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2); + } else if (info->line == 3) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma5); + genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3); + } + *R_GEN_CONFIG = genconfig_shadow; + local_irq_restore(flags); +} + +#ifdef SERIAL_HANDLE_EARLY_ERRORS +/* in order to detect and fix errors on the first byte + we have to use the serial interrupts as well. */ + +static inline void +e100_disable_serial_data_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_irq(%d): 0\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line)); + *R_IRQ_MASK1_CLR = (1U << (8+2*info->line)); +} + +static inline void +e100_enable_serial_data_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_irq(%d): 1\n",info->line); + printk("**** %d = %d\n", + (8+2*info->line), + (1U << (8+2*info->line))); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line)); + *R_IRQ_MASK1_SET = (1U << (8+2*info->line)); +} +#endif + +static inline void +e100_disable_serial_tx_ready_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_tx_irq(%d): 0\n",info->line); +#endif + DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line)); + *R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line)); +} + +static inline void +e100_enable_serial_tx_ready_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_tx_irq(%d): 1\n",info->line); + printk("**** %d = %d\n", + (8+1+2*info->line), + (1U << (8+1+2*info->line))); +#endif + DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line)); + *R_IRQ_MASK1_SET = (1U << (8+1+2*info->line)); +} + +static inline void e100_enable_rx_irq(struct e100_serial *info) +{ + if (info->uses_dma_in) + e100_enable_rxdma_irq(info); + else + e100_enable_serial_data_irq(info); +} +static inline void e100_disable_rx_irq(struct e100_serial *info) +{ + if (info->uses_dma_in) + e100_disable_rxdma_irq(info); + else + e100_disable_serial_data_irq(info); +} + +#if defined(CONFIG_ETRAX_RS485) +/* Enable RS-485 mode on selected port. This is UGLY. */ +static int +e100_enable_rs485(struct tty_struct *tty, struct serial_rs485 *r) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + +#if defined(CONFIG_ETRAX_RS485_ON_PA) + *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit); +#endif +#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, + rs485_port_g_bit, 1); +#endif +#if defined(CONFIG_ETRAX_RS485_LTC1387) + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, + CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 1); + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, + CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 1); +#endif + + info->rs485 = *r; + + /* Maximum delay before RTS equal to 1000 */ + if (info->rs485.delay_rts_before_send >= 1000) + info->rs485.delay_rts_before_send = 1000; + +/* printk("rts: on send = %i, after = %i, enabled = %i", + info->rs485.rts_on_send, + info->rs485.rts_after_sent, + info->rs485.enabled + ); +*/ + return 0; +} + +static int +e100_write_rs485(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + int old_value = (info->rs485.flags) & SER_RS485_ENABLED; + + /* rs485 is always implicitly enabled if we're using the ioctl() + * but it doesn't have to be set in the serial_rs485 + * (to be backward compatible with old apps) + * So we store, set and restore it. + */ + info->rs485.flags |= SER_RS485_ENABLED; + /* rs_write now deals with RS485 if enabled */ + count = rs_write(tty, buf, count); + if (!old_value) + info->rs485.flags &= ~(SER_RS485_ENABLED); + return count; +} + +#ifdef CONFIG_ETRAX_FAST_TIMER +/* Timer function to toggle RTS when using FAST_TIMER */ +static void rs485_toggle_rts_timer_function(unsigned long data) +{ + struct e100_serial *info = (struct e100_serial *)data; + + fast_timers_rs485[info->line].function = NULL; + e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND)); +#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) + e100_enable_rx(info); + e100_enable_rx_irq(info); +#endif +} +#endif +#endif /* CONFIG_ETRAX_RS485 */ + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter using the XOFF registers, as necessary. + * ------------------------------------------------------------ + */ + +static void +rs_stop(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + if (info) { + unsigned long flags; + unsigned long xoff; + + local_irq_save(flags); + DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n", + CIRC_CNT(info->xmit.head, + info->xmit.tail,SERIAL_XMIT_SIZE))); + + xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, + STOP_CHAR(info->port.tty)); + xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop); + if (tty->termios->c_iflag & IXON ) { + xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); + } + + *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; + local_irq_restore(flags); + } +} + +static void +rs_start(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + if (info) { + unsigned long flags; + unsigned long xoff; + + local_irq_save(flags); + DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n", + CIRC_CNT(info->xmit.head, + info->xmit.tail,SERIAL_XMIT_SIZE))); + xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty)); + xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); + if (tty->termios->c_iflag & IXON ) { + xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); + } + + *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; + if (!info->uses_dma_out && + info->xmit.head != info->xmit.tail && info->xmit.buf) + e100_enable_serial_tx_ready_irq(info); + + local_irq_restore(flags); + } +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static void rs_sched_event(struct e100_serial *info, int event) +{ + if (info->event & (1 << event)) + return; + info->event |= 1 << event; + schedule_work(&info->work); +} + +/* The output DMA channel is free - use it to send as many chars as possible + * NOTES: + * We don't pay attention to info->x_char, which means if the TTY wants to + * use XON/XOFF it will set info->x_char but we won't send any X char! + * + * To implement this, we'd just start a DMA send of 1 byte pointing at a + * buffer containing the X char, and skip updating xmit. We'd also have to + * check if the last sent char was the X char when we enter this function + * the next time, to avoid updating xmit with the sent X value. + */ + +static void +transmit_chars_dma(struct e100_serial *info) +{ + unsigned int c, sentl; + struct etrax_dma_descr *descr; + +#ifdef CONFIG_SVINTO_SIM + /* This will output too little if tail is not 0 always since + * we don't reloop to send the other part. Anyway this SHOULD be a + * no-op - transmit_chars_dma would never really be called during sim + * since rs_write does not write into the xmit buffer then. + */ + if (info->xmit.tail) + printk("Error in serial.c:transmit_chars-dma(), tail!=0\n"); + if (info->xmit.head != info->xmit.tail) { + SIMCOUT(info->xmit.buf + info->xmit.tail, + CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE)); + info->xmit.head = info->xmit.tail; /* move back head */ + info->tr_running = 0; + } + return; +#endif + /* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ + *info->oclrintradr = + IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | + IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + +#ifdef SERIAL_DEBUG_INTR + if (info->line == SERIAL_DEBUG_LINE) + printk("tc\n"); +#endif + if (!info->tr_running) { + /* weirdo... we shouldn't get here! */ + printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n"); + return; + } + + descr = &info->tr_descr; + + /* first get the amount of bytes sent during the last DMA transfer, + and update xmit accordingly */ + + /* if the stop bit was not set, all data has been sent */ + if (!(descr->status & d_stop)) { + sentl = descr->sw_len; + } else + /* otherwise we find the amount of data sent here */ + sentl = descr->hw_len; + + DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl)); + + /* update stats */ + info->icount.tx += sentl; + + /* update xmit buffer */ + info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1); + + /* if there is only a few chars left in the buf, wake up the blocked + write if any */ + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + + /* find out the largest amount of consecutive bytes we want to send now */ + + c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + + /* Don't send all in one DMA transfer - divide it so we wake up + * application before all is sent + */ + + if (c >= 4*WAKEUP_CHARS) + c = c/2; + + if (c <= 0) { + /* our job here is done, don't schedule any new DMA transfer */ + info->tr_running = 0; + +#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER) + if (info->rs485.flags & SER_RS485_ENABLED) { + /* Set a short timer to toggle RTS */ + start_one_shot_timer(&fast_timers_rs485[info->line], + rs485_toggle_rts_timer_function, + (unsigned long)info, + info->char_time_usec*2, + "RS-485"); + } +#endif /* RS485 */ + return; + } + + /* ok we can schedule a dma send of c chars starting at info->xmit.tail */ + /* set up the descriptor correctly for output */ + DFLOW(DEBUG_LOG(info->line, "TX %i\n", c)); + descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */ + descr->sw_len = c; + descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail); + descr->status = 0; + + *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */ + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); + + /* DMA is now running (hopefully) */ +} /* transmit_chars_dma */ + +static void +start_transmit(struct e100_serial *info) +{ +#if 0 + if (info->line == SERIAL_DEBUG_LINE) + printk("x\n"); +#endif + + info->tr_descr.sw_len = 0; + info->tr_descr.hw_len = 0; + info->tr_descr.status = 0; + info->tr_running = 1; + if (info->uses_dma_out) + transmit_chars_dma(info); + else + e100_enable_serial_tx_ready_irq(info); +} /* start_transmit */ + +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +static int serial_fast_timer_started = 0; +static int serial_fast_timer_expired = 0; +static void flush_timeout_function(unsigned long data); +#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\ + unsigned long timer_flags; \ + local_irq_save(timer_flags); \ + if (fast_timers[info->line].function == NULL) { \ + serial_fast_timer_started++; \ + TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \ + TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \ + start_one_shot_timer(&fast_timers[info->line], \ + flush_timeout_function, \ + (unsigned long)info, \ + (usec), \ + string); \ + } \ + else { \ + TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \ + } \ + local_irq_restore(timer_flags); \ +} +#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec) + +#else +#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) +#define START_FLUSH_FAST_TIMER(info, string) +#endif + +static struct etrax_recv_buffer * +alloc_recv_buffer(unsigned int size) +{ + struct etrax_recv_buffer *buffer; + + if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC))) + return NULL; + + buffer->next = NULL; + buffer->length = 0; + buffer->error = TTY_NORMAL; + + return buffer; +} + +static void +append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer) +{ + unsigned long flags; + + local_irq_save(flags); + + if (!info->first_recv_buffer) + info->first_recv_buffer = buffer; + else + info->last_recv_buffer->next = buffer; + + info->last_recv_buffer = buffer; + + info->recv_cnt += buffer->length; + if (info->recv_cnt > info->max_recv_cnt) + info->max_recv_cnt = info->recv_cnt; + + local_irq_restore(flags); +} + +static int +add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag) +{ + struct etrax_recv_buffer *buffer; + if (info->uses_dma_in) { + if (!(buffer = alloc_recv_buffer(4))) + return 0; + + buffer->length = 1; + buffer->error = flag; + buffer->buffer[0] = data; + + append_recv_buffer(info, buffer); + + info->icount.rx++; + } else { + struct tty_struct *tty = info->port.tty; + tty_insert_flip_char(tty, data, flag); + info->icount.rx++; + } + + return 1; +} + +static unsigned int handle_descr_data(struct e100_serial *info, + struct etrax_dma_descr *descr, + unsigned int recvl) +{ + struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer; + + if (info->recv_cnt + recvl > 65536) { + printk(KERN_CRIT + "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __func__, recvl); + return 0; + } + + buffer->length = recvl; + + if (info->errorcode == ERRCODE_SET_BREAK) + buffer->error = TTY_BREAK; + info->errorcode = 0; + + append_recv_buffer(info, buffer); + + if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) + panic("%s: Failed to allocate memory for receive buffer!\n", __func__); + + descr->buf = virt_to_phys(buffer->buffer); + + return recvl; +} + +static unsigned int handle_all_descr_data(struct e100_serial *info) +{ + struct etrax_dma_descr *descr; + unsigned int recvl; + unsigned int ret = 0; + + while (1) + { + descr = &info->rec_descr[info->cur_rec_descr]; + + if (descr == phys_to_virt(*info->idescradr)) + break; + + if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS) + info->cur_rec_descr = 0; + + /* find out how many bytes were read */ + + /* if the eop bit was not set, all data has been received */ + if (!(descr->status & d_eop)) { + recvl = descr->sw_len; + } else { + /* otherwise we find the amount of data received here */ + recvl = descr->hw_len; + } + + /* Reset the status information */ + descr->status = 0; + + DFLOW( DEBUG_LOG(info->line, "RX %lu\n", recvl); + if (info->port.tty->stopped) { + unsigned char *buf = phys_to_virt(descr->buf); + DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]); + DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]); + DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]); + } + ); + + /* update stats */ + info->icount.rx += recvl; + + ret += handle_descr_data(info, descr, recvl); + } + + return ret; +} + +static void receive_chars_dma(struct e100_serial *info) +{ + struct tty_struct *tty; + unsigned char rstat; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + return; +#endif + + /* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */ + *info->iclrintradr = + IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | + IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + + tty = info->port.tty; + if (!tty) /* Something wrong... */ + return; + +#ifdef SERIAL_HANDLE_EARLY_ERRORS + if (info->uses_dma_in) + e100_enable_serial_data_irq(info); +#endif + + if (info->errorcode == ERRCODE_INSERT_BREAK) + add_char_and_flag(info, '\0', TTY_BREAK); + + handle_all_descr_data(info); + + /* Read the status register to detect errors */ + rstat = info->ioport[REG_STATUS]; + if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) { + DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat)); + } + + if (rstat & SER_ERROR_MASK) { + /* If we got an error, we must reset it by reading the + * data_in field + */ + unsigned char data = info->ioport[REG_DATA]; + + PROCSTAT(ser_stat[info->line].errors_cnt++); + DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n", + ((rstat & SER_ERROR_MASK) << 8) | data); + + if (rstat & SER_PAR_ERR_MASK) + add_char_and_flag(info, data, TTY_PARITY); + else if (rstat & SER_OVERRUN_MASK) + add_char_and_flag(info, data, TTY_OVERRUN); + else if (rstat & SER_FRAMING_ERR_MASK) + add_char_and_flag(info, data, TTY_FRAME); + } + + START_FLUSH_FAST_TIMER(info, "receive_chars"); + + /* Restart the receiving DMA */ + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); +} + +static int start_recv_dma(struct e100_serial *info) +{ + struct etrax_dma_descr *descr = info->rec_descr; + struct etrax_recv_buffer *buffer; + int i; + + /* Set up the receiving descriptors */ + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) { + if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE))) + panic("%s: Failed to allocate memory for receive buffer!\n", __func__); + + descr[i].ctrl = d_int; + descr[i].buf = virt_to_phys(buffer->buffer); + descr[i].sw_len = SERIAL_DESCR_BUF_SIZE; + descr[i].hw_len = 0; + descr[i].status = 0; + descr[i].next = virt_to_phys(&descr[i+1]); + } + + /* Link the last descriptor to the first */ + descr[i-1].next = virt_to_phys(&descr[0]); + + /* Start with the first descriptor in the list */ + info->cur_rec_descr = 0; + + /* Start the DMA */ + *info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]); + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start); + + /* Input DMA should be running now */ + return 1; +} + +static void +start_receive(struct e100_serial *info) +{ +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + return; +#endif + if (info->uses_dma_in) { + /* reset the input dma channel to be sure it works */ + + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + + start_recv_dma(info); + } +} + + +/* the bits in the MASK2 register are laid out like this: + DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR + where I is the input channel and O is the output channel for the port. + info->irq is the bit number for the DMAO_DESCR so to check the others we + shift info->irq to the left. +*/ + +/* dma output channel interrupt handler + this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or + DMA8(ser1) when they have finished a descriptor with the intr flag set. +*/ + +static irqreturn_t +tr_interrupt(int irq, void *dev_id) +{ + struct e100_serial *info; + unsigned long ireg; + int i; + int handled = 0; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + { + const char *s = "What? tr_interrupt in simulator??\n"; + SIMCOUT(s,strlen(s)); + } + return IRQ_HANDLED; +#endif + + /* find out the line that caused this irq and get it from rs_table */ + + ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ + + for (i = 0; i < NR_PORTS; i++) { + info = rs_table + i; + if (!info->enabled || !info->uses_dma_out) + continue; + /* check for dma_descr (don't need to check for dma_eop in output dma for serial */ + if (ireg & info->irq) { + handled = 1; + /* we can send a new dma bunch. make it so. */ + DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i)); + /* Read jiffies_usec first, + * we want this time to be as late as possible + */ + PROCSTAT(ser_stat[info->line].tx_dma_ints++); + info->last_tx_active_usec = GET_JIFFIES_USEC(); + info->last_tx_active = jiffies; + transmit_chars_dma(info); + } + + /* FIXME: here we should really check for a change in the + status lines and if so call status_handle(info) */ + } + return IRQ_RETVAL(handled); +} /* tr_interrupt */ + +/* dma input channel interrupt handler */ + +static irqreturn_t +rec_interrupt(int irq, void *dev_id) +{ + struct e100_serial *info; + unsigned long ireg; + int i; + int handled = 0; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + { + const char *s = "What? rec_interrupt in simulator??\n"; + SIMCOUT(s,strlen(s)); + } + return IRQ_HANDLED; +#endif + + /* find out the line that caused this irq and get it from rs_table */ + + ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ + + for (i = 0; i < NR_PORTS; i++) { + info = rs_table + i; + if (!info->enabled || !info->uses_dma_in) + continue; + /* check for both dma_eop and dma_descr for the input dma channel */ + if (ireg & ((info->irq << 2) | (info->irq << 3))) { + handled = 1; + /* we have received something */ + receive_chars_dma(info); + } + + /* FIXME: here we should really check for a change in the + status lines and if so call status_handle(info) */ + } + return IRQ_RETVAL(handled); +} /* rec_interrupt */ + +static int force_eop_if_needed(struct e100_serial *info) +{ + /* We check data_avail bit to determine if data has + * arrived since last time + */ + unsigned char rstat = info->ioport[REG_STATUS]; + + /* error or datavail? */ + if (rstat & SER_ERROR_MASK) { + /* Some error has occurred. If there has been valid data, an + * EOP interrupt will be made automatically. If no data, the + * normal ser_interrupt should be enabled and handle it. + * So do nothing! + */ + DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n", + rstat | (info->line << 8)); + return 0; + } + + if (rstat & SER_DATA_AVAIL_MASK) { + /* Ok data, no error, count it */ + TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n", + rstat | (info->line << 8))); + /* Read data to clear status flags */ + (void)info->ioport[REG_DATA]; + + info->forced_eop = 0; + START_FLUSH_FAST_TIMER(info, "magic"); + return 0; + } + + /* hit the timeout, force an EOP for the input + * dma channel if we haven't already + */ + if (!info->forced_eop) { + info->forced_eop = 1; + PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); + TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line)); + FORCE_EOP(info); + } + + return 1; +} + +static void flush_to_flip_buffer(struct e100_serial *info) +{ + struct tty_struct *tty; + struct etrax_recv_buffer *buffer; + unsigned long flags; + + local_irq_save(flags); + tty = info->port.tty; + + if (!tty) { + local_irq_restore(flags); + return; + } + + while ((buffer = info->first_recv_buffer) != NULL) { + unsigned int count = buffer->length; + + tty_insert_flip_string(tty, buffer->buffer, count); + info->recv_cnt -= count; + + if (count == buffer->length) { + info->first_recv_buffer = buffer->next; + kfree(buffer); + } else { + buffer->length -= count; + memmove(buffer->buffer, buffer->buffer + count, buffer->length); + buffer->error = TTY_NORMAL; + } + } + + if (!info->first_recv_buffer) + info->last_recv_buffer = NULL; + + local_irq_restore(flags); + + /* This includes a check for low-latency */ + tty_flip_buffer_push(tty); +} + +static void check_flush_timeout(struct e100_serial *info) +{ + /* Flip what we've got (if we can) */ + flush_to_flip_buffer(info); + + /* We might need to flip later, but not to fast + * since the system is busy processing input... */ + if (info->first_recv_buffer) + START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000); + + /* Force eop last, since data might have come while we're processing + * and if we started the slow timer above, we won't start a fast + * below. + */ + force_eop_if_needed(info); +} + +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER +static void flush_timeout_function(unsigned long data) +{ + struct e100_serial *info = (struct e100_serial *)data; + + fast_timers[info->line].function = NULL; + serial_fast_timer_expired++; + TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line)); + TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired)); + check_flush_timeout(info); +} + +#else + +/* dma fifo/buffer timeout handler + forces an end-of-packet for the dma input channel if no chars + have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s. +*/ + +static struct timer_list flush_timer; + +static void +timed_flush_handler(unsigned long ptr) +{ + struct e100_serial *info; + int i; + +#ifdef CONFIG_SVINTO_SIM + return; +#endif + + for (i = 0; i < NR_PORTS; i++) { + info = rs_table + i; + if (info->uses_dma_in) + check_flush_timeout(info); + } + + /* restart flush timer */ + mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS); +} +#endif + +#ifdef SERIAL_HANDLE_EARLY_ERRORS + +/* If there is an error (ie break) when the DMA is running and + * there are no bytes in the fifo the DMA is stopped and we get no + * eop interrupt. Thus we have to monitor the first bytes on a DMA + * transfer, and if it is without error we can turn the serial + * interrupts off. + */ + +/* +BREAK handling on ETRAX 100: +ETRAX will generate interrupt although there is no stop bit between the +characters. + +Depending on how long the break sequence is, the end of the breaksequence +will look differently: +| indicates start/end of a character. + +B= Break character (0x00) with framing error. +E= Error byte with parity error received after B characters. +F= "Faked" valid byte received immediately after B characters. +V= Valid byte + +1. + B BL ___________________________ V +.._|__________|__________| |valid data | + +Multiple frame errors with data == 0x00 (B), +the timing matches up "perfectly" so no extra ending char is detected. +The RXD pin is 1 in the last interrupt, in that case +we set info->errorcode = ERRCODE_INSERT_BREAK, but we can't really +know if another byte will come and this really is case 2. below +(e.g F=0xFF or 0xFE) +If RXD pin is 0 we can expect another character (see 2. below). + + +2. + + B B E or F__________________..__ V +.._|__________|__________|______ | |valid data + "valid" or + parity error + +Multiple frame errors with data == 0x00 (B), +but the part of the break trigs is interpreted as a start bit (and possibly +some 0 bits followed by a number of 1 bits and a stop bit). +Depending on parity settings etc. this last character can be either +a fake "valid" char (F) or have a parity error (E). + +If the character is valid it will be put in the buffer, +we set info->errorcode = ERRCODE_SET_BREAK so the receive interrupt +will set the flags so the tty will handle it, +if it's an error byte it will not be put in the buffer +and we set info->errorcode = ERRCODE_INSERT_BREAK. + +To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp +of the last faulty char (B) and compares it with the current time: +If the time elapsed time is less then 2*char_time_usec we will assume +it's a faked F char and not a Valid char and set +info->errorcode = ERRCODE_SET_BREAK. + +Flaws in the above solution: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We use the timer to distinguish a F character from a V character, +if a V character is to close after the break we might make the wrong decision. + +TODO: The break will be delayed until an F or V character is received. + +*/ + +static +struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info) +{ + unsigned long data_read; + struct tty_struct *tty = info->port.tty; + + if (!tty) { + printk("!NO TTY!\n"); + return info; + } + + /* Read data and status at the same time */ + data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]); +more_data: + if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) { + DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0)); + } + DINTR2(DEBUG_LOG(info->line, "ser_rx %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read))); + + if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) | + IO_MASK(R_SERIAL0_READ, par_err) | + IO_MASK(R_SERIAL0_READ, overrun) )) { + /* An error */ + info->last_rx_active_usec = GET_JIFFIES_USEC(); + info->last_rx_active = jiffies; + DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read)); + DLOG_INT_TRIG( + if (!log_int_trig1_pos) { + log_int_trig1_pos = log_int_pos; + log_int(rdpc(), 0, 0); + } + ); + + + if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) && + (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) { + /* Most likely a break, but we get interrupts over and + * over again. + */ + + if (!info->break_detected_cnt) { + DEBUG_LOG(info->line, "#BRK start\n", 0); + } + if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) { + /* The RX pin is high now, so the break + * must be over, but.... + * we can't really know if we will get another + * last byte ending the break or not. + * And we don't know if the byte (if any) will + * have an error or look valid. + */ + DEBUG_LOG(info->line, "# BL BRK\n", 0); + info->errorcode = ERRCODE_INSERT_BREAK; + } + info->break_detected_cnt++; + } else { + /* The error does not look like a break, but could be + * the end of one + */ + if (info->break_detected_cnt) { + DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); + info->errorcode = ERRCODE_INSERT_BREAK; + } else { + unsigned char data = IO_EXTRACT(R_SERIAL0_READ, + data_in, data_read); + char flag = TTY_NORMAL; + if (info->errorcode == ERRCODE_INSERT_BREAK) { + struct tty_struct *tty = info->port.tty; + tty_insert_flip_char(tty, 0, flag); + info->icount.rx++; + } + + if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) { + info->icount.parity++; + flag = TTY_PARITY; + } else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) { + info->icount.overrun++; + flag = TTY_OVERRUN; + } else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) { + info->icount.frame++; + flag = TTY_FRAME; + } + tty_insert_flip_char(tty, data, flag); + info->errorcode = 0; + } + info->break_detected_cnt = 0; + } + } else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) { + /* No error */ + DLOG_INT_TRIG( + if (!log_int_trig1_pos) { + if (log_int_pos >= log_int_size) { + log_int_pos = 0; + } + log_int_trig0_pos = log_int_pos; + log_int(rdpc(), 0, 0); + } + ); + tty_insert_flip_char(tty, + IO_EXTRACT(R_SERIAL0_READ, data_in, data_read), + TTY_NORMAL); + } else { + DEBUG_LOG(info->line, "ser_rx int but no data_avail %08lX\n", data_read); + } + + + info->icount.rx++; + data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]); + if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) { + DEBUG_LOG(info->line, "ser_rx %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)); + goto more_data; + } + + tty_flip_buffer_push(info->port.tty); + return info; +} + +static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info) +{ + unsigned char rstat; + +#ifdef SERIAL_DEBUG_INTR + printk("Interrupt from serport %d\n", i); +#endif +/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ + if (!info->uses_dma_in) { + return handle_ser_rx_interrupt_no_dma(info); + } + /* DMA is used */ + rstat = info->ioport[REG_STATUS]; + if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) { + DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0)); + } + + if (rstat & SER_ERROR_MASK) { + unsigned char data; + + info->last_rx_active_usec = GET_JIFFIES_USEC(); + info->last_rx_active = jiffies; + /* If we got an error, we must reset it by reading the + * data_in field + */ + data = info->ioport[REG_DATA]; + DINTR1(DEBUG_LOG(info->line, "ser_rx! %c\n", data)); + DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat)); + if (!data && (rstat & SER_FRAMING_ERR_MASK)) { + /* Most likely a break, but we get interrupts over and + * over again. + */ + + if (!info->break_detected_cnt) { + DEBUG_LOG(info->line, "#BRK start\n", 0); + } + if (rstat & SER_RXD_MASK) { + /* The RX pin is high now, so the break + * must be over, but.... + * we can't really know if we will get another + * last byte ending the break or not. + * And we don't know if the byte (if any) will + * have an error or look valid. + */ + DEBUG_LOG(info->line, "# BL BRK\n", 0); + info->errorcode = ERRCODE_INSERT_BREAK; + } + info->break_detected_cnt++; + } else { + /* The error does not look like a break, but could be + * the end of one + */ + if (info->break_detected_cnt) { + DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); + info->errorcode = ERRCODE_INSERT_BREAK; + } else { + if (info->errorcode == ERRCODE_INSERT_BREAK) { + info->icount.brk++; + add_char_and_flag(info, '\0', TTY_BREAK); + } + + if (rstat & SER_PAR_ERR_MASK) { + info->icount.parity++; + add_char_and_flag(info, data, TTY_PARITY); + } else if (rstat & SER_OVERRUN_MASK) { + info->icount.overrun++; + add_char_and_flag(info, data, TTY_OVERRUN); + } else if (rstat & SER_FRAMING_ERR_MASK) { + info->icount.frame++; + add_char_and_flag(info, data, TTY_FRAME); + } + + info->errorcode = 0; + } + info->break_detected_cnt = 0; + DEBUG_LOG(info->line, "#iERR s d %04X\n", + ((rstat & SER_ERROR_MASK) << 8) | data); + } + PROCSTAT(ser_stat[info->line].early_errors_cnt++); + } else { /* It was a valid byte, now let the DMA do the rest */ + unsigned long curr_time_u = GET_JIFFIES_USEC(); + unsigned long curr_time = jiffies; + + if (info->break_detected_cnt) { + /* Detect if this character is a new valid char or the + * last char in a break sequence: If LSBits are 0 and + * MSBits are high AND the time is close to the + * previous interrupt we should discard it. + */ + long elapsed_usec = + (curr_time - info->last_rx_active) * (1000000/HZ) + + curr_time_u - info->last_rx_active_usec; + if (elapsed_usec < 2*info->char_time_usec) { + DEBUG_LOG(info->line, "FBRK %i\n", info->line); + /* Report as BREAK (error) and let + * receive_chars_dma() handle it + */ + info->errorcode = ERRCODE_SET_BREAK; + } else { + DEBUG_LOG(info->line, "Not end of BRK (V)%i\n", info->line); + } + DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt); + } + +#ifdef SERIAL_DEBUG_INTR + printk("** OK, disabling ser_interrupts\n"); +#endif + e100_disable_serial_data_irq(info); + DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line)); + info->break_detected_cnt = 0; + + PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); + } + /* Restarting the DMA never hurts */ + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); + START_FLUSH_FAST_TIMER(info, "ser_int"); + return info; +} /* handle_ser_rx_interrupt */ + +static void handle_ser_tx_interrupt(struct e100_serial *info) +{ + unsigned long flags; + + if (info->x_char) { + unsigned char rstat; + DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char)); + local_irq_save(flags); + rstat = info->ioport[REG_STATUS]; + DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat)); + + info->ioport[REG_TR_DATA] = info->x_char; + info->icount.tx++; + info->x_char = 0; + /* We must enable since it is disabled in ser_interrupt */ + e100_enable_serial_tx_ready_irq(info); + local_irq_restore(flags); + return; + } + if (info->uses_dma_out) { + unsigned char rstat; + int i; + /* We only use normal tx interrupt when sending x_char */ + DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0)); + local_irq_save(flags); + rstat = info->ioport[REG_STATUS]; + DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat)); + e100_disable_serial_tx_ready_irq(info); + if (info->port.tty->stopped) + rs_stop(info->port.tty); + /* Enable the DMA channel and tell it to continue */ + e100_enable_txdma_channel(info); + /* Wait 12 cycles before doing the DMA command */ + for(i = 6; i > 0; i--) + nop(); + + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue); + local_irq_restore(flags); + return; + } + /* Normal char-by-char interrupt */ + if (info->xmit.head == info->xmit.tail + || info->port.tty->stopped + || info->port.tty->hw_stopped) { + DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", + info->port.tty->stopped)); + e100_disable_serial_tx_ready_irq(info); + info->tr_running = 0; + return; + } + DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail])); + /* Send a byte, rs485 timing is critical so turn of ints */ + local_irq_save(flags); + info->ioport[REG_TR_DATA] = info->xmit.buf[info->xmit.tail]; + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->icount.tx++; + if (info->xmit.head == info->xmit.tail) { +#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER) + if (info->rs485.flags & SER_RS485_ENABLED) { + /* Set a short timer to toggle RTS */ + start_one_shot_timer(&fast_timers_rs485[info->line], + rs485_toggle_rts_timer_function, + (unsigned long)info, + info->char_time_usec*2, + "RS-485"); + } +#endif /* RS485 */ + info->last_tx_active_usec = GET_JIFFIES_USEC(); + info->last_tx_active = jiffies; + e100_disable_serial_tx_ready_irq(info); + info->tr_running = 0; + DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0)); + } else { + /* We must enable since it is disabled in ser_interrupt */ + e100_enable_serial_tx_ready_irq(info); + } + local_irq_restore(flags); + + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + +} /* handle_ser_tx_interrupt */ + +/* result of time measurements: + * RX duration 54-60 us when doing something, otherwise 6-9 us + * ser_int duration: just sending: 8-15 us normally, up to 73 us + */ +static irqreturn_t +ser_interrupt(int irq, void *dev_id) +{ + static volatile int tx_started = 0; + struct e100_serial *info; + int i; + unsigned long flags; + unsigned long irq_mask1_rd; + unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */ + int handled = 0; + static volatile unsigned long reentered_ready_mask = 0; + + local_irq_save(flags); + irq_mask1_rd = *R_IRQ_MASK1_RD; + /* First handle all rx interrupts with ints disabled */ + info = rs_table; + irq_mask1_rd &= e100_ser_int_mask; + for (i = 0; i < NR_PORTS; i++) { + /* Which line caused the data irq? */ + if (irq_mask1_rd & data_mask) { + handled = 1; + handle_ser_rx_interrupt(info); + } + info += 1; + data_mask <<= 2; + } + /* Handle tx interrupts with interrupts enabled so we + * can take care of new data interrupts while transmitting + * We protect the tx part with the tx_started flag. + * We disable the tr_ready interrupts we are about to handle and + * unblock the serial interrupt so new serial interrupts may come. + * + * If we get a new interrupt: + * - it migth be due to synchronous serial ports. + * - serial irq will be blocked by general irq handler. + * - async data will be handled above (sync will be ignored). + * - tx_started flag will prevent us from trying to send again and + * we will exit fast - no need to unblock serial irq. + * - Next (sync) serial interrupt handler will be runned with + * disabled interrupt due to restore_flags() at end of function, + * so sync handler will not be preempted or reentered. + */ + if (!tx_started) { + unsigned long ready_mask; + unsigned long + tx_started = 1; + /* Only the tr_ready interrupts left */ + irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser1_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser2_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser3_ready)); + while (irq_mask1_rd) { + /* Disable those we are about to handle */ + *R_IRQ_MASK1_CLR = irq_mask1_rd; + /* Unblock the serial interrupt */ + *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); + + local_irq_enable(); + ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */ + info = rs_table; + for (i = 0; i < NR_PORTS; i++) { + /* Which line caused the ready irq? */ + if (irq_mask1_rd & ready_mask) { + handled = 1; + handle_ser_tx_interrupt(info); + } + info += 1; + ready_mask <<= 2; + } + /* handle_ser_tx_interrupt enables tr_ready interrupts */ + local_irq_disable(); + /* Handle reentered TX interrupt */ + irq_mask1_rd = reentered_ready_mask; + } + local_irq_disable(); + tx_started = 0; + } else { + unsigned long ready_mask; + ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser1_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser2_ready) | + IO_MASK(R_IRQ_MASK1_RD, ser3_ready)); + if (ready_mask) { + reentered_ready_mask |= ready_mask; + /* Disable those we are about to handle */ + *R_IRQ_MASK1_CLR = ready_mask; + DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask)); + } + } + + local_irq_restore(flags); + return IRQ_RETVAL(handled); +} /* ser_interrupt */ +#endif + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void +do_softint(struct work_struct *work) +{ + struct e100_serial *info; + struct tty_struct *tty; + + info = container_of(work, struct e100_serial, work); + + tty = info->port.tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) + tty_wakeup(tty); +} + +static int +startup(struct e100_serial * info) +{ + unsigned long flags; + unsigned long xmit_page; + int i; + + xmit_page = get_zeroed_page(GFP_KERNEL); + if (!xmit_page) + return -ENOMEM; + + local_irq_save(flags); + + /* if it was already initialized, skip this */ + + if (info->flags & ASYNC_INITIALIZED) { + local_irq_restore(flags); + free_page(xmit_page); + return 0; + } + + if (info->xmit.buf) + free_page(xmit_page); + else + info->xmit.buf = (unsigned char *) xmit_page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf); +#endif + +#ifdef CONFIG_SVINTO_SIM + /* Bits and pieces collected from below. Better to have them + in one ifdef:ed clause than to mix in a lot of ifdefs, + right? */ + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->xmit.head = info->xmit.tail = 0; + info->first_recv_buffer = info->last_recv_buffer = NULL; + info->recv_cnt = info->max_recv_cnt = 0; + + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) + info->rec_descr[i].buf = NULL; + + /* No real action in the simulator, but may set info important + to ioctl. */ + change_speed(info); +#else + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + + /* + * Reset the DMA channels and make sure their interrupts are cleared + */ + + if (info->dma_in_enabled) { + info->uses_dma_in = 1; + e100_enable_rxdma_channel(info); + + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + + /* Wait until reset cycle is complete */ + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + + /* Make sure the irqs are cleared */ + *info->iclrintradr = + IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | + IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + } else { + e100_disable_rxdma_channel(info); + } + + if (info->dma_out_enabled) { + info->uses_dma_out = 1; + e100_enable_txdma_channel(info); + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) == + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset)); + + /* Make sure the irqs are cleared */ + *info->oclrintradr = + IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) | + IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do); + } else { + e100_disable_txdma_channel(info); + } + + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->xmit.head = info->xmit.tail = 0; + info->first_recv_buffer = info->last_recv_buffer = NULL; + info->recv_cnt = info->max_recv_cnt = 0; + + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) + info->rec_descr[i].buf = 0; + + /* + * and set the speed and other flags of the serial port + * this will start the rx/tx as well + */ +#ifdef SERIAL_HANDLE_EARLY_ERRORS + e100_enable_serial_data_irq(info); +#endif + change_speed(info); + + /* dummy read to reset any serial errors */ + + (void)info->ioport[REG_DATA]; + + /* enable the interrupts */ + if (info->uses_dma_out) + e100_enable_txdma_irq(info); + + e100_enable_rx_irq(info); + + info->tr_running = 0; /* to be sure we don't lock up the transmitter */ + + /* setup the dma input descriptor and start dma */ + + start_receive(info); + + /* for safety, make sure the descriptors last result is 0 bytes written */ + + info->tr_descr.sw_len = 0; + info->tr_descr.hw_len = 0; + info->tr_descr.status = 0; + + /* enable RTS/DTR last */ + + e100_rts(info, 1); + e100_dtr(info, 1); + +#endif /* CONFIG_SVINTO_SIM */ + + info->flags |= ASYNC_INITIALIZED; + + local_irq_restore(flags); + return 0; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void +shutdown(struct e100_serial * info) +{ + unsigned long flags; + struct etrax_dma_descr *descr = info->rec_descr; + struct etrax_recv_buffer *buffer; + int i; + +#ifndef CONFIG_SVINTO_SIM + /* shut down the transmitter and receiver */ + DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line)); + e100_disable_rx(info); + info->ioport[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40); + + /* disable interrupts, reset dma channels */ + if (info->uses_dma_in) { + e100_disable_rxdma_irq(info); + *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + info->uses_dma_in = 0; + } else { + e100_disable_serial_data_irq(info); + } + + if (info->uses_dma_out) { + e100_disable_txdma_irq(info); + info->tr_running = 0; + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset); + info->uses_dma_out = 0; + } else { + e100_disable_serial_tx_ready_irq(info); + info->tr_running = 0; + } + +#endif /* CONFIG_SVINTO_SIM */ + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....\n", info->line, + info->irq); +#endif + + local_irq_save(flags); + + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + } + + for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) + if (descr[i].buf) { + buffer = phys_to_virt(descr[i].buf) - sizeof *buffer; + kfree(buffer); + descr[i].buf = 0; + } + + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { + /* hang up DTR and RTS if HUPCL is enabled */ + e100_dtr(info, 0); + e100_rts(info, 0); /* could check CRTSCTS before doing this */ + } + + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + local_irq_restore(flags); +} + + +/* change baud rate and other assorted parameters */ + +static void +change_speed(struct e100_serial *info) +{ + unsigned int cflag; + unsigned long xoff; + unsigned long flags; + /* first some safety checks */ + + if (!info->port.tty || !info->port.tty->termios) + return; + if (!info->ioport) + return; + + cflag = info->port.tty->termios->c_cflag; + + /* possibly, the tx/rx should be disabled first to do this safely */ + + /* change baud-rate and write it to the hardware */ + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { + /* Special baudrate */ + u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ + unsigned long alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); + /* R_ALT_SER_BAUDRATE selects the source */ + DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n", + (unsigned long)info->baud_base, info->custom_divisor)); + if (info->baud_base == SERIAL_PRESCALE_BASE) { + /* 0, 2-65535 (0=65536) */ + u16 divisor = info->custom_divisor; + /* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */ + /* baudrate is 3.125MHz/custom_divisor */ + alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale); + alt_source = 0x11; + DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor)); + *R_SERIAL_PRESCALE = divisor; + info->baud = SERIAL_PRESCALE_BASE/divisor; + } +#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED + else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 && + info->custom_divisor == 1) || + (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ && + info->custom_divisor == 8)) { + /* ext_clk selected */ + alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern); + DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8)); + info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8; + } +#endif + else + { + /* Bad baudbase, we don't support using timer0 + * for baudrate. + */ + printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n", + (unsigned long)info->baud_base, info->custom_divisor); + } + r_alt_ser_baudrate_shadow &= ~mask; + r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); + *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; + } else { + /* Normal baudrate */ + /* Make sure we use normal baudrate */ + u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */ + unsigned long alt_source = + IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) | + IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal); + r_alt_ser_baudrate_shadow &= ~mask; + r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8)); +#ifndef CONFIG_SVINTO_SIM + *R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow; +#endif /* CONFIG_SVINTO_SIM */ + + info->baud = cflag_to_baud(cflag); +#ifndef CONFIG_SVINTO_SIM + info->ioport[REG_BAUD] = cflag_to_etrax_baud(cflag); +#endif /* CONFIG_SVINTO_SIM */ + } + +#ifndef CONFIG_SVINTO_SIM + /* start with default settings and then fill in changes */ + local_irq_save(flags); + /* 8 bit, no/even parity */ + info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) | + IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) | + IO_MASK(R_SERIAL0_REC_CTRL, rec_par)); + + /* 8 bit, no/even parity, 1 stop bit, no cts */ + info->tx_ctrl &= ~(IO_MASK(R_SERIAL0_TR_CTRL, tr_bitnr) | + IO_MASK(R_SERIAL0_TR_CTRL, tr_par_en) | + IO_MASK(R_SERIAL0_TR_CTRL, tr_par) | + IO_MASK(R_SERIAL0_TR_CTRL, stop_bits) | + IO_MASK(R_SERIAL0_TR_CTRL, auto_cts)); + + if ((cflag & CSIZE) == CS7) { + /* set 7 bit mode */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit); + } + + if (cflag & CSTOPB) { + /* set 2 stop bit mode */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, two_bits); + } + + if (cflag & PARENB) { + /* enable parity */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable); + } + + if (cflag & CMSPAR) { + /* enable stick parity, PARODD mean Mark which matches ETRAX */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick); + } + if (cflag & PARODD) { + /* set odd parity (or Mark if CMSPAR) */ + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd); + } + + if (cflag & CRTSCTS) { + /* enable automatic CTS handling */ + DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0)); + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active); + } + + /* make sure the tx and rx are enabled */ + + info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable); + info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); + + /* actually write the control regs to the hardware */ + + info->ioport[REG_TR_CTRL] = info->tx_ctrl; + info->ioport[REG_REC_CTRL] = info->rx_ctrl; + xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty)); + xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable); + if (info->port.tty->termios->c_iflag & IXON ) { + DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", + STOP_CHAR(info->port.tty))); + xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable); + } + + *((unsigned long *)&info->ioport[REG_XOFF]) = xoff; + local_irq_restore(flags); +#endif /* !CONFIG_SVINTO_SIM */ + + update_char_time(info); + +} /* change_speed */ + +/* start transmitting chars NOW */ + +static void +rs_flush_chars(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + if (info->tr_running || + info->xmit.head == info->xmit.tail || + tty->stopped || + tty->hw_stopped || + !info->xmit.buf) + return; + +#ifdef SERIAL_DEBUG_FLOW + printk("rs_flush_chars\n"); +#endif + + /* this protection might not exactly be necessary here */ + + local_irq_save(flags); + start_transmit(info); + local_irq_restore(flags); +} + +static int rs_raw_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + /* first some sanity checks */ + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + +#ifdef SERIAL_DEBUG_DATA + if (info->line == SERIAL_DEBUG_LINE) + printk("rs_raw_write (%d), status %d\n", + count, info->ioport[REG_STATUS]); +#endif + +#ifdef CONFIG_SVINTO_SIM + /* Really simple. The output is here and now. */ + SIMCOUT(buf, count); + return count; +#endif + local_save_flags(flags); + DFLOW(DEBUG_LOG(info->line, "write count %i ", count)); + DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty))); + + + /* The local_irq_disable/restore_flags pairs below are needed + * because the DMA interrupt handler moves the info->xmit values. + * the memcpy needs to be in the critical region unfortunately, + * because we need to read xmit values, memcpy, write xmit values + * in one atomic operation... this could perhaps be avoided by + * more clever design. + */ + local_irq_disable(); + while (count) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + + if (count < c) + c = count; + if (c <= 0) + break; + + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = (info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1); + buf += c; + count -= c; + ret += c; + } + local_irq_restore(flags); + + /* enable transmitter if not running, unless the tty is stopped + * this does not need IRQ protection since if tr_running == 0 + * the IRQ's are not running anyway for this port. + */ + DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret)); + + if (info->xmit.head != info->xmit.tail && + !tty->stopped && + !tty->hw_stopped && + !info->tr_running) { + start_transmit(info); + } + + return ret; +} /* raw_raw_write() */ + +static int +rs_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ +#if defined(CONFIG_ETRAX_RS485) + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + if (info->rs485.flags & SER_RS485_ENABLED) + { + /* If we are in RS-485 mode, we need to toggle RTS and disable + * the receiver before initiating a DMA transfer + */ +#ifdef CONFIG_ETRAX_FAST_TIMER + /* Abort any started timer */ + fast_timers_rs485[info->line].function = NULL; + del_fast_timer(&fast_timers_rs485[info->line]); +#endif + e100_rts(info, (info->rs485.flags & SER_RS485_RTS_ON_SEND)); +#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) + e100_disable_rx(info); + e100_enable_rx_irq(info); +#endif + if ((info->rs485.flags & SER_RS485_RTS_BEFORE_SEND) && + (info->rs485.delay_rts_before_send > 0)) + msleep(info->rs485.delay_rts_before_send); + } +#endif /* CONFIG_ETRAX_RS485 */ + + count = rs_raw_write(tty, buf, count); + +#if defined(CONFIG_ETRAX_RS485) + if (info->rs485.flags & SER_RS485_ENABLED) + { + unsigned int val; + /* If we are in RS-485 mode the following has to be done: + * wait until DMA is ready + * wait on transmit shift register + * toggle RTS + * enable the receiver + */ + + /* Sleep until all sent */ + tty_wait_until_sent(tty, 0); +#ifdef CONFIG_ETRAX_FAST_TIMER + /* Now sleep a little more so that shift register is empty */ + schedule_usleep(info->char_time_usec * 2); +#endif + /* wait on transmit shift register */ + do{ + get_lsr_info(info, &val); + }while (!(val & TIOCSER_TEMT)); + + e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND)); + +#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER) + e100_enable_rx(info); + e100_enable_rxdma_irq(info); +#endif + } +#endif /* CONFIG_ETRAX_RS485 */ + + return count; +} /* rs_write */ + + +/* how much space is available in the xmit buffer? */ + +static int +rs_write_room(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +/* How many chars are in the xmit buffer? + * This does not include any chars in the transmitter FIFO. + * Use wait_until_sent for waiting for FIFO drain. + */ + +static int +rs_chars_in_buffer(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +/* discard everything in the xmit buffer */ + +static void +rs_flush_buffer(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + local_irq_save(flags); + info->xmit.head = info->xmit.tail = 0; + local_irq_restore(flags); + + tty_wakeup(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + * + * Since we use DMA we don't check for info->x_char in transmit_chars_dma(), + * but we do it in handle_ser_tx_interrupt(). + * We disable DMA channel and enable tx ready interrupt and write the + * character when possible. + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + local_irq_save(flags); + if (info->uses_dma_out) { + /* Put the DMA on hold and disable the channel */ + *info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold); + while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) != + IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold)); + e100_disable_txdma_channel(info); + } + + /* Must make sure transmitter is not stopped before we can transmit */ + if (tty->stopped) + rs_start(tty); + + /* Enable manual transmit interrupt and send from there */ + DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch)); + info->x_char = ch; + e100_enable_serial_tx_ready_irq(info); + local_irq_restore(flags); +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void +rs_throttle(struct tty_struct * tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %lu....\n", tty_name(tty, buf), + (unsigned long)tty->ldisc.chars_in_buffer(tty)); +#endif + DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty))); + + /* Do RTS before XOFF since XOFF might take some time */ + if (tty->termios->c_cflag & CRTSCTS) { + /* Turn off RTS line */ + e100_rts(info, 0); + } + if (I_IXOFF(tty)) + rs_send_xchar(tty, STOP_CHAR(tty)); + +} + +static void +rs_unthrottle(struct tty_struct * tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %lu....\n", tty_name(tty, buf), + (unsigned long)tty->ldisc.chars_in_buffer(tty)); +#endif + DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty))); + DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count)); + /* Do RTS before XOFF since XOFF might take some time */ + if (tty->termios->c_cflag & CRTSCTS) { + /* Assert RTS line */ + e100_rts(info, 1); + } + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + rs_send_xchar(tty, START_CHAR(tty)); + } + +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int +get_serial_info(struct e100_serial * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + /* this is all probably wrong, there are a lot of fields + * here that we don't have in e100_serial and maybe we + * should set them to something else than 0. + */ + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = (int)info->ioport; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.baud_base = info->baud_base; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + tmp.custom_divisor = info->custom_divisor; + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int +set_serial_info(struct e100_serial *info, + struct serial_struct *new_info) +{ + struct serial_struct new_serial; + struct e100_serial old_info; + int retval = 0; + + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + old_info = *info; + + if (!capable(CAP_SYS_ADMIN)) { + if ((new_serial.type != info->type) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (info->flags & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + goto check_and_exit; + } + + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->baud_base = new_serial.baud_base; + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->custom_divisor = new_serial.custom_divisor; + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; + info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + check_and_exit: + if (info->flags & ASYNC_INITIALIZED) { + change_speed(info); + } else + retval = startup(info); + return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int +get_lsr_info(struct e100_serial * info, unsigned int *value) +{ + unsigned int result = TIOCSER_TEMT; +#ifndef CONFIG_SVINTO_SIM + unsigned long curr_time = jiffies; + unsigned long curr_time_usec = GET_JIFFIES_USEC(); + unsigned long elapsed_usec = + (curr_time - info->last_tx_active) * 1000000/HZ + + curr_time_usec - info->last_tx_active_usec; + + if (info->xmit.head != info->xmit.tail || + elapsed_usec < 2*info->char_time_usec) { + result = 0; + } +#endif + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +#ifdef SERIAL_DEBUG_IO +struct state_str +{ + int state; + const char *str; +}; + +const struct state_str control_state_str[] = { + {TIOCM_DTR, "DTR" }, + {TIOCM_RTS, "RTS"}, + {TIOCM_ST, "ST?" }, + {TIOCM_SR, "SR?" }, + {TIOCM_CTS, "CTS" }, + {TIOCM_CD, "CD" }, + {TIOCM_RI, "RI" }, + {TIOCM_DSR, "DSR" }, + {0, NULL } +}; + +char *get_control_state_str(int MLines, char *s) +{ + int i = 0; + + s[0]='\0'; + while (control_state_str[i].str != NULL) { + if (MLines & control_state_str[i].state) { + if (s[0] != '\0') { + strcat(s, ", "); + } + strcat(s, control_state_str[i].str); + } + i++; + } + return s; +} +#endif + +static int +rs_break(struct tty_struct *tty, int break_state) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + if (!info->ioport) + return -EIO; + + local_irq_save(flags); + if (break_state == -1) { + /* Go to manual mode and set the txd pin to 0 */ + /* Clear bit 7 (txd) and 6 (tr_enable) */ + info->tx_ctrl &= 0x3F; + } else { + /* Set bit 7 (txd) and 6 (tr_enable) */ + info->tx_ctrl |= (0x80 | 0x40); + } + info->ioport[REG_TR_CTRL] = info->tx_ctrl; + local_irq_restore(flags); + return 0; +} + +static int +rs_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + local_irq_save(flags); + + if (clear & TIOCM_RTS) + e100_rts(info, 0); + if (clear & TIOCM_DTR) + e100_dtr(info, 0); + /* Handle FEMALE behaviour */ + if (clear & TIOCM_RI) + e100_ri_out(info, 0); + if (clear & TIOCM_CD) + e100_cd_out(info, 0); + + if (set & TIOCM_RTS) + e100_rts(info, 1); + if (set & TIOCM_DTR) + e100_dtr(info, 1); + /* Handle FEMALE behaviour */ + if (set & TIOCM_RI) + e100_ri_out(info, 1); + if (set & TIOCM_CD) + e100_cd_out(info, 1); + + local_irq_restore(flags); + return 0; +} + +static int +rs_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned int result; + unsigned long flags; + + local_irq_save(flags); + + result = + (!E100_RTS_GET(info) ? TIOCM_RTS : 0) + | (!E100_DTR_GET(info) ? TIOCM_DTR : 0) + | (!E100_RI_GET(info) ? TIOCM_RNG : 0) + | (!E100_DSR_GET(info) ? TIOCM_DSR : 0) + | (!E100_CD_GET(info) ? TIOCM_CAR : 0) + | (!E100_CTS_GET(info) ? TIOCM_CTS : 0); + + local_irq_restore(flags); + +#ifdef SERIAL_DEBUG_IO + printk(KERN_DEBUG "ser%i: modem state: %i 0x%08X\n", + info->line, result, result); + { + char s[100]; + + get_control_state_str(result, s); + printk(KERN_DEBUG "state: %s\n", s); + } +#endif + return result; + +} + + +static int +rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct e100_serial *) arg, + info, sizeof(struct e100_serial))) + return -EFAULT; + return 0; + +#if defined(CONFIG_ETRAX_RS485) + case TIOCSERSETRS485: + { + /* In this ioctl we still use the old structure + * rs485_control for backward compatibility + * (if we use serial_rs485, then old user-level code + * wouldn't work anymore...). + * The use of this ioctl is deprecated: use TIOCSRS485 + * instead.*/ + struct rs485_control rs485ctrl; + struct serial_rs485 rs485data; + printk(KERN_DEBUG "The use of this ioctl is deprecated. Use TIOCSRS485 instead\n"); + if (copy_from_user(&rs485ctrl, (struct rs485_control *)arg, + sizeof(rs485ctrl))) + return -EFAULT; + + rs485data.delay_rts_before_send = rs485ctrl.delay_rts_before_send; + rs485data.flags = 0; + if (rs485data.delay_rts_before_send != 0) + rs485data.flags |= SER_RS485_RTS_BEFORE_SEND; + else + rs485data.flags &= ~(SER_RS485_RTS_BEFORE_SEND); + + if (rs485ctrl.enabled) + rs485data.flags |= SER_RS485_ENABLED; + else + rs485data.flags &= ~(SER_RS485_ENABLED); + + if (rs485ctrl.rts_on_send) + rs485data.flags |= SER_RS485_RTS_ON_SEND; + else + rs485data.flags &= ~(SER_RS485_RTS_ON_SEND); + + if (rs485ctrl.rts_after_sent) + rs485data.flags |= SER_RS485_RTS_AFTER_SEND; + else + rs485data.flags &= ~(SER_RS485_RTS_AFTER_SEND); + + return e100_enable_rs485(tty, &rs485data); + } + + case TIOCSRS485: + { + /* This is the new version of TIOCSRS485, with new + * data structure serial_rs485 */ + struct serial_rs485 rs485data; + if (copy_from_user(&rs485data, (struct rs485_control *)arg, + sizeof(rs485data))) + return -EFAULT; + + return e100_enable_rs485(tty, &rs485data); + } + + case TIOCGRS485: + { + struct serial_rs485 *rs485data = + &(((struct e100_serial *)tty->driver_data)->rs485); + /* This is the ioctl to get RS485 data from user-space */ + if (copy_to_user((struct serial_rs485 *) arg, + rs485data, + sizeof(struct serial_rs485))) + return -EFAULT; + break; + } + + case TIOCSERWRRS485: + { + struct rs485_write rs485wr; + if (copy_from_user(&rs485wr, (struct rs485_write *)arg, + sizeof(rs485wr))) + return -EFAULT; + + return e100_write_rs485(tty, rs485wr.outc, rs485wr.outc_size); + } +#endif + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void +rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + change_speed(info); + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * S structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void +rs_close(struct tty_struct *tty, struct file * filp) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + if (!info) + return; + + /* interrupts are disabled for this entire function */ + + local_irq_save(flags); + + if (tty_hung_up_p(filp)) { + local_irq_restore(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, + info->line, info->count); +#endif + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk(KERN_CRIT + "rs_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + local_irq_restore(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the serial receiver and the DMA receive interrupt. + */ +#ifdef SERIAL_HANDLE_EARLY_ERRORS + e100_disable_serial_data_irq(info); +#endif + +#ifndef CONFIG_SVINTO_SIM + e100_disable_rx(info); + e100_disable_rx_irq(info); + + if (info->flags & ASYNC_INITIALIZED) { + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important as we have a transmit FIFO! + */ + rs_wait_until_sent(tty, HZ); + } +#endif + + shutdown(info); + rs_flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; + info->event = 0; + info->port.tty = NULL; + if (info->blocked_open) { + if (info->close_delay) + schedule_timeout_interruptible(info->close_delay); + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + local_irq_restore(flags); + + /* port closed */ + +#if defined(CONFIG_ETRAX_RS485) + if (info->rs485.flags & SER_RS485_ENABLED) { + info->rs485.flags &= ~(SER_RS485_ENABLED); +#if defined(CONFIG_ETRAX_RS485_ON_PA) + *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit); +#endif +#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, + rs485_port_g_bit, 0); +#endif +#if defined(CONFIG_ETRAX_RS485_LTC1387) + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, + CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 0); + REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, + CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 0); +#endif + } +#endif + + /* + * Release any allocated DMA irq's. + */ + if (info->dma_in_enabled) { + free_irq(info->dma_in_irq_nbr, info); + cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description); + info->uses_dma_in = 0; +#ifdef SERIAL_DEBUG_OPEN + printk(KERN_DEBUG "DMA irq '%s' freed\n", + info->dma_in_irq_description); +#endif + } + if (info->dma_out_enabled) { + free_irq(info->dma_out_irq_nbr, info); + cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description); + info->uses_dma_out = 0; +#ifdef SERIAL_DEBUG_OPEN + printk(KERN_DEBUG "DMA irq '%s' freed\n", + info->dma_out_irq_description); +#endif + } +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + unsigned long orig_jiffies; + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long curr_time = jiffies; + unsigned long curr_time_usec = GET_JIFFIES_USEC(); + long elapsed_usec = + (curr_time - info->last_tx_active) * (1000000/HZ) + + curr_time_usec - info->last_tx_active_usec; + + /* + * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO + * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) + */ + orig_jiffies = jiffies; + while (info->xmit.head != info->xmit.tail || /* More in send queue */ + (*info->ostatusadr & 0x007f) || /* more in FIFO */ + (elapsed_usec < 2*info->char_time_usec)) { + schedule_timeout_interruptible(1); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + curr_time = jiffies; + curr_time_usec = GET_JIFFIES_USEC(); + elapsed_usec = + (curr_time - info->last_tx_active) * (1000000/HZ) + + curr_time_usec - info->last_tx_active_usec; + } + set_current_state(TASK_RUNNING); +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void +rs_hangup(struct tty_struct *tty) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct e100_serial *info) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int retval; + int do_clocal = 0, extra_count = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(info->close_wait, + !(info->flags & ASYNC_CLOSING)); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (tty->termios->c_cflag & CLOCAL) { + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttyS%d, count = %d\n", + info->line, info->count); +#endif + local_irq_save(flags); + if (!tty_hung_up_p(filp)) { + extra_count++; + info->count--; + } + local_irq_restore(flags); + info->blocked_open++; + while (1) { + local_irq_save(flags); + /* assert RTS and DTR */ + e100_rts(info, 1); + e100_dtr(info, 1); + local_irq_restore(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CLOSING) && do_clocal) + /* && (do_clocal || DCD_IS_ASSERTED) */ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttyS%d, count = %d\n", + info->line, info->count); +#endif + tty_unlock(); + schedule(); + tty_lock(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + info->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttyS%d, count = %d\n", + info->line, info->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static void +deinit_port(struct e100_serial *info) +{ + if (info->dma_out_enabled) { + cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description); + free_irq(info->dma_out_irq_nbr, info); + } + if (info->dma_in_enabled) { + cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description); + free_irq(info->dma_in_irq_nbr, info); + } +} + +/* + * This routine is called whenever a serial port is opened. + * It performs the serial-specific initialization for the tty structure. + */ +static int +rs_open(struct tty_struct *tty, struct file * filp) +{ + struct e100_serial *info; + int retval, line; + unsigned long page; + int allocated_resources = 0; + + /* find which port we want to open */ + line = tty->index; + + if (line < 0 || line >= NR_PORTS) + return -ENODEV; + + /* find the corresponding e100_serial struct in the table */ + info = rs_table + line; + + /* don't allow the opening of ports that are not enabled in the HW config */ + if (!info->enabled) + return -ENODEV; + +#ifdef SERIAL_DEBUG_OPEN + printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name, + info->count); +#endif + + info->count++; + tty->driver_data = info; + info->port.tty = tty; + + info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + /* + * If the port is in the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + wait_event_interruptible_tty(info->close_wait, + !(info->flags & ASYNC_CLOSING)); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * If DMA is enabled try to allocate the irq's. + */ + if (info->count == 1) { + allocated_resources = 1; + if (info->dma_in_enabled) { + if (request_irq(info->dma_in_irq_nbr, + rec_interrupt, + info->dma_in_irq_flags, + info->dma_in_irq_description, + info)) { + printk(KERN_WARNING "DMA irq '%s' busy; " + "falling back to non-DMA mode\n", + info->dma_in_irq_description); + /* Make sure we never try to use DMA in */ + /* for the port again. */ + info->dma_in_enabled = 0; + } else if (cris_request_dma(info->dma_in_nbr, + info->dma_in_irq_description, + DMA_VERBOSE_ON_ERROR, + info->dma_owner)) { + free_irq(info->dma_in_irq_nbr, info); + printk(KERN_WARNING "DMA '%s' busy; " + "falling back to non-DMA mode\n", + info->dma_in_irq_description); + /* Make sure we never try to use DMA in */ + /* for the port again. */ + info->dma_in_enabled = 0; + } +#ifdef SERIAL_DEBUG_OPEN + else + printk(KERN_DEBUG "DMA irq '%s' allocated\n", + info->dma_in_irq_description); +#endif + } + if (info->dma_out_enabled) { + if (request_irq(info->dma_out_irq_nbr, + tr_interrupt, + info->dma_out_irq_flags, + info->dma_out_irq_description, + info)) { + printk(KERN_WARNING "DMA irq '%s' busy; " + "falling back to non-DMA mode\n", + info->dma_out_irq_description); + /* Make sure we never try to use DMA out */ + /* for the port again. */ + info->dma_out_enabled = 0; + } else if (cris_request_dma(info->dma_out_nbr, + info->dma_out_irq_description, + DMA_VERBOSE_ON_ERROR, + info->dma_owner)) { + free_irq(info->dma_out_irq_nbr, info); + printk(KERN_WARNING "DMA '%s' busy; " + "falling back to non-DMA mode\n", + info->dma_out_irq_description); + /* Make sure we never try to use DMA out */ + /* for the port again. */ + info->dma_out_enabled = 0; + } +#ifdef SERIAL_DEBUG_OPEN + else + printk(KERN_DEBUG "DMA irq '%s' allocated\n", + info->dma_out_irq_description); +#endif + } + } + + /* + * Start up the serial port + */ + + retval = startup(info); + if (retval) { + if (allocated_resources) + deinit_port(info); + + /* FIXME Decrease count info->count here too? */ + return retval; + } + + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + if (allocated_resources) + deinit_port(info); + + return retval; + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + *tty->termios = info->normal_termios; + change_speed(info); + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttyS%d successful...\n", info->line); +#endif + DLOG_INT_TRIG( log_int_pos = 0); + + DFLIP( if (info->line == SERIAL_DEBUG_LINE) { + info->icount.rx = 0; + } ); + + return 0; +} + +#ifdef CONFIG_PROC_FS +/* + * /proc fs routines.... + */ + +static void seq_line_info(struct seq_file *m, struct e100_serial *info) +{ + unsigned long tmp; + + seq_printf(m, "%d: uart:E100 port:%lX irq:%d", + info->line, (unsigned long)info->ioport, info->irq); + + if (!info->ioport || (info->type == PORT_UNKNOWN)) { + seq_printf(m, "\n"); + return; + } + + seq_printf(m, " baud:%d", info->baud); + seq_printf(m, " tx:%lu rx:%lu", + (unsigned long)info->icount.tx, + (unsigned long)info->icount.rx); + tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + if (tmp) + seq_printf(m, " tx_pend:%lu/%lu", + (unsigned long)tmp, + (unsigned long)SERIAL_XMIT_SIZE); + + seq_printf(m, " rx_pend:%lu/%lu", + (unsigned long)info->recv_cnt, + (unsigned long)info->max_recv_cnt); + +#if 1 + if (info->port.tty) { + if (info->port.tty->stopped) + seq_printf(m, " stopped:%i", + (int)info->port.tty->stopped); + if (info->port.tty->hw_stopped) + seq_printf(m, " hw_stopped:%i", + (int)info->port.tty->hw_stopped); + } + + { + unsigned char rstat = info->ioport[REG_STATUS]; + if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect)) + seq_printf(m, " xoff_detect:1"); + } + +#endif + + if (info->icount.frame) + seq_printf(m, " fe:%lu", (unsigned long)info->icount.frame); + + if (info->icount.parity) + seq_printf(m, " pe:%lu", (unsigned long)info->icount.parity); + + if (info->icount.brk) + seq_printf(m, " brk:%lu", (unsigned long)info->icount.brk); + + if (info->icount.overrun) + seq_printf(m, " oe:%lu", (unsigned long)info->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + if (!E100_RTS_GET(info)) + seq_puts(m, "|RTS"); + if (!E100_CTS_GET(info)) + seq_puts(m, "|CTS"); + if (!E100_DTR_GET(info)) + seq_puts(m, "|DTR"); + if (!E100_DSR_GET(info)) + seq_puts(m, "|DSR"); + if (!E100_CD_GET(info)) + seq_puts(m, "|CD"); + if (!E100_RI_GET(info)) + seq_puts(m, "|RI"); + seq_puts(m, "\n"); +} + + +static int crisv10_proc_show(struct seq_file *m, void *v) +{ + int i; + + seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version); + + for (i = 0; i < NR_PORTS; i++) { + if (!rs_table[i].enabled) + continue; + seq_line_info(m, &rs_table[i]); + } +#ifdef DEBUG_LOG_INCLUDED + for (i = 0; i < debug_log_pos; i++) { + seq_printf(m, "%-4i %lu.%lu ", + i, debug_log[i].time, + timer_data_to_ns(debug_log[i].timer_data)); + seq_printf(m, debug_log[i].string, debug_log[i].value); + } + seq_printf(m, "debug_log %i/%i\n", i, DEBUG_LOG_SIZE); + debug_log_pos = 0; +#endif + return 0; +} + +static int crisv10_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, crisv10_proc_show, NULL); +} + +static const struct file_operations crisv10_proc_fops = { + .owner = THIS_MODULE, + .open = crisv10_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + + +/* Finally, routines used to initialize the serial driver. */ + +static void show_serial_version(void) +{ + printk(KERN_INFO + "ETRAX 100LX serial-driver %s, " + "(c) 2000-2004 Axis Communications AB\r\n", + &serial_version[11]); /* "$Revision: x.yy" */ +} + +/* rs_init inits the driver at boot (using the module_init chain) */ + +static const struct tty_operations rs_ops = { + .open = rs_open, + .close = rs_close, + .write = rs_write, + .flush_chars = rs_flush_chars, + .write_room = rs_write_room, + .chars_in_buffer = rs_chars_in_buffer, + .flush_buffer = rs_flush_buffer, + .ioctl = rs_ioctl, + .throttle = rs_throttle, + .unthrottle = rs_unthrottle, + .set_termios = rs_set_termios, + .stop = rs_stop, + .start = rs_start, + .hangup = rs_hangup, + .break_ctl = rs_break, + .send_xchar = rs_send_xchar, + .wait_until_sent = rs_wait_until_sent, + .tiocmget = rs_tiocmget, + .tiocmset = rs_tiocmset, +#ifdef CONFIG_PROC_FS + .proc_fops = &crisv10_proc_fops, +#endif +}; + +static int __init rs_init(void) +{ + int i; + struct e100_serial *info; + struct tty_driver *driver = alloc_tty_driver(NR_PORTS); + + if (!driver) + return -ENOMEM; + + show_serial_version(); + + /* Setup the timed flush handler system */ + +#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) + setup_timer(&flush_timer, timed_flush_handler, 0); + mod_timer(&flush_timer, jiffies + 5); +#endif + +#if defined(CONFIG_ETRAX_RS485) +#if defined(CONFIG_ETRAX_RS485_ON_PA) + if (cris_io_interface_allocate_pins(if_ser0, 'a', rs485_pa_bit, + rs485_pa_bit)) { + printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " + "RS485 pin\n"); + put_tty_driver(driver); + return -EBUSY; + } +#endif +#if defined(CONFIG_ETRAX_RS485_ON_PORT_G) + if (cris_io_interface_allocate_pins(if_ser0, 'g', rs485_pa_bit, + rs485_port_g_bit)) { + printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " + "RS485 pin\n"); + put_tty_driver(driver); + return -EBUSY; + } +#endif +#endif + + /* Initialize the tty_driver structure */ + + driver->driver_name = "serial"; + driver->name = "ttyS"; + driver->major = TTY_MAJOR; + driver->minor_start = 64; + driver->type = TTY_DRIVER_TYPE_SERIAL; + driver->subtype = SERIAL_TYPE_NORMAL; + driver->init_termios = tty_std_termios; + driver->init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ + driver->init_termios.c_ispeed = 115200; + driver->init_termios.c_ospeed = 115200; + driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + + tty_set_operations(driver, &rs_ops); + serial_driver = driver; + if (tty_register_driver(driver)) + panic("Couldn't register serial driver\n"); + /* do some initializing for the separate ports */ + + for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { + if (info->enabled) { + if (cris_request_io_interface(info->io_if, + info->io_if_description)) { + printk(KERN_CRIT "ETRAX100LX async serial: " + "Could not allocate IO pins for " + "%s, port %d\n", + info->io_if_description, i); + info->enabled = 0; + } + } + info->uses_dma_in = 0; + info->uses_dma_out = 0; + info->line = i; + info->port.tty = NULL; + info->type = PORT_ETRAX; + info->tr_running = 0; + info->forced_eop = 0; + info->baud_base = DEF_BAUD_BASE; + info->custom_divisor = 0; + info->flags = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->normal_termios = driver->init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + info->xmit.buf = NULL; + info->xmit.tail = info->xmit.head = 0; + info->first_recv_buffer = info->last_recv_buffer = NULL; + info->recv_cnt = info->max_recv_cnt = 0; + info->last_tx_active_usec = 0; + info->last_tx_active = 0; + +#if defined(CONFIG_ETRAX_RS485) + /* Set sane defaults */ + info->rs485.flags &= ~(SER_RS485_RTS_ON_SEND); + info->rs485.flags |= SER_RS485_RTS_AFTER_SEND; + info->rs485.flags &= ~(SER_RS485_RTS_BEFORE_SEND); + info->rs485.delay_rts_before_send = 0; + info->rs485.flags &= ~(SER_RS485_ENABLED); +#endif + INIT_WORK(&info->work, do_softint); + + if (info->enabled) { + printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n", + serial_driver->name, info->line, info->ioport); + } + } +#ifdef CONFIG_ETRAX_FAST_TIMER +#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER + memset(fast_timers, 0, sizeof(fast_timers)); +#endif +#ifdef CONFIG_ETRAX_RS485 + memset(fast_timers_rs485, 0, sizeof(fast_timers_rs485)); +#endif + fast_timer_init(); +#endif + +#ifndef CONFIG_SVINTO_SIM +#ifndef CONFIG_ETRAX_KGDB + /* Not needed in simulator. May only complicate stuff. */ + /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */ + + if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, + IRQF_SHARED | IRQF_DISABLED, "serial ", driver)) + panic("%s: Failed to request irq8", __func__); + +#endif +#endif /* CONFIG_SVINTO_SIM */ + + return 0; +} + +/* this makes sure that rs_init is called during kernel boot */ + +module_init(rs_init); diff --git a/drivers/tty/serial/crisv10.h b/drivers/tty/serial/crisv10.h new file mode 100644 index 000000000000..ea0beb46a10d --- /dev/null +++ b/drivers/tty/serial/crisv10.h @@ -0,0 +1,147 @@ +/* + * serial.h: Arch-dep definitions for the Etrax100 serial driver. + * + * Copyright (C) 1998-2007 Axis Communications AB + */ + +#ifndef _ETRAX_SERIAL_H +#define _ETRAX_SERIAL_H + +#include +#include +#include +#include + +/* Software state per channel */ + +#ifdef __KERNEL__ +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * For definitions of the flags field, see tty.h + */ + +#define SERIAL_RECV_DESCRIPTORS 8 + +struct etrax_recv_buffer { + struct etrax_recv_buffer *next; + unsigned short length; + unsigned char error; + unsigned char pad; + + unsigned char buffer[0]; +}; + +struct e100_serial { + struct tty_port port; + int baud; + volatile u8 *ioport; /* R_SERIALx_CTRL */ + u32 irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ + + /* Output registers */ + volatile u8 *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ + volatile u32 *ofirstadr; /* adr to R_DMA_CHx_FIRST */ + volatile u8 *ocmdadr; /* adr to R_DMA_CHx_CMD */ + const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS */ + + /* Input registers */ + volatile u8 *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */ + volatile u32 *ifirstadr; /* adr to R_DMA_CHx_FIRST */ + volatile u8 *icmdadr; /* adr to R_DMA_CHx_CMD */ + volatile u32 *idescradr; /* adr to R_DMA_CHx_DESCR */ + + int flags; /* defined in tty.h */ + + u8 rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ + u8 tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ + u8 iseteop; /* bit number for R_SET_EOP for the input dma */ + int enabled; /* Set to 1 if the port is enabled in HW config */ + + u8 dma_out_enabled; /* Set to 1 if DMA should be used */ + u8 dma_in_enabled; /* Set to 1 if DMA should be used */ + + /* end of fields defined in rs_table[] in .c-file */ + int dma_owner; + unsigned int dma_in_nbr; + unsigned int dma_out_nbr; + unsigned int dma_in_irq_nbr; + unsigned int dma_out_irq_nbr; + unsigned long dma_in_irq_flags; + unsigned long dma_out_irq_flags; + char *dma_in_irq_description; + char *dma_out_irq_description; + + enum cris_io_interface io_if; + char *io_if_description; + + u8 uses_dma_in; /* Set to 1 if DMA is used */ + u8 uses_dma_out; /* Set to 1 if DMA is used */ + u8 forced_eop; /* a fifo eop has been forced */ + int baud_base; /* For special baudrates */ + int custom_divisor; /* For special baudrates */ + struct etrax_dma_descr tr_descr; + struct etrax_dma_descr rec_descr[SERIAL_RECV_DESCRIPTORS]; + int cur_rec_descr; + + volatile int tr_running; /* 1 if output is running */ + + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int line; + int type; /* PORT_ETRAX */ + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + struct circ_buf xmit; + struct etrax_recv_buffer *first_recv_buffer; + struct etrax_recv_buffer *last_recv_buffer; + unsigned int recv_cnt; + unsigned int max_recv_cnt; + + struct work_struct work; + struct async_icount icount; /* error-statistics etc.*/ + struct ktermios normal_termios; + struct ktermios callout_termios; + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; + + unsigned long char_time_usec; /* The time for 1 char, in usecs */ + unsigned long flush_time_usec; /* How often we should flush */ + unsigned long last_tx_active_usec; /* Last tx usec in the jiffies */ + unsigned long last_tx_active; /* Last tx time in jiffies */ + unsigned long last_rx_active_usec; /* Last rx usec in the jiffies */ + unsigned long last_rx_active; /* Last rx time in jiffies */ + + int break_detected_cnt; + int errorcode; + +#ifdef CONFIG_ETRAX_RS485 + struct serial_rs485 rs485; /* RS-485 support */ +#endif +}; + +/* this PORT is not in the standard serial.h. it's not actually used for + * anything since we only have one type of async serial-port anyway in this + * system. + */ + +#define PORT_ETRAX 1 + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define RS_EVENT_WRITE_WAKEUP 0 + +#endif /* __KERNEL__ */ + +#endif /* !_ETRAX_SERIAL_H */ diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c new file mode 100644 index 000000000000..57421d776329 --- /dev/null +++ b/drivers/tty/serial/dz.c @@ -0,0 +1,955 @@ +/* + * dz.c: Serial port driver for DECstations equipped + * with the DZ chipset. + * + * Copyright (C) 1998 Olivier A. D. Lebaillif + * + * Email: olivier.lebaillif@ifrsys.com + * + * Copyright (C) 2004, 2006, 2007 Maciej W. Rozycki + * + * [31-AUG-98] triemer + * Changed IRQ to use Harald's dec internals interrupts.h + * removed base_addr code - moving address assignment to setup.c + * Changed name of dz_init to rs_init to be consistent with tc code + * [13-NOV-98] triemer fixed code to receive characters + * after patches by harald to irq code. + * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout + * field from "current" - somewhere between 2.1.121 and 2.1.131 + Qua Jun 27 15:02:26 BRT 2001 + * [27-JUN-2001] Arnaldo Carvalho de Melo - cleanups + * + * Parts (C) 1999 David Airlie, airlied@linux.ie + * [07-SEP-99] Bugfixes + * + * [06-Jan-2002] Russell King + * Converted to new serial core + */ + +#undef DEBUG_DZ + +#if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#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 + +#include "dz.h" + + +MODULE_DESCRIPTION("DECstation DZ serial driver"); +MODULE_LICENSE("GPL"); + + +static char dz_name[] __initdata = "DECstation DZ serial driver version "; +static char dz_version[] __initdata = "1.04"; + +struct dz_port { + struct dz_mux *mux; + struct uart_port port; + unsigned int cflag; +}; + +struct dz_mux { + struct dz_port dport[DZ_NB_PORT]; + atomic_t map_guard; + atomic_t irq_guard; + int initialised; +}; + +static struct dz_mux dz_mux; + +static inline struct dz_port *to_dport(struct uart_port *uport) +{ + return container_of(uport, struct dz_port, port); +} + +/* + * ------------------------------------------------------------ + * dz_in () and dz_out () + * + * These routines are used to access the registers of the DZ + * chip, hiding relocation differences between implementation. + * ------------------------------------------------------------ + */ + +static u16 dz_in(struct dz_port *dport, unsigned offset) +{ + void __iomem *addr = dport->port.membase + offset; + + return readw(addr); +} + +static void dz_out(struct dz_port *dport, unsigned offset, u16 value) +{ + void __iomem *addr = dport->port.membase + offset; + + writew(value, addr); +} + +/* + * ------------------------------------------------------------ + * rs_stop () and rs_start () + * + * These routines are called before setting or resetting + * tty->stopped. They enable or disable transmitter interrupts, + * as necessary. + * ------------------------------------------------------------ + */ + +static void dz_stop_tx(struct uart_port *uport) +{ + struct dz_port *dport = to_dport(uport); + u16 tmp, mask = 1 << dport->port.line; + + tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ + tmp &= ~mask; /* clear the TX flag */ + dz_out(dport, DZ_TCR, tmp); +} + +static void dz_start_tx(struct uart_port *uport) +{ + struct dz_port *dport = to_dport(uport); + u16 tmp, mask = 1 << dport->port.line; + + tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ + tmp |= mask; /* set the TX flag */ + dz_out(dport, DZ_TCR, tmp); +} + +static void dz_stop_rx(struct uart_port *uport) +{ + struct dz_port *dport = to_dport(uport); + + dport->cflag &= ~DZ_RXENAB; + dz_out(dport, DZ_LPR, dport->cflag); +} + +static void dz_enable_ms(struct uart_port *uport) +{ + /* nothing to do */ +} + +/* + * ------------------------------------------------------------ + * + * Here start the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * dz_interrupt. They were separated out for readability's sake. + * + * Note: dz_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * dz_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * make drivers/serial/dz.s + * + * and look at the resulting assemble code in dz.s. + * + * ------------------------------------------------------------ + */ + +/* + * ------------------------------------------------------------ + * receive_char () + * + * This routine deals with inputs from any lines. + * ------------------------------------------------------------ + */ +static inline void dz_receive_chars(struct dz_mux *mux) +{ + struct uart_port *uport; + struct dz_port *dport = &mux->dport[0]; + struct tty_struct *tty = NULL; + struct uart_icount *icount; + int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; + unsigned char ch, flag; + u16 status; + int i; + + while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) { + dport = &mux->dport[LINE(status)]; + uport = &dport->port; + tty = uport->state->port.tty; /* point to the proper dev */ + + ch = UCHAR(status); /* grab the char */ + flag = TTY_NORMAL; + + icount = &uport->icount; + icount->rx++; + + if (unlikely(status & (DZ_OERR | DZ_FERR | DZ_PERR))) { + + /* + * There is no separate BREAK status bit, so treat + * null characters with framing errors as BREAKs; + * normally, otherwise. For this move the Framing + * Error bit to a simulated BREAK bit. + */ + if (!ch) { + status |= (status & DZ_FERR) >> + (ffs(DZ_FERR) - ffs(DZ_BREAK)); + status &= ~DZ_FERR; + } + + /* Handle SysRq/SAK & keep track of the statistics. */ + if (status & DZ_BREAK) { + icount->brk++; + if (uart_handle_break(uport)) + continue; + } else if (status & DZ_FERR) + icount->frame++; + else if (status & DZ_PERR) + icount->parity++; + if (status & DZ_OERR) + icount->overrun++; + + status &= uport->read_status_mask; + if (status & DZ_BREAK) + flag = TTY_BREAK; + else if (status & DZ_FERR) + flag = TTY_FRAME; + else if (status & DZ_PERR) + flag = TTY_PARITY; + + } + + if (uart_handle_sysrq_char(uport, ch)) + continue; + + uart_insert_char(uport, status, DZ_OERR, ch, flag); + lines_rx[LINE(status)] = 1; + } + for (i = 0; i < DZ_NB_PORT; i++) + if (lines_rx[i]) + tty_flip_buffer_push(mux->dport[i].port.state->port.tty); +} + +/* + * ------------------------------------------------------------ + * transmit_char () + * + * This routine deals with outputs to any lines. + * ------------------------------------------------------------ + */ +static inline void dz_transmit_chars(struct dz_mux *mux) +{ + struct dz_port *dport = &mux->dport[0]; + struct circ_buf *xmit; + unsigned char tmp; + u16 status; + + status = dz_in(dport, DZ_CSR); + dport = &mux->dport[LINE(status)]; + xmit = &dport->port.state->xmit; + + if (dport->port.x_char) { /* XON/XOFF chars */ + dz_out(dport, DZ_TDR, dport->port.x_char); + dport->port.icount.tx++; + dport->port.x_char = 0; + return; + } + /* If nothing to do or stopped or hardware stopped. */ + if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { + spin_lock(&dport->port.lock); + dz_stop_tx(&dport->port); + spin_unlock(&dport->port.lock); + return; + } + + /* + * If something to do... (remember the dz has no output fifo, + * so we go one char at a time) :-< + */ + tmp = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); + dz_out(dport, DZ_TDR, tmp); + dport->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) + uart_write_wakeup(&dport->port); + + /* Are we are done. */ + if (uart_circ_empty(xmit)) { + spin_lock(&dport->port.lock); + dz_stop_tx(&dport->port); + spin_unlock(&dport->port.lock); + } +} + +/* + * ------------------------------------------------------------ + * check_modem_status() + * + * DS 3100 & 5100: Only valid for the MODEM line, duh! + * DS 5000/200: Valid for the MODEM and PRINTER line. + * ------------------------------------------------------------ + */ +static inline void check_modem_status(struct dz_port *dport) +{ + /* + * FIXME: + * 1. No status change interrupt; use a timer. + * 2. Handle the 3100/5000 as appropriate. --macro + */ + u16 status; + + /* If not the modem line just return. */ + if (dport->port.line != DZ_MODEM) + return; + + status = dz_in(dport, DZ_MSR); + + /* it's easy, since DSR2 is the only bit in the register */ + if (status) + dport->port.icount.dsr++; +} + +/* + * ------------------------------------------------------------ + * dz_interrupt () + * + * this is the main interrupt routine for the DZ chip. + * It deals with the multiple ports. + * ------------------------------------------------------------ + */ +static irqreturn_t dz_interrupt(int irq, void *dev_id) +{ + struct dz_mux *mux = dev_id; + struct dz_port *dport = &mux->dport[0]; + u16 status; + + /* get the reason why we just got an irq */ + status = dz_in(dport, DZ_CSR); + + if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) + dz_receive_chars(mux); + + if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) + dz_transmit_chars(mux); + + return IRQ_HANDLED; +} + +/* + * ------------------------------------------------------------------- + * Here ends the DZ interrupt routines. + * ------------------------------------------------------------------- + */ + +static unsigned int dz_get_mctrl(struct uart_port *uport) +{ + /* + * FIXME: Handle the 3100/5000 as appropriate. --macro + */ + struct dz_port *dport = to_dport(uport); + unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; + + if (dport->port.line == DZ_MODEM) { + if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) + mctrl &= ~TIOCM_DSR; + } + + return mctrl; +} + +static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) +{ + /* + * FIXME: Handle the 3100/5000 as appropriate. --macro + */ + struct dz_port *dport = to_dport(uport); + u16 tmp; + + if (dport->port.line == DZ_MODEM) { + tmp = dz_in(dport, DZ_TCR); + if (mctrl & TIOCM_DTR) + tmp &= ~DZ_MODEM_DTR; + else + tmp |= DZ_MODEM_DTR; + dz_out(dport, DZ_TCR, tmp); + } +} + +/* + * ------------------------------------------------------------------- + * startup () + * + * various initialization tasks + * ------------------------------------------------------------------- + */ +static int dz_startup(struct uart_port *uport) +{ + struct dz_port *dport = to_dport(uport); + struct dz_mux *mux = dport->mux; + unsigned long flags; + int irq_guard; + int ret; + u16 tmp; + + irq_guard = atomic_add_return(1, &mux->irq_guard); + if (irq_guard != 1) + return 0; + + ret = request_irq(dport->port.irq, dz_interrupt, + IRQF_SHARED, "dz", mux); + if (ret) { + atomic_add(-1, &mux->irq_guard); + printk(KERN_ERR "dz: Cannot get IRQ %d!\n", dport->port.irq); + return ret; + } + + spin_lock_irqsave(&dport->port.lock, flags); + + /* Enable interrupts. */ + tmp = dz_in(dport, DZ_CSR); + tmp |= DZ_RIE | DZ_TIE; + dz_out(dport, DZ_CSR, tmp); + + spin_unlock_irqrestore(&dport->port.lock, flags); + + return 0; +} + +/* + * ------------------------------------------------------------------- + * shutdown () + * + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + * ------------------------------------------------------------------- + */ +static void dz_shutdown(struct uart_port *uport) +{ + struct dz_port *dport = to_dport(uport); + struct dz_mux *mux = dport->mux; + unsigned long flags; + int irq_guard; + u16 tmp; + + spin_lock_irqsave(&dport->port.lock, flags); + dz_stop_tx(&dport->port); + spin_unlock_irqrestore(&dport->port.lock, flags); + + irq_guard = atomic_add_return(-1, &mux->irq_guard); + if (!irq_guard) { + /* Disable interrupts. */ + tmp = dz_in(dport, DZ_CSR); + tmp &= ~(DZ_RIE | DZ_TIE); + dz_out(dport, DZ_CSR, tmp); + + free_irq(dport->port.irq, mux); + } +} + +/* + * ------------------------------------------------------------------- + * dz_tx_empty() -- get the transmitter empty status + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + * ------------------------------------------------------------------- + */ +static unsigned int dz_tx_empty(struct uart_port *uport) +{ + struct dz_port *dport = to_dport(uport); + unsigned short tmp, mask = 1 << dport->port.line; + + tmp = dz_in(dport, DZ_TCR); + tmp &= mask; + + return tmp ? 0 : TIOCSER_TEMT; +} + +static void dz_break_ctl(struct uart_port *uport, int break_state) +{ + /* + * FIXME: Can't access BREAK bits in TDR easily; + * reuse the code for polled TX. --macro + */ + struct dz_port *dport = to_dport(uport); + unsigned long flags; + unsigned short tmp, mask = 1 << dport->port.line; + + spin_lock_irqsave(&uport->lock, flags); + tmp = dz_in(dport, DZ_TCR); + if (break_state) + tmp |= mask; + else + tmp &= ~mask; + dz_out(dport, DZ_TCR, tmp); + spin_unlock_irqrestore(&uport->lock, flags); +} + +static int dz_encode_baud_rate(unsigned int baud) +{ + switch (baud) { + case 50: + return DZ_B50; + case 75: + return DZ_B75; + case 110: + return DZ_B110; + case 134: + return DZ_B134; + case 150: + return DZ_B150; + case 300: + return DZ_B300; + case 600: + return DZ_B600; + case 1200: + return DZ_B1200; + case 1800: + return DZ_B1800; + case 2000: + return DZ_B2000; + case 2400: + return DZ_B2400; + case 3600: + return DZ_B3600; + case 4800: + return DZ_B4800; + case 7200: + return DZ_B7200; + case 9600: + return DZ_B9600; + default: + return -1; + } +} + + +static void dz_reset(struct dz_port *dport) +{ + struct dz_mux *mux = dport->mux; + + if (mux->initialised) + return; + + dz_out(dport, DZ_CSR, DZ_CLR); + while (dz_in(dport, DZ_CSR) & DZ_CLR); + iob(); + + /* Enable scanning. */ + dz_out(dport, DZ_CSR, DZ_MSE); + + mux->initialised = 1; +} + +static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, + struct ktermios *old_termios) +{ + struct dz_port *dport = to_dport(uport); + unsigned long flags; + unsigned int cflag, baud; + int bflag; + + cflag = dport->port.line; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cflag |= DZ_CS5; + break; + case CS6: + cflag |= DZ_CS6; + break; + case CS7: + cflag |= DZ_CS7; + break; + case CS8: + default: + cflag |= DZ_CS8; + } + + if (termios->c_cflag & CSTOPB) + cflag |= DZ_CSTOPB; + if (termios->c_cflag & PARENB) + cflag |= DZ_PARENB; + if (termios->c_cflag & PARODD) + cflag |= DZ_PARODD; + + baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600); + bflag = dz_encode_baud_rate(baud); + if (bflag < 0) { /* Try to keep unchanged. */ + baud = uart_get_baud_rate(uport, old_termios, NULL, 50, 9600); + bflag = dz_encode_baud_rate(baud); + if (bflag < 0) { /* Resort to 9600. */ + baud = 9600; + bflag = DZ_B9600; + } + tty_termios_encode_baud_rate(termios, baud, baud); + } + cflag |= bflag; + + if (termios->c_cflag & CREAD) + cflag |= DZ_RXENAB; + + spin_lock_irqsave(&dport->port.lock, flags); + + uart_update_timeout(uport, termios->c_cflag, baud); + + dz_out(dport, DZ_LPR, cflag); + dport->cflag = cflag; + + /* setup accept flag */ + dport->port.read_status_mask = DZ_OERR; + if (termios->c_iflag & INPCK) + dport->port.read_status_mask |= DZ_FERR | DZ_PERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + dport->port.read_status_mask |= DZ_BREAK; + + /* characters to ignore */ + uport->ignore_status_mask = 0; + if ((termios->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) + dport->port.ignore_status_mask |= DZ_OERR; + if (termios->c_iflag & IGNPAR) + dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR; + if (termios->c_iflag & IGNBRK) + dport->port.ignore_status_mask |= DZ_BREAK; + + spin_unlock_irqrestore(&dport->port.lock, flags); +} + +/* + * Hack alert! + * Required solely so that the initial PROM-based console + * works undisturbed in parallel with this one. + */ +static void dz_pm(struct uart_port *uport, unsigned int state, + unsigned int oldstate) +{ + struct dz_port *dport = to_dport(uport); + unsigned long flags; + + spin_lock_irqsave(&dport->port.lock, flags); + if (state < 3) + dz_start_tx(&dport->port); + else + dz_stop_tx(&dport->port); + spin_unlock_irqrestore(&dport->port.lock, flags); +} + + +static const char *dz_type(struct uart_port *uport) +{ + return "DZ"; +} + +static void dz_release_port(struct uart_port *uport) +{ + struct dz_mux *mux = to_dport(uport)->mux; + int map_guard; + + iounmap(uport->membase); + uport->membase = NULL; + + map_guard = atomic_add_return(-1, &mux->map_guard); + if (!map_guard) + release_mem_region(uport->mapbase, dec_kn_slot_size); +} + +static int dz_map_port(struct uart_port *uport) +{ + if (!uport->membase) + uport->membase = ioremap_nocache(uport->mapbase, + dec_kn_slot_size); + if (!uport->membase) { + printk(KERN_ERR "dz: Cannot map MMIO\n"); + return -ENOMEM; + } + return 0; +} + +static int dz_request_port(struct uart_port *uport) +{ + struct dz_mux *mux = to_dport(uport)->mux; + int map_guard; + int ret; + + map_guard = atomic_add_return(1, &mux->map_guard); + if (map_guard == 1) { + if (!request_mem_region(uport->mapbase, dec_kn_slot_size, + "dz")) { + atomic_add(-1, &mux->map_guard); + printk(KERN_ERR + "dz: Unable to reserve MMIO resource\n"); + return -EBUSY; + } + } + ret = dz_map_port(uport); + if (ret) { + map_guard = atomic_add_return(-1, &mux->map_guard); + if (!map_guard) + release_mem_region(uport->mapbase, dec_kn_slot_size); + return ret; + } + return 0; +} + +static void dz_config_port(struct uart_port *uport, int flags) +{ + struct dz_port *dport = to_dport(uport); + + if (flags & UART_CONFIG_TYPE) { + if (dz_request_port(uport)) + return; + + uport->type = PORT_DZ; + + dz_reset(dport); + } +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + */ +static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ) + ret = -EINVAL; + if (ser->irq != uport->irq) + ret = -EINVAL; + return ret; +} + +static struct uart_ops dz_ops = { + .tx_empty = dz_tx_empty, + .get_mctrl = dz_get_mctrl, + .set_mctrl = dz_set_mctrl, + .stop_tx = dz_stop_tx, + .start_tx = dz_start_tx, + .stop_rx = dz_stop_rx, + .enable_ms = dz_enable_ms, + .break_ctl = dz_break_ctl, + .startup = dz_startup, + .shutdown = dz_shutdown, + .set_termios = dz_set_termios, + .pm = dz_pm, + .type = dz_type, + .release_port = dz_release_port, + .request_port = dz_request_port, + .config_port = dz_config_port, + .verify_port = dz_verify_port, +}; + +static void __init dz_init_ports(void) +{ + static int first = 1; + unsigned long base; + int line; + + if (!first) + return; + first = 0; + + if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) + base = dec_kn_slot_base + KN01_DZ11; + else + base = dec_kn_slot_base + KN02_DZ11; + + for (line = 0; line < DZ_NB_PORT; line++) { + struct dz_port *dport = &dz_mux.dport[line]; + struct uart_port *uport = &dport->port; + + dport->mux = &dz_mux; + + uport->irq = dec_interrupt[DEC_IRQ_DZ11]; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &dz_ops; + uport->line = line; + uport->mapbase = base; + } +} + +#ifdef CONFIG_SERIAL_DZ_CONSOLE +/* + * ------------------------------------------------------------------- + * dz_console_putchar() -- transmit a character + * + * Polled transmission. This is tricky. We need to mask transmit + * interrupts so that they do not interfere, enable the transmitter + * for the line requested and then wait till the transmit scanner + * requests data for this line. But it may request data for another + * line first, in which case we have to disable its transmitter and + * repeat waiting till our line pops up. Only then the character may + * be transmitted. Finally, the state of the transmitter mask is + * restored. Welcome to the world of PDP-11! + * ------------------------------------------------------------------- + */ +static void dz_console_putchar(struct uart_port *uport, int ch) +{ + struct dz_port *dport = to_dport(uport); + unsigned long flags; + unsigned short csr, tcr, trdy, mask; + int loops = 10000; + + spin_lock_irqsave(&dport->port.lock, flags); + csr = dz_in(dport, DZ_CSR); + dz_out(dport, DZ_CSR, csr & ~DZ_TIE); + tcr = dz_in(dport, DZ_TCR); + tcr |= 1 << dport->port.line; + mask = tcr; + dz_out(dport, DZ_TCR, mask); + iob(); + spin_unlock_irqrestore(&dport->port.lock, flags); + + do { + trdy = dz_in(dport, DZ_CSR); + if (!(trdy & DZ_TRDY)) + continue; + trdy = (trdy & DZ_TLINE) >> 8; + if (trdy == dport->port.line) + break; + mask &= ~(1 << trdy); + dz_out(dport, DZ_TCR, mask); + iob(); + udelay(2); + } while (--loops); + + if (loops) /* Cannot send otherwise. */ + dz_out(dport, DZ_TDR, ch); + + dz_out(dport, DZ_TCR, tcr); + dz_out(dport, DZ_CSR, csr); +} + +/* + * ------------------------------------------------------------------- + * dz_console_print () + * + * dz_console_print is registered for printk. + * The console must be locked when we get here. + * ------------------------------------------------------------------- + */ +static void dz_console_print(struct console *co, + const char *str, + unsigned int count) +{ + struct dz_port *dport = &dz_mux.dport[co->index]; +#ifdef DEBUG_DZ + prom_printf((char *) str); +#endif + uart_console_write(&dport->port, str, count, dz_console_putchar); +} + +static int __init dz_console_setup(struct console *co, char *options) +{ + struct dz_port *dport = &dz_mux.dport[co->index]; + struct uart_port *uport = &dport->port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + ret = dz_map_port(uport); + if (ret) + return ret; + + spin_lock_init(&dport->port.lock); /* For dz_pm(). */ + + dz_reset(dport); + dz_pm(uport, 0, -1); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&dport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver dz_reg; +static struct console dz_console = { + .name = "ttyS", + .write = dz_console_print, + .device = uart_console_device, + .setup = dz_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &dz_reg, +}; + +static int __init dz_serial_console_init(void) +{ + if (!IOASIC) { + dz_init_ports(); + register_console(&dz_console); + return 0; + } else + return -ENXIO; +} + +console_initcall(dz_serial_console_init); + +#define SERIAL_DZ_CONSOLE &dz_console +#else +#define SERIAL_DZ_CONSOLE NULL +#endif /* CONFIG_SERIAL_DZ_CONSOLE */ + +static struct uart_driver dz_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = DZ_NB_PORT, + .cons = SERIAL_DZ_CONSOLE, +}; + +static int __init dz_init(void) +{ + int ret, i; + + if (IOASIC) + return -ENXIO; + + printk("%s%s\n", dz_name, dz_version); + + dz_init_ports(); + + ret = uart_register_driver(&dz_reg); + if (ret) + return ret; + + for (i = 0; i < DZ_NB_PORT; i++) + uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); + + return 0; +} + +module_init(dz_init); diff --git a/drivers/tty/serial/dz.h b/drivers/tty/serial/dz.h new file mode 100644 index 000000000000..faf169ed27b3 --- /dev/null +++ b/drivers/tty/serial/dz.h @@ -0,0 +1,129 @@ +/* + * dz.h: Serial port driver for DECstations equipped + * with the DZ chipset. + * + * Copyright (C) 1998 Olivier A. D. Lebaillif + * + * Email: olivier.lebaillif@ifrsys.com + * + * Copyright (C) 2004, 2006 Maciej W. Rozycki + */ +#ifndef DZ_SERIAL_H +#define DZ_SERIAL_H + +/* + * Definitions for the Control and Status Register. + */ +#define DZ_TRDY 0x8000 /* Transmitter empty */ +#define DZ_TIE 0x4000 /* Transmitter Interrupt Enbl */ +#define DZ_TLINE 0x0300 /* Transmitter Line Number */ +#define DZ_RDONE 0x0080 /* Receiver data ready */ +#define DZ_RIE 0x0040 /* Receive Interrupt Enable */ +#define DZ_MSE 0x0020 /* Master Scan Enable */ +#define DZ_CLR 0x0010 /* Master reset */ +#define DZ_MAINT 0x0008 /* Loop Back Mode */ + +/* + * Definitions for the Receiver Buffer Register. + */ +#define DZ_RBUF_MASK 0x00FF /* Data Mask */ +#define DZ_LINE_MASK 0x0300 /* Line Mask */ +#define DZ_DVAL 0x8000 /* Valid Data indicator */ +#define DZ_OERR 0x4000 /* Overrun error indicator */ +#define DZ_FERR 0x2000 /* Frame error indicator */ +#define DZ_PERR 0x1000 /* Parity error indicator */ + +#define DZ_BREAK 0x0800 /* BREAK event software flag */ + +#define LINE(x) ((x & DZ_LINE_MASK) >> 8) /* Get the line number + from the input buffer */ +#define UCHAR(x) ((unsigned char)(x & DZ_RBUF_MASK)) + +/* + * Definitions for the Transmit Control Register. + */ +#define DZ_LINE_KEYBOARD 0x0001 +#define DZ_LINE_MOUSE 0x0002 +#define DZ_LINE_MODEM 0x0004 +#define DZ_LINE_PRINTER 0x0008 + +#define DZ_MODEM_RTS 0x0800 /* RTS for the modem line (2) */ +#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */ +#define DZ_PRINT_RTS 0x0200 /* RTS for the prntr line (3) */ +#define DZ_PRINT_DTR 0x0100 /* DTR for the prntr line (3) */ +#define DZ_LNENB 0x000f /* Transmitter Line Enable */ + +/* + * Definitions for the Modem Status Register. + */ +#define DZ_MODEM_RI 0x0800 /* RI for the modem line (2) */ +#define DZ_MODEM_CD 0x0400 /* CD for the modem line (2) */ +#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */ +#define DZ_MODEM_CTS 0x0100 /* CTS for the modem line (2) */ +#define DZ_PRINT_RI 0x0008 /* RI for the printer line (3) */ +#define DZ_PRINT_CD 0x0004 /* CD for the printer line (3) */ +#define DZ_PRINT_DSR 0x0002 /* DSR for the prntr line (3) */ +#define DZ_PRINT_CTS 0x0001 /* CTS for the prntr line (3) */ + +/* + * Definitions for the Transmit Data Register. + */ +#define DZ_BRK0 0x0100 /* Break assertion for line 0 */ +#define DZ_BRK1 0x0200 /* Break assertion for line 1 */ +#define DZ_BRK2 0x0400 /* Break assertion for line 2 */ +#define DZ_BRK3 0x0800 /* Break assertion for line 3 */ + +/* + * Definitions for the Line Parameter Register. + */ +#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */ +#define DZ_MOUSE 0x0001 /* line 1 = mouse */ +#define DZ_MODEM 0x0002 /* line 2 = modem */ +#define DZ_PRINTER 0x0003 /* line 3 = printer */ + +#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */ +#define DZ_CS5 0x0000 /* 5 bits per byte */ +#define DZ_CS6 0x0008 /* 6 bits per byte */ +#define DZ_CS7 0x0010 /* 7 bits per byte */ +#define DZ_CS8 0x0018 /* 8 bits per byte */ + +#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */ + +#define DZ_PARENB 0x0040 /* Parity enable */ +#define DZ_PARODD 0x0080 /* Odd parity instead of even */ + +#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */ +#define DZ_B50 0x0000 +#define DZ_B75 0x0100 +#define DZ_B110 0x0200 +#define DZ_B134 0x0300 +#define DZ_B150 0x0400 +#define DZ_B300 0x0500 +#define DZ_B600 0x0600 +#define DZ_B1200 0x0700 +#define DZ_B1800 0x0800 +#define DZ_B2000 0x0900 +#define DZ_B2400 0x0A00 +#define DZ_B3600 0x0B00 +#define DZ_B4800 0x0C00 +#define DZ_B7200 0x0D00 +#define DZ_B9600 0x0E00 + +#define DZ_RXENAB 0x1000 /* Receiver Enable */ + +/* + * Addresses for the DZ registers + */ +#define DZ_CSR 0x00 /* Control and Status Register */ +#define DZ_RBUF 0x08 /* Receive Buffer */ +#define DZ_LPR 0x08 /* Line Parameters Register */ +#define DZ_TCR 0x10 /* Transmitter Control Register */ +#define DZ_MSR 0x18 /* Modem Status Register */ +#define DZ_TDR 0x18 /* Transmit Data Register */ + +#define DZ_NB_PORT 4 + +#define DZ_XMIT_SIZE 4096 /* buffer size */ +#define DZ_WAKEUP_CHARS DZ_XMIT_SIZE/4 + +#endif /* DZ_SERIAL_H */ diff --git a/drivers/tty/serial/icom.c b/drivers/tty/serial/icom.c new file mode 100644 index 000000000000..53a468227056 --- /dev/null +++ b/drivers/tty/serial/icom.c @@ -0,0 +1,1658 @@ +/* + * icom.c + * + * Copyright (C) 2001 IBM Corporation. All rights reserved. + * + * Serial device driver. + * + * Based on code from serial.c + * + * 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 + * + */ +#define SERIAL_DO_RESTART +#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 +#include +#include +#include + +#include +#include +#include +#include + +#include "icom.h" + +/*#define ICOM_TRACE enable port trace capabilities */ + +#define ICOM_DRIVER_NAME "icom" +#define ICOM_VERSION_STR "1.3.1" +#define NR_PORTS 128 +#define ICOM_PORT ((struct icom_port *)port) +#define to_icom_adapter(d) container_of(d, struct icom_adapter, kref) + +static const struct pci_device_id icom_pci_table[] = { + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = ADAPTER_V1, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX, + .driver_data = ADAPTER_V2, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM, + .driver_data = ADAPTER_V2, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL, + .driver_data = ADAPTER_V2, + }, + { + .vendor = PCI_VENDOR_ID_IBM, + .device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE, + .driver_data = ADAPTER_V2, + }, + {} +}; + +struct lookup_proc_table start_proc[4] = { + {NULL, ICOM_CONTROL_START_A}, + {NULL, ICOM_CONTROL_START_B}, + {NULL, ICOM_CONTROL_START_C}, + {NULL, ICOM_CONTROL_START_D} +}; + + +struct lookup_proc_table stop_proc[4] = { + {NULL, ICOM_CONTROL_STOP_A}, + {NULL, ICOM_CONTROL_STOP_B}, + {NULL, ICOM_CONTROL_STOP_C}, + {NULL, ICOM_CONTROL_STOP_D} +}; + +struct lookup_int_table int_mask_tbl[4] = { + {NULL, ICOM_INT_MASK_PRC_A}, + {NULL, ICOM_INT_MASK_PRC_B}, + {NULL, ICOM_INT_MASK_PRC_C}, + {NULL, ICOM_INT_MASK_PRC_D}, +}; + + +MODULE_DEVICE_TABLE(pci, icom_pci_table); + +static LIST_HEAD(icom_adapter_head); + +/* spinlock for adapter initialization and changing adapter operations */ +static spinlock_t icom_lock; + +#ifdef ICOM_TRACE +static inline void trace(struct icom_port *icom_port, char *trace_pt, + unsigned long trace_data) +{ + dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n", + icom_port->port, trace_pt, trace_data); +} +#else +static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {}; +#endif +static void icom_kref_release(struct kref *kref); + +static void free_port_memory(struct icom_port *icom_port) +{ + struct pci_dev *dev = icom_port->adapter->pci_dev; + + trace(icom_port, "RET_PORT_MEM", 0); + if (icom_port->recv_buf) { + pci_free_consistent(dev, 4096, icom_port->recv_buf, + icom_port->recv_buf_pci); + icom_port->recv_buf = NULL; + } + if (icom_port->xmit_buf) { + pci_free_consistent(dev, 4096, icom_port->xmit_buf, + icom_port->xmit_buf_pci); + icom_port->xmit_buf = NULL; + } + if (icom_port->statStg) { + pci_free_consistent(dev, 4096, icom_port->statStg, + icom_port->statStg_pci); + icom_port->statStg = NULL; + } + + if (icom_port->xmitRestart) { + pci_free_consistent(dev, 4096, icom_port->xmitRestart, + icom_port->xmitRestart_pci); + icom_port->xmitRestart = NULL; + } +} + +static int __devinit get_port_memory(struct icom_port *icom_port) +{ + int index; + unsigned long stgAddr; + unsigned long startStgAddr; + unsigned long offset; + struct pci_dev *dev = icom_port->adapter->pci_dev; + + icom_port->xmit_buf = + pci_alloc_consistent(dev, 4096, &icom_port->xmit_buf_pci); + if (!icom_port->xmit_buf) { + dev_err(&dev->dev, "Can not allocate Transmit buffer\n"); + return -ENOMEM; + } + + trace(icom_port, "GET_PORT_MEM", + (unsigned long) icom_port->xmit_buf); + + icom_port->recv_buf = + pci_alloc_consistent(dev, 4096, &icom_port->recv_buf_pci); + if (!icom_port->recv_buf) { + dev_err(&dev->dev, "Can not allocate Receive buffer\n"); + free_port_memory(icom_port); + return -ENOMEM; + } + trace(icom_port, "GET_PORT_MEM", + (unsigned long) icom_port->recv_buf); + + icom_port->statStg = + pci_alloc_consistent(dev, 4096, &icom_port->statStg_pci); + if (!icom_port->statStg) { + dev_err(&dev->dev, "Can not allocate Status buffer\n"); + free_port_memory(icom_port); + return -ENOMEM; + } + trace(icom_port, "GET_PORT_MEM", + (unsigned long) icom_port->statStg); + + icom_port->xmitRestart = + pci_alloc_consistent(dev, 4096, &icom_port->xmitRestart_pci); + if (!icom_port->xmitRestart) { + dev_err(&dev->dev, + "Can not allocate xmit Restart buffer\n"); + free_port_memory(icom_port); + return -ENOMEM; + } + + memset(icom_port->statStg, 0, 4096); + + /* FODs: Frame Out Descriptor Queue, this is a FIFO queue that + indicates that frames are to be transmitted + */ + + stgAddr = (unsigned long) icom_port->statStg; + for (index = 0; index < NUM_XBUFFS; index++) { + trace(icom_port, "FOD_ADDR", stgAddr); + stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]); + if (index < (NUM_XBUFFS - 1)) { + memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); + icom_port->statStg->xmit[index].leLengthASD = + (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ); + trace(icom_port, "FOD_ADDR", stgAddr); + trace(icom_port, "FOD_XBUFF", + (unsigned long) icom_port->xmit_buf); + icom_port->statStg->xmit[index].leBuffer = + cpu_to_le32(icom_port->xmit_buf_pci); + } else if (index == (NUM_XBUFFS - 1)) { + memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); + icom_port->statStg->xmit[index].leLengthASD = + (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ); + trace(icom_port, "FOD_XBUFF", + (unsigned long) icom_port->xmit_buf); + icom_port->statStg->xmit[index].leBuffer = + cpu_to_le32(icom_port->xmit_buf_pci); + } else { + memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area)); + } + } + /* FIDs */ + startStgAddr = stgAddr; + + /* fill in every entry, even if no buffer */ + for (index = 0; index < NUM_RBUFFS; index++) { + trace(icom_port, "FID_ADDR", stgAddr); + stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]); + icom_port->statStg->rcv[index].leLength = 0; + icom_port->statStg->rcv[index].WorkingLength = + (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); + if (index < (NUM_RBUFFS - 1) ) { + offset = stgAddr - (unsigned long) icom_port->statStg; + icom_port->statStg->rcv[index].leNext = + cpu_to_le32(icom_port-> statStg_pci + offset); + trace(icom_port, "FID_RBUFF", + (unsigned long) icom_port->recv_buf); + icom_port->statStg->rcv[index].leBuffer = + cpu_to_le32(icom_port->recv_buf_pci); + } else if (index == (NUM_RBUFFS -1) ) { + offset = startStgAddr - (unsigned long) icom_port->statStg; + icom_port->statStg->rcv[index].leNext = + cpu_to_le32(icom_port-> statStg_pci + offset); + trace(icom_port, "FID_RBUFF", + (unsigned long) icom_port->recv_buf + 2048); + icom_port->statStg->rcv[index].leBuffer = + cpu_to_le32(icom_port->recv_buf_pci + 2048); + } else { + icom_port->statStg->rcv[index].leNext = 0; + icom_port->statStg->rcv[index].leBuffer = 0; + } + } + + return 0; +} + +static void stop_processor(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned long flags; + int port; + + spin_lock_irqsave(&icom_lock, flags); + + port = icom_port->port; + if (port == 0 || port == 1) + stop_proc[port].global_control_reg = &icom_port->global_reg->control; + else + stop_proc[port].global_control_reg = &icom_port->global_reg->control_2; + + + if (port < 4) { + temp = readl(stop_proc[port].global_control_reg); + temp = + (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; + writel(temp, stop_proc[port].global_control_reg); + + /* write flush */ + readl(stop_proc[port].global_control_reg); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + + spin_unlock_irqrestore(&icom_lock, flags); +} + +static void start_processor(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned long flags; + int port; + + spin_lock_irqsave(&icom_lock, flags); + + port = icom_port->port; + if (port == 0 || port == 1) + start_proc[port].global_control_reg = &icom_port->global_reg->control; + else + start_proc[port].global_control_reg = &icom_port->global_reg->control_2; + if (port < 4) { + temp = readl(start_proc[port].global_control_reg); + temp = + (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; + writel(temp, start_proc[port].global_control_reg); + + /* write flush */ + readl(start_proc[port].global_control_reg); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + + spin_unlock_irqrestore(&icom_lock, flags); +} + +static void load_code(struct icom_port *icom_port) +{ + const struct firmware *fw; + char __iomem *iram_ptr; + int index; + int status = 0; + void __iomem *dram_ptr = icom_port->dram; + dma_addr_t temp_pci; + unsigned char *new_page = NULL; + unsigned char cable_id = NO_CABLE; + struct pci_dev *dev = icom_port->adapter->pci_dev; + + /* Clear out any pending interrupts */ + writew(0x3FFF, icom_port->int_reg); + + trace(icom_port, "CLEAR_INTERRUPTS", 0); + + /* Stop processor */ + stop_processor(icom_port); + + /* Zero out DRAM */ + memset_io(dram_ptr, 0, 512); + + /* Load Call Setup into Adapter */ + if (request_firmware(&fw, "icom_call_setup.bin", &dev->dev) < 0) { + dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n"); + status = -1; + goto load_code_exit; + } + + if (fw->size > ICOM_DCE_IRAM_OFFSET) { + dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n"); + release_firmware(fw); + status = -1; + goto load_code_exit; + } + + iram_ptr = (char __iomem *)icom_port->dram + ICOM_IRAM_OFFSET; + for (index = 0; index < fw->size; index++) + writeb(fw->data[index], &iram_ptr[index]); + + release_firmware(fw); + + /* Load Resident DCE portion of Adapter */ + if (request_firmware(&fw, "icom_res_dce.bin", &dev->dev) < 0) { + dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n"); + status = -1; + goto load_code_exit; + } + + if (fw->size > ICOM_IRAM_SIZE) { + dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n"); + release_firmware(fw); + status = -1; + goto load_code_exit; + } + + iram_ptr = (char __iomem *) icom_port->dram + ICOM_IRAM_OFFSET; + for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++) + writeb(fw->data[index], &iram_ptr[index]); + + release_firmware(fw); + + /* Set Hardware level */ + if (icom_port->adapter->version == ADAPTER_V2) + writeb(V2_HARDWARE, &(icom_port->dram->misc_flags)); + + /* Start the processor in Adapter */ + start_processor(icom_port); + + writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL), + &(icom_port->dram->HDLCConfigReg)); + writeb(0x04, &(icom_port->dram->FlagFillIdleTimer)); /* 0.5 seconds */ + writeb(0x00, &(icom_port->dram->CmdReg)); + writeb(0x10, &(icom_port->dram->async_config3)); + writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC | + ICOM_ACFG_1STOP_BIT), &(icom_port->dram->async_config2)); + + /*Set up data in icom DRAM to indicate where personality + *code is located and its length. + */ + new_page = pci_alloc_consistent(dev, 4096, &temp_pci); + + if (!new_page) { + dev_err(&dev->dev, "Can not allocate DMA buffer\n"); + status = -1; + goto load_code_exit; + } + + if (request_firmware(&fw, "icom_asc.bin", &dev->dev) < 0) { + dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n"); + status = -1; + goto load_code_exit; + } + + if (fw->size > ICOM_DCE_IRAM_OFFSET) { + dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n"); + release_firmware(fw); + status = -1; + goto load_code_exit; + } + + for (index = 0; index < fw->size; index++) + new_page[index] = fw->data[index]; + + release_firmware(fw); + + writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length); + writel(temp_pci, &icom_port->dram->mac_load_addr); + + /*Setting the syncReg to 0x80 causes adapter to start downloading + the personality code into adapter instruction RAM. + Once code is loaded, it will begin executing and, based on + information provided above, will start DMAing data from + shared memory to adapter DRAM. + */ + /* the wait loop below verifies this write operation has been done + and processed + */ + writeb(START_DOWNLOAD, &icom_port->dram->sync); + + /* Wait max 1 Sec for data download and processor to start */ + for (index = 0; index < 10; index++) { + msleep(100); + if (readb(&icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE) + break; + } + + if (index == 10) + status = -1; + + /* + * check Cable ID + */ + cable_id = readb(&icom_port->dram->cable_id); + + if (cable_id & ICOM_CABLE_ID_VALID) { + /* Get cable ID into the lower 4 bits (standard form) */ + cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4; + icom_port->cable_id = cable_id; + } else { + dev_err(&dev->dev,"Invalid or no cable attached\n"); + icom_port->cable_id = NO_CABLE; + } + + load_code_exit: + + if (status != 0) { + /* Clear out any pending interrupts */ + writew(0x3FFF, icom_port->int_reg); + + /* Turn off port */ + writeb(ICOM_DISABLE, &(icom_port->dram->disable)); + + /* Stop processor */ + stop_processor(icom_port); + + dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n"); + } + + if (new_page != NULL) + pci_free_consistent(dev, 4096, new_page, temp_pci); +} + +static int startup(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned char cable_id, raw_cable_id; + unsigned long flags; + int port; + + trace(icom_port, "STARTUP", 0); + + if (!icom_port->dram) { + /* should NEVER be NULL */ + dev_err(&icom_port->adapter->pci_dev->dev, + "Unusable Port, port configuration missing\n"); + return -ENODEV; + } + + /* + * check Cable ID + */ + raw_cable_id = readb(&icom_port->dram->cable_id); + trace(icom_port, "CABLE_ID", raw_cable_id); + + /* Get cable ID into the lower 4 bits (standard form) */ + cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; + + /* Check for valid Cable ID */ + if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || + (cable_id != icom_port->cable_id)) { + + /* reload adapter code, pick up any potential changes in cable id */ + load_code(icom_port); + + /* still no sign of cable, error out */ + raw_cable_id = readb(&icom_port->dram->cable_id); + cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4; + if (!(raw_cable_id & ICOM_CABLE_ID_VALID) || + (icom_port->cable_id == NO_CABLE)) + return -EIO; + } + + /* + * Finally, clear and enable interrupts + */ + spin_lock_irqsave(&icom_lock, flags); + port = icom_port->port; + if (port == 0 || port == 1) + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; + else + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; + + if (port == 0 || port == 2) + writew(0x00FF, icom_port->int_reg); + else + writew(0x3F00, icom_port->int_reg); + if (port < 4) { + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + + spin_unlock_irqrestore(&icom_lock, flags); + return 0; +} + +static void shutdown(struct icom_port *icom_port) +{ + unsigned long temp; + unsigned char cmdReg; + unsigned long flags; + int port; + + spin_lock_irqsave(&icom_lock, flags); + trace(icom_port, "SHUTDOWN", 0); + + /* + * disable all interrupts + */ + port = icom_port->port; + if (port == 0 || port == 1) + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask; + else + int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2; + + if (port < 4) { + temp = readl(int_mask_tbl[port].global_int_mask); + writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask); + + /* write flush */ + readl(int_mask_tbl[port].global_int_mask); + } else { + dev_err(&icom_port->adapter->pci_dev->dev, + "Invalid port assignment\n"); + } + spin_unlock_irqrestore(&icom_lock, flags); + + /* + * disable break condition + */ + cmdReg = readb(&icom_port->dram->CmdReg); + if (cmdReg & CMD_SND_BREAK) { + writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg); + } +} + +static int icom_write(struct uart_port *port) +{ + unsigned long data_count; + unsigned char cmdReg; + unsigned long offset; + int temp_tail = port->state->xmit.tail; + + trace(ICOM_PORT, "WRITE", 0); + + if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) & + SA_FLAGS_READY_TO_XMIT) { + trace(ICOM_PORT, "WRITE_FULL", 0); + return 0; + } + + data_count = 0; + while ((port->state->xmit.head != temp_tail) && + (data_count <= XMIT_BUFF_SZ)) { + + ICOM_PORT->xmit_buf[data_count++] = + port->state->xmit.buf[temp_tail]; + + temp_tail++; + temp_tail &= (UART_XMIT_SIZE - 1); + } + + if (data_count) { + ICOM_PORT->statStg->xmit[0].flags = + cpu_to_le16(SA_FLAGS_READY_TO_XMIT); + ICOM_PORT->statStg->xmit[0].leLength = + cpu_to_le16(data_count); + offset = + (unsigned long) &ICOM_PORT->statStg->xmit[0] - + (unsigned long) ICOM_PORT->statStg; + *ICOM_PORT->xmitRestart = + cpu_to_le32(ICOM_PORT->statStg_pci + offset); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg | CMD_XMIT_RCV_ENABLE, + &ICOM_PORT->dram->CmdReg); + writeb(START_XMIT, &ICOM_PORT->dram->StartXmitCmd); + trace(ICOM_PORT, "WRITE_START", data_count); + /* write flush */ + readb(&ICOM_PORT->dram->StartXmitCmd); + } + + return data_count; +} + +static inline void check_modem_status(struct icom_port *icom_port) +{ + static char old_status = 0; + char delta_status; + unsigned char status; + + spin_lock(&icom_port->uart_port.lock); + + /*modem input register */ + status = readb(&icom_port->dram->isr); + trace(icom_port, "CHECK_MODEM", status); + delta_status = status ^ old_status; + if (delta_status) { + if (delta_status & ICOM_RI) + icom_port->uart_port.icount.rng++; + if (delta_status & ICOM_DSR) + icom_port->uart_port.icount.dsr++; + if (delta_status & ICOM_DCD) + uart_handle_dcd_change(&icom_port->uart_port, + delta_status & ICOM_DCD); + if (delta_status & ICOM_CTS) + uart_handle_cts_change(&icom_port->uart_port, + delta_status & ICOM_CTS); + + wake_up_interruptible(&icom_port->uart_port.state-> + port.delta_msr_wait); + old_status = status; + } + spin_unlock(&icom_port->uart_port.lock); +} + +static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) +{ + unsigned short int count; + int i; + + if (port_int_reg & (INT_XMIT_COMPLETED)) { + trace(icom_port, "XMIT_COMPLETE", 0); + + /* clear buffer in use bit */ + icom_port->statStg->xmit[0].flags &= + cpu_to_le16(~SA_FLAGS_READY_TO_XMIT); + + count = (unsigned short int) + cpu_to_le16(icom_port->statStg->xmit[0].leLength); + icom_port->uart_port.icount.tx += count; + + for (i=0; iuart_port.state->xmit); i++) { + + icom_port->uart_port.state->xmit.tail++; + icom_port->uart_port.state->xmit.tail &= + (UART_XMIT_SIZE - 1); + } + + if (!icom_write(&icom_port->uart_port)) + /* activate write queue */ + uart_write_wakeup(&icom_port->uart_port); + } else + trace(icom_port, "XMIT_DISABLED", 0); +} + +static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) +{ + short int count, rcv_buff; + struct tty_struct *tty = icom_port->uart_port.state->port.tty; + unsigned short int status; + struct uart_icount *icount; + unsigned long offset; + unsigned char flag; + + trace(icom_port, "RCV_COMPLETE", 0); + rcv_buff = icom_port->next_rcv; + + status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); + while (status & SA_FL_RCV_DONE) { + int first = -1; + + trace(icom_port, "FID_STATUS", status); + count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength); + + trace(icom_port, "RCV_COUNT", count); + + trace(icom_port, "REAL_COUNT", count); + + offset = + cpu_to_le32(icom_port->statStg->rcv[rcv_buff].leBuffer) - + icom_port->recv_buf_pci; + + /* Block copy all but the last byte as this may have status */ + if (count > 0) { + first = icom_port->recv_buf[offset]; + tty_insert_flip_string(tty, icom_port->recv_buf + offset, count - 1); + } + + icount = &icom_port->uart_port.icount; + icount->rx += count; + + /* Break detect logic */ + if ((status & SA_FLAGS_FRAME_ERROR) + && first == 0) { + status &= ~SA_FLAGS_FRAME_ERROR; + status |= SA_FLAGS_BREAK_DET; + trace(icom_port, "BREAK_DET", 0); + } + + flag = TTY_NORMAL; + + if (status & + (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR | + SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) { + + if (status & SA_FLAGS_BREAK_DET) + icount->brk++; + if (status & SA_FLAGS_PARITY_ERROR) + icount->parity++; + if (status & SA_FLAGS_FRAME_ERROR) + icount->frame++; + if (status & SA_FLAGS_OVERRUN) + icount->overrun++; + + /* + * Now check to see if character should be + * ignored, and mask off conditions which + * should be ignored. + */ + if (status & icom_port->ignore_status_mask) { + trace(icom_port, "IGNORE_CHAR", 0); + goto ignore_char; + } + + status &= icom_port->read_status_mask; + + if (status & SA_FLAGS_BREAK_DET) { + flag = TTY_BREAK; + } else if (status & SA_FLAGS_PARITY_ERROR) { + trace(icom_port, "PARITY_ERROR", 0); + flag = TTY_PARITY; + } else if (status & SA_FLAGS_FRAME_ERROR) + flag = TTY_FRAME; + + } + + tty_insert_flip_char(tty, *(icom_port->recv_buf + offset + count - 1), flag); + + if (status & SA_FLAGS_OVERRUN) + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); +ignore_char: + icom_port->statStg->rcv[rcv_buff].flags = 0; + icom_port->statStg->rcv[rcv_buff].leLength = 0; + icom_port->statStg->rcv[rcv_buff].WorkingLength = + (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); + + rcv_buff++; + if (rcv_buff == NUM_RBUFFS) + rcv_buff = 0; + + status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags); + } + icom_port->next_rcv = rcv_buff; + tty_flip_buffer_push(tty); +} + +static void process_interrupt(u16 port_int_reg, + struct icom_port *icom_port) +{ + + spin_lock(&icom_port->uart_port.lock); + trace(icom_port, "INTERRUPT", port_int_reg); + + if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED)) + xmit_interrupt(port_int_reg, icom_port); + + if (port_int_reg & INT_RCV_COMPLETED) + recv_interrupt(port_int_reg, icom_port); + + spin_unlock(&icom_port->uart_port.lock); +} + +static irqreturn_t icom_interrupt(int irq, void *dev_id) +{ + void __iomem * int_reg; + u32 adapter_interrupts; + u16 port_int_reg; + struct icom_adapter *icom_adapter; + struct icom_port *icom_port; + + /* find icom_port for this interrupt */ + icom_adapter = (struct icom_adapter *) dev_id; + + if (icom_adapter->version == ADAPTER_V2) { + int_reg = icom_adapter->base_addr + 0x8024; + + adapter_interrupts = readl(int_reg); + + if (adapter_interrupts & 0x00003FFF) { + /* port 2 interrupt, NOTE: for all ADAPTER_V2, port 2 will be active */ + icom_port = &icom_adapter->port_info[2]; + port_int_reg = (u16) adapter_interrupts; + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + if (adapter_interrupts & 0x3FFF0000) { + /* port 3 interrupt */ + icom_port = &icom_adapter->port_info[3]; + if (icom_port->status == ICOM_PORT_ACTIVE) { + port_int_reg = + (u16) (adapter_interrupts >> 16); + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + } + + /* Clear out any pending interrupts */ + writel(adapter_interrupts, int_reg); + + int_reg = icom_adapter->base_addr + 0x8004; + } else { + int_reg = icom_adapter->base_addr + 0x4004; + } + + adapter_interrupts = readl(int_reg); + + if (adapter_interrupts & 0x00003FFF) { + /* port 0 interrupt, NOTE: for all adapters, port 0 will be active */ + icom_port = &icom_adapter->port_info[0]; + port_int_reg = (u16) adapter_interrupts; + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + if (adapter_interrupts & 0x3FFF0000) { + /* port 1 interrupt */ + icom_port = &icom_adapter->port_info[1]; + if (icom_port->status == ICOM_PORT_ACTIVE) { + port_int_reg = (u16) (adapter_interrupts >> 16); + process_interrupt(port_int_reg, icom_port); + check_modem_status(icom_port); + } + } + + /* Clear out any pending interrupts */ + writel(adapter_interrupts, int_reg); + + /* flush the write */ + adapter_interrupts = readl(int_reg); + + return IRQ_HANDLED; +} + +/* + * ------------------------------------------------------------------ + * Begin serial-core API + * ------------------------------------------------------------------ + */ +static unsigned int icom_tx_empty(struct uart_port *port) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) & + SA_FLAGS_READY_TO_XMIT) + ret = TIOCSER_TEMT; + else + ret = 0; + + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned char local_osr; + + trace(ICOM_PORT, "SET_MODEM", 0); + local_osr = readb(&ICOM_PORT->dram->osr); + + if (mctrl & TIOCM_RTS) { + trace(ICOM_PORT, "RAISE_RTS", 0); + local_osr |= ICOM_RTS; + } else { + trace(ICOM_PORT, "LOWER_RTS", 0); + local_osr &= ~ICOM_RTS; + } + + if (mctrl & TIOCM_DTR) { + trace(ICOM_PORT, "RAISE_DTR", 0); + local_osr |= ICOM_DTR; + } else { + trace(ICOM_PORT, "LOWER_DTR", 0); + local_osr &= ~ICOM_DTR; + } + + writeb(local_osr, &ICOM_PORT->dram->osr); +} + +static unsigned int icom_get_mctrl(struct uart_port *port) +{ + unsigned char status; + unsigned int result; + + trace(ICOM_PORT, "GET_MODEM", 0); + + status = readb(&ICOM_PORT->dram->isr); + + result = ((status & ICOM_DCD) ? TIOCM_CAR : 0) + | ((status & ICOM_RI) ? TIOCM_RNG : 0) + | ((status & ICOM_DSR) ? TIOCM_DSR : 0) + | ((status & ICOM_CTS) ? TIOCM_CTS : 0); + return result; +} + +static void icom_stop_tx(struct uart_port *port) +{ + unsigned char cmdReg; + + trace(ICOM_PORT, "STOP", 0); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg | CMD_HOLD_XMIT, &ICOM_PORT->dram->CmdReg); +} + +static void icom_start_tx(struct uart_port *port) +{ + unsigned char cmdReg; + + trace(ICOM_PORT, "START", 0); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT) + writeb(cmdReg & ~CMD_HOLD_XMIT, + &ICOM_PORT->dram->CmdReg); + + icom_write(port); +} + +static void icom_send_xchar(struct uart_port *port, char ch) +{ + unsigned char xdata; + int index; + unsigned long flags; + + trace(ICOM_PORT, "SEND_XCHAR", ch); + + /* wait .1 sec to send char */ + for (index = 0; index < 10; index++) { + spin_lock_irqsave(&port->lock, flags); + xdata = readb(&ICOM_PORT->dram->xchar); + if (xdata == 0x00) { + trace(ICOM_PORT, "QUICK_WRITE", 0); + writeb(ch, &ICOM_PORT->dram->xchar); + + /* flush write operation */ + xdata = readb(&ICOM_PORT->dram->xchar); + spin_unlock_irqrestore(&port->lock, flags); + break; + } + spin_unlock_irqrestore(&port->lock, flags); + msleep(10); + } +} + +static void icom_stop_rx(struct uart_port *port) +{ + unsigned char cmdReg; + + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); +} + +static void icom_enable_ms(struct uart_port *port) +{ + /* no-op */ +} + +static void icom_break(struct uart_port *port, int break_state) +{ + unsigned char cmdReg; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + trace(ICOM_PORT, "BREAK", 0); + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + if (break_state == -1) { + writeb(cmdReg | CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg); + } else { + writeb(cmdReg & ~CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static int icom_open(struct uart_port *port) +{ + int retval; + + kref_get(&ICOM_PORT->adapter->kref); + retval = startup(ICOM_PORT); + + if (retval) { + kref_put(&ICOM_PORT->adapter->kref, icom_kref_release); + trace(ICOM_PORT, "STARTUP_ERROR", 0); + return retval; + } + + return 0; +} + +static void icom_close(struct uart_port *port) +{ + unsigned char cmdReg; + + trace(ICOM_PORT, "CLOSE", 0); + + /* stop receiver */ + cmdReg = readb(&ICOM_PORT->dram->CmdReg); + writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE, + &ICOM_PORT->dram->CmdReg); + + shutdown(ICOM_PORT); + + kref_put(&ICOM_PORT->adapter->kref, icom_kref_release); +} + +static void icom_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old_termios) +{ + int baud; + unsigned cflag, iflag; + char new_config2; + char new_config3 = 0; + char tmp_byte; + int index; + int rcv_buff, xmit_buff; + unsigned long offset; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + trace(ICOM_PORT, "CHANGE_SPEED", 0); + + cflag = termios->c_cflag; + iflag = termios->c_iflag; + + new_config2 = ICOM_ACFG_DRIVE1; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: /* 5 bits/char */ + new_config2 |= ICOM_ACFG_5BPC; + break; + case CS6: /* 6 bits/char */ + new_config2 |= ICOM_ACFG_6BPC; + break; + case CS7: /* 7 bits/char */ + new_config2 |= ICOM_ACFG_7BPC; + break; + case CS8: /* 8 bits/char */ + new_config2 |= ICOM_ACFG_8BPC; + break; + default: + break; + } + if (cflag & CSTOPB) { + /* 2 stop bits */ + new_config2 |= ICOM_ACFG_2STOP_BIT; + } + if (cflag & PARENB) { + /* parity bit enabled */ + new_config2 |= ICOM_ACFG_PARITY_ENAB; + trace(ICOM_PORT, "PARENB", 0); + } + if (cflag & PARODD) { + /* odd parity */ + new_config2 |= ICOM_ACFG_PARITY_ODD; + trace(ICOM_PORT, "PARODD", 0); + } + + /* Determine divisor based on baud rate */ + baud = uart_get_baud_rate(port, termios, old_termios, + icom_acfg_baud[0], + icom_acfg_baud[BAUD_TABLE_LIMIT]); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + + for (index = 0; index < BAUD_TABLE_LIMIT; index++) { + if (icom_acfg_baud[index] == baud) { + new_config3 = index; + break; + } + } + + uart_update_timeout(port, cflag, baud); + + /* CTS flow control flag and modem status interrupts */ + tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg)); + if (cflag & CRTSCTS) + tmp_byte |= HDLC_HDW_FLOW; + else + tmp_byte &= ~HDLC_HDW_FLOW; + writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg)); + + /* + * Set up parity check flag + */ + ICOM_PORT->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE; + if (iflag & INPCK) + ICOM_PORT->read_status_mask |= + SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR; + + if ((iflag & BRKINT) || (iflag & PARMRK)) + ICOM_PORT->read_status_mask |= SA_FLAGS_BREAK_DET; + + /* + * Characters to ignore + */ + ICOM_PORT->ignore_status_mask = 0; + if (iflag & IGNPAR) + ICOM_PORT->ignore_status_mask |= + SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR; + if (iflag & IGNBRK) { + ICOM_PORT->ignore_status_mask |= SA_FLAGS_BREAK_DET; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (iflag & IGNPAR) + ICOM_PORT->ignore_status_mask |= SA_FLAGS_OVERRUN; + } + + /* + * !!! ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + ICOM_PORT->ignore_status_mask |= SA_FL_RCV_DONE; + + /* Turn off Receiver to prepare for reset */ + writeb(CMD_RCV_DISABLE, &ICOM_PORT->dram->CmdReg); + + for (index = 0; index < 10; index++) { + if (readb(&ICOM_PORT->dram->PrevCmdReg) == 0x00) { + break; + } + } + + /* clear all current buffers of data */ + for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) { + ICOM_PORT->statStg->rcv[rcv_buff].flags = 0; + ICOM_PORT->statStg->rcv[rcv_buff].leLength = 0; + ICOM_PORT->statStg->rcv[rcv_buff].WorkingLength = + (unsigned short int) cpu_to_le16(RCV_BUFF_SZ); + } + + for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) { + ICOM_PORT->statStg->xmit[xmit_buff].flags = 0; + } + + /* activate changes and start xmit and receiver here */ + /* Enable the receiver */ + writeb(new_config3, &(ICOM_PORT->dram->async_config3)); + writeb(new_config2, &(ICOM_PORT->dram->async_config2)); + tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg)); + tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL; + writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg)); + writeb(0x04, &(ICOM_PORT->dram->FlagFillIdleTimer)); /* 0.5 seconds */ + writeb(0xFF, &(ICOM_PORT->dram->ier)); /* enable modem signal interrupts */ + + /* reset processor */ + writeb(CMD_RESTART, &ICOM_PORT->dram->CmdReg); + + for (index = 0; index < 10; index++) { + if (readb(&ICOM_PORT->dram->CmdReg) == 0x00) { + break; + } + } + + /* Enable Transmitter and Reciever */ + offset = + (unsigned long) &ICOM_PORT->statStg->rcv[0] - + (unsigned long) ICOM_PORT->statStg; + writel(ICOM_PORT->statStg_pci + offset, + &ICOM_PORT->dram->RcvStatusAddr); + ICOM_PORT->next_rcv = 0; + ICOM_PORT->put_length = 0; + *ICOM_PORT->xmitRestart = 0; + writel(ICOM_PORT->xmitRestart_pci, + &ICOM_PORT->dram->XmitStatusAddr); + trace(ICOM_PORT, "XR_ENAB", 0); + writeb(CMD_XMIT_RCV_ENABLE, &ICOM_PORT->dram->CmdReg); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *icom_type(struct uart_port *port) +{ + return "icom"; +} + +static void icom_release_port(struct uart_port *port) +{ +} + +static int icom_request_port(struct uart_port *port) +{ + return 0; +} + +static void icom_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ICOM; +} + +static struct uart_ops icom_ops = { + .tx_empty = icom_tx_empty, + .set_mctrl = icom_set_mctrl, + .get_mctrl = icom_get_mctrl, + .stop_tx = icom_stop_tx, + .start_tx = icom_start_tx, + .send_xchar = icom_send_xchar, + .stop_rx = icom_stop_rx, + .enable_ms = icom_enable_ms, + .break_ctl = icom_break, + .startup = icom_open, + .shutdown = icom_close, + .set_termios = icom_set_termios, + .type = icom_type, + .release_port = icom_release_port, + .request_port = icom_request_port, + .config_port = icom_config_port, +}; + +#define ICOM_CONSOLE NULL + +static struct uart_driver icom_uart_driver = { + .owner = THIS_MODULE, + .driver_name = ICOM_DRIVER_NAME, + .dev_name = "ttyA", + .major = ICOM_MAJOR, + .minor = ICOM_MINOR_START, + .nr = NR_PORTS, + .cons = ICOM_CONSOLE, +}; + +static int __devinit icom_init_ports(struct icom_adapter *icom_adapter) +{ + u32 subsystem_id = icom_adapter->subsystem_id; + int i; + struct icom_port *icom_port; + + if (icom_adapter->version == ADAPTER_V1) { + icom_adapter->numb_ports = 2; + + for (i = 0; i < 2; i++) { + icom_port = &icom_adapter->port_info[i]; + icom_port->port = i; + icom_port->status = ICOM_PORT_ACTIVE; + icom_port->imbed_modem = ICOM_UNKNOWN; + } + } else { + if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) { + icom_adapter->numb_ports = 4; + + for (i = 0; i < 4; i++) { + icom_port = &icom_adapter->port_info[i]; + + icom_port->port = i; + icom_port->status = ICOM_PORT_ACTIVE; + icom_port->imbed_modem = ICOM_IMBED_MODEM; + } + } else { + icom_adapter->numb_ports = 4; + + icom_adapter->port_info[0].port = 0; + icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE; + + if (subsystem_id == + PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM) { + icom_adapter->port_info[0].imbed_modem = ICOM_IMBED_MODEM; + } else { + icom_adapter->port_info[0].imbed_modem = ICOM_RVX; + } + + icom_adapter->port_info[1].status = ICOM_PORT_OFF; + + icom_adapter->port_info[2].port = 2; + icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE; + icom_adapter->port_info[2].imbed_modem = ICOM_RVX; + icom_adapter->port_info[3].status = ICOM_PORT_OFF; + } + } + + return 0; +} + +static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num) +{ + if (icom_adapter->version == ADAPTER_V1) { + icom_port->global_reg = icom_adapter->base_addr + 0x4000; + icom_port->int_reg = icom_adapter->base_addr + + 0x4004 + 2 - 2 * port_num; + } else { + icom_port->global_reg = icom_adapter->base_addr + 0x8000; + if (icom_port->port < 2) + icom_port->int_reg = icom_adapter->base_addr + + 0x8004 + 2 - 2 * icom_port->port; + else + icom_port->int_reg = icom_adapter->base_addr + + 0x8024 + 2 - 2 * (icom_port->port - 2); + } +} +static int __devinit icom_load_ports(struct icom_adapter *icom_adapter) +{ + struct icom_port *icom_port; + int port_num; + + for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) { + + icom_port = &icom_adapter->port_info[port_num]; + + if (icom_port->status == ICOM_PORT_ACTIVE) { + icom_port_active(icom_port, icom_adapter, port_num); + icom_port->dram = icom_adapter->base_addr + + 0x2000 * icom_port->port; + + icom_port->adapter = icom_adapter; + + /* get port memory */ + if (get_port_memory(icom_port) != 0) { + dev_err(&icom_port->adapter->pci_dev->dev, + "Memory allocation for port FAILED\n"); + } + } + } + return 0; +} + +static int __devinit icom_alloc_adapter(struct icom_adapter + **icom_adapter_ref) +{ + int adapter_count = 0; + struct icom_adapter *icom_adapter; + struct icom_adapter *cur_adapter_entry; + struct list_head *tmp; + + icom_adapter = (struct icom_adapter *) + kzalloc(sizeof(struct icom_adapter), GFP_KERNEL); + + if (!icom_adapter) { + return -ENOMEM; + } + + list_for_each(tmp, &icom_adapter_head) { + cur_adapter_entry = + list_entry(tmp, struct icom_adapter, + icom_adapter_entry); + if (cur_adapter_entry->index != adapter_count) { + break; + } + adapter_count++; + } + + icom_adapter->index = adapter_count; + list_add_tail(&icom_adapter->icom_adapter_entry, tmp); + + *icom_adapter_ref = icom_adapter; + return 0; +} + +static void icom_free_adapter(struct icom_adapter *icom_adapter) +{ + list_del(&icom_adapter->icom_adapter_entry); + kfree(icom_adapter); +} + +static void icom_remove_adapter(struct icom_adapter *icom_adapter) +{ + struct icom_port *icom_port; + int index; + + for (index = 0; index < icom_adapter->numb_ports; index++) { + icom_port = &icom_adapter->port_info[index]; + + if (icom_port->status == ICOM_PORT_ACTIVE) { + dev_info(&icom_adapter->pci_dev->dev, + "Device removed\n"); + + uart_remove_one_port(&icom_uart_driver, + &icom_port->uart_port); + + /* be sure that DTR and RTS are dropped */ + writeb(0x00, &icom_port->dram->osr); + + /* Wait 0.1 Sec for simple Init to complete */ + msleep(100); + + /* Stop proccessor */ + stop_processor(icom_port); + + free_port_memory(icom_port); + } + } + + free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter); + iounmap(icom_adapter->base_addr); + pci_release_regions(icom_adapter->pci_dev); + icom_free_adapter(icom_adapter); +} + +static void icom_kref_release(struct kref *kref) +{ + struct icom_adapter *icom_adapter; + + icom_adapter = to_icom_adapter(kref); + icom_remove_adapter(icom_adapter); +} + +static int __devinit icom_probe(struct pci_dev *dev, + const struct pci_device_id *ent) +{ + int index; + unsigned int command_reg; + int retval; + struct icom_adapter *icom_adapter; + struct icom_port *icom_port; + + retval = pci_enable_device(dev); + if (retval) { + dev_err(&dev->dev, "Device enable FAILED\n"); + return retval; + } + + if ( (retval = pci_request_regions(dev, "icom"))) { + dev_err(&dev->dev, "pci_request_regions FAILED\n"); + pci_disable_device(dev); + return retval; + } + + pci_set_master(dev); + + if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { + dev_err(&dev->dev, "PCI Config read FAILED\n"); + return retval; + } + + pci_write_config_dword(dev, PCI_COMMAND, + command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER + | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); + + if (ent->driver_data == ADAPTER_V1) { + pci_write_config_dword(dev, 0x44, 0x8300830A); + } else { + pci_write_config_dword(dev, 0x44, 0x42004200); + pci_write_config_dword(dev, 0x48, 0x42004200); + } + + + retval = icom_alloc_adapter(&icom_adapter); + if (retval) { + dev_err(&dev->dev, "icom_alloc_adapter FAILED\n"); + retval = -EIO; + goto probe_exit0; + } + + icom_adapter->base_addr_pci = pci_resource_start(dev, 0); + icom_adapter->pci_dev = dev; + icom_adapter->version = ent->driver_data; + icom_adapter->subsystem_id = ent->subdevice; + + + retval = icom_init_ports(icom_adapter); + if (retval) { + dev_err(&dev->dev, "Port configuration failed\n"); + goto probe_exit1; + } + + icom_adapter->base_addr = pci_ioremap_bar(dev, 0); + + if (!icom_adapter->base_addr) + goto probe_exit1; + + /* save off irq and request irq line */ + if ( (retval = request_irq(dev->irq, icom_interrupt, + IRQF_DISABLED | IRQF_SHARED, ICOM_DRIVER_NAME, + (void *) icom_adapter))) { + goto probe_exit2; + } + + retval = icom_load_ports(icom_adapter); + + for (index = 0; index < icom_adapter->numb_ports; index++) { + icom_port = &icom_adapter->port_info[index]; + + if (icom_port->status == ICOM_PORT_ACTIVE) { + icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq; + icom_port->uart_port.type = PORT_ICOM; + icom_port->uart_port.iotype = UPIO_MEM; + icom_port->uart_port.membase = + (char *) icom_adapter->base_addr_pci; + icom_port->uart_port.fifosize = 16; + icom_port->uart_port.ops = &icom_ops; + icom_port->uart_port.line = + icom_port->port + icom_adapter->index * 4; + if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) { + icom_port->status = ICOM_PORT_OFF; + dev_err(&dev->dev, "Device add failed\n"); + } else + dev_info(&dev->dev, "Device added\n"); + } + } + + kref_init(&icom_adapter->kref); + return 0; + +probe_exit2: + iounmap(icom_adapter->base_addr); +probe_exit1: + icom_free_adapter(icom_adapter); + +probe_exit0: + pci_release_regions(dev); + pci_disable_device(dev); + + return retval; +} + +static void __devexit icom_remove(struct pci_dev *dev) +{ + struct icom_adapter *icom_adapter; + struct list_head *tmp; + + list_for_each(tmp, &icom_adapter_head) { + icom_adapter = list_entry(tmp, struct icom_adapter, + icom_adapter_entry); + if (icom_adapter->pci_dev == dev) { + kref_put(&icom_adapter->kref, icom_kref_release); + return; + } + } + + dev_err(&dev->dev, "Unable to find device to remove\n"); +} + +static struct pci_driver icom_pci_driver = { + .name = ICOM_DRIVER_NAME, + .id_table = icom_pci_table, + .probe = icom_probe, + .remove = __devexit_p(icom_remove), +}; + +static int __init icom_init(void) +{ + int ret; + + spin_lock_init(&icom_lock); + + ret = uart_register_driver(&icom_uart_driver); + if (ret) + return ret; + + ret = pci_register_driver(&icom_pci_driver); + + if (ret < 0) + uart_unregister_driver(&icom_uart_driver); + + return ret; +} + +static void __exit icom_exit(void) +{ + pci_unregister_driver(&icom_pci_driver); + uart_unregister_driver(&icom_uart_driver); +} + +module_init(icom_init); +module_exit(icom_exit); + +MODULE_AUTHOR("Michael Anderson "); +MODULE_DESCRIPTION("IBM iSeries Serial IOA driver"); +MODULE_SUPPORTED_DEVICE + ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("icom_call_setup.bin"); +MODULE_FIRMWARE("icom_res_dce.bin"); +MODULE_FIRMWARE("icom_asc.bin"); diff --git a/drivers/tty/serial/icom.h b/drivers/tty/serial/icom.h new file mode 100644 index 000000000000..c8029e0025c9 --- /dev/null +++ b/drivers/tty/serial/icom.h @@ -0,0 +1,287 @@ +/* + * icom.h + * + * Copyright (C) 2001 Michael Anderson, IBM Corporation + * + * Serial device driver include file. + * + * 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 + +#define BAUD_TABLE_LIMIT ((sizeof(icom_acfg_baud)/sizeof(int)) - 1) +static int icom_acfg_baud[] = { + 300, + 600, + 900, + 1200, + 1800, + 2400, + 3600, + 4800, + 7200, + 9600, + 14400, + 19200, + 28800, + 38400, + 57600, + 76800, + 115200, + 153600, + 230400, + 307200, + 460800, +}; + +struct icom_regs { + u32 control; /* Adapter Control Register */ + u32 interrupt; /* Adapter Interrupt Register */ + u32 int_mask; /* Adapter Interrupt Mask Reg */ + u32 int_pri; /* Adapter Interrupt Priority r */ + u32 int_reg_b; /* Adapter non-masked Interrupt */ + u32 resvd01; + u32 resvd02; + u32 resvd03; + u32 control_2; /* Adapter Control Register 2 */ + u32 interrupt_2; /* Adapter Interrupt Register 2 */ + u32 int_mask_2; /* Adapter Interrupt Mask 2 */ + u32 int_pri_2; /* Adapter Interrupt Prior 2 */ + u32 int_reg_2b; /* Adapter non-masked 2 */ +}; + +struct func_dram { + u32 reserved[108]; /* 0-1B0 reserved by personality code */ + u32 RcvStatusAddr; /* 1B0-1B3 Status Address for Next rcv */ + u8 RcvStnAddr; /* 1B4 Receive Station Addr */ + u8 IdleState; /* 1B5 Idle State */ + u8 IdleMonitor; /* 1B6 Idle Monitor */ + u8 FlagFillIdleTimer; /* 1B7 Flag Fill Idle Timer */ + u32 XmitStatusAddr; /* 1B8-1BB Transmit Status Address */ + u8 StartXmitCmd; /* 1BC Start Xmit Command */ + u8 HDLCConfigReg; /* 1BD Reserved */ + u8 CauseCode; /* 1BE Cause code for fatal error */ + u8 xchar; /* 1BF High priority send */ + u32 reserved3; /* 1C0-1C3 Reserved */ + u8 PrevCmdReg; /* 1C4 Reserved */ + u8 CmdReg; /* 1C5 Command Register */ + u8 async_config2; /* 1C6 Async Config Byte 2 */ + u8 async_config3; /* 1C7 Async Config Byte 3 */ + u8 dce_resvd[20]; /* 1C8-1DB DCE Rsvd */ + u8 dce_resvd21; /* 1DC DCE Rsvd (21st byte */ + u8 misc_flags; /* 1DD misc flags */ +#define V2_HARDWARE 0x40 +#define ICOM_HDW_ACTIVE 0x01 + u8 call_length; /* 1DE Phone #/CFI buff ln */ + u8 call_length2; /* 1DF Upper byte (unused) */ + u32 call_addr; /* 1E0-1E3 Phn #/CFI buff addr */ + u16 timer_value; /* 1E4-1E5 general timer value */ + u8 timer_command; /* 1E6 general timer cmd */ + u8 dce_command; /* 1E7 dce command reg */ + u8 dce_cmd_status; /* 1E8 dce command stat */ + u8 x21_r1_ioff; /* 1E9 dce ready counter */ + u8 x21_r0_ioff; /* 1EA dce not ready ctr */ + u8 x21_ralt_ioff; /* 1EB dce CNR counter */ + u8 x21_r1_ion; /* 1EC dce ready I on ctr */ + u8 rsvd_ier; /* 1ED Rsvd for IER (if ne */ + u8 ier; /* 1EE Interrupt Enable */ + u8 isr; /* 1EF Input Signal Reg */ + u8 osr; /* 1F0 Output Signal Reg */ + u8 reset; /* 1F1 Reset/Reload Reg */ + u8 disable; /* 1F2 Disable Reg */ + u8 sync; /* 1F3 Sync Reg */ + u8 error_stat; /* 1F4 Error Status */ + u8 cable_id; /* 1F5 Cable ID */ + u8 cs_length; /* 1F6 CS Load Length */ + u8 mac_length; /* 1F7 Mac Load Length */ + u32 cs_load_addr; /* 1F8-1FB Call Load PCI Addr */ + u32 mac_load_addr; /* 1FC-1FF Mac Load PCI Addr */ +}; + +/* + * adapter defines and structures + */ +#define ICOM_CONTROL_START_A 0x00000008 +#define ICOM_CONTROL_STOP_A 0x00000004 +#define ICOM_CONTROL_START_B 0x00000002 +#define ICOM_CONTROL_STOP_B 0x00000001 +#define ICOM_CONTROL_START_C 0x00000008 +#define ICOM_CONTROL_STOP_C 0x00000004 +#define ICOM_CONTROL_START_D 0x00000002 +#define ICOM_CONTROL_STOP_D 0x00000001 +#define ICOM_IRAM_OFFSET 0x1000 +#define ICOM_IRAM_SIZE 0x0C00 +#define ICOM_DCE_IRAM_OFFSET 0x0A00 +#define ICOM_CABLE_ID_VALID 0x01 +#define ICOM_CABLE_ID_MASK 0xF0 +#define ICOM_DISABLE 0x80 +#define CMD_XMIT_RCV_ENABLE 0xC0 +#define CMD_XMIT_ENABLE 0x40 +#define CMD_RCV_DISABLE 0x00 +#define CMD_RCV_ENABLE 0x80 +#define CMD_RESTART 0x01 +#define CMD_HOLD_XMIT 0x02 +#define CMD_SND_BREAK 0x04 +#define RS232_CABLE 0x06 +#define V24_CABLE 0x0E +#define V35_CABLE 0x0C +#define V36_CABLE 0x02 +#define NO_CABLE 0x00 +#define START_DOWNLOAD 0x80 +#define ICOM_INT_MASK_PRC_A 0x00003FFF +#define ICOM_INT_MASK_PRC_B 0x3FFF0000 +#define ICOM_INT_MASK_PRC_C 0x00003FFF +#define ICOM_INT_MASK_PRC_D 0x3FFF0000 +#define INT_RCV_COMPLETED 0x1000 +#define INT_XMIT_COMPLETED 0x2000 +#define INT_IDLE_DETECT 0x0800 +#define INT_RCV_DISABLED 0x0400 +#define INT_XMIT_DISABLED 0x0200 +#define INT_RCV_XMIT_SHUTDOWN 0x0100 +#define INT_FATAL_ERROR 0x0080 +#define INT_CABLE_PULL 0x0020 +#define INT_SIGNAL_CHANGE 0x0010 +#define HDLC_PPP_PURE_ASYNC 0x02 +#define HDLC_FF_FILL 0x00 +#define HDLC_HDW_FLOW 0x01 +#define START_XMIT 0x80 +#define ICOM_ACFG_DRIVE1 0x20 +#define ICOM_ACFG_NO_PARITY 0x00 +#define ICOM_ACFG_PARITY_ENAB 0x02 +#define ICOM_ACFG_PARITY_ODD 0x01 +#define ICOM_ACFG_8BPC 0x00 +#define ICOM_ACFG_7BPC 0x04 +#define ICOM_ACFG_6BPC 0x08 +#define ICOM_ACFG_5BPC 0x0C +#define ICOM_ACFG_1STOP_BIT 0x00 +#define ICOM_ACFG_2STOP_BIT 0x10 +#define ICOM_DTR 0x80 +#define ICOM_RTS 0x40 +#define ICOM_RI 0x08 +#define ICOM_DSR 0x80 +#define ICOM_DCD 0x20 +#define ICOM_CTS 0x40 + +#define NUM_XBUFFS 1 +#define NUM_RBUFFS 2 +#define RCV_BUFF_SZ 0x0200 +#define XMIT_BUFF_SZ 0x1000 +struct statusArea { + /**********************************************/ + /* Transmit Status Area */ + /**********************************************/ + struct xmit_status_area{ + u32 leNext; /* Next entry in Little Endian on Adapter */ + u32 leNextASD; + u32 leBuffer; /* Buffer for entry in LE for Adapter */ + u16 leLengthASD; + u16 leOffsetASD; + u16 leLength; /* Length of data in segment */ + u16 flags; +#define SA_FLAGS_DONE 0x0080 /* Done with Segment */ +#define SA_FLAGS_CONTINUED 0x8000 /* More Segments */ +#define SA_FLAGS_IDLE 0x4000 /* Mark IDLE after frm */ +#define SA_FLAGS_READY_TO_XMIT 0x0800 +#define SA_FLAGS_STAT_MASK 0x007F + } xmit[NUM_XBUFFS]; + + /**********************************************/ + /* Receive Status Area */ + /**********************************************/ + struct { + u32 leNext; /* Next entry in Little Endian on Adapter */ + u32 leNextASD; + u32 leBuffer; /* Buffer for entry in LE for Adapter */ + u16 WorkingLength; /* size of segment */ + u16 reserv01; + u16 leLength; /* Length of data in segment */ + u16 flags; +#define SA_FL_RCV_DONE 0x0010 /* Data ready */ +#define SA_FLAGS_OVERRUN 0x0040 +#define SA_FLAGS_PARITY_ERROR 0x0080 +#define SA_FLAGS_FRAME_ERROR 0x0001 +#define SA_FLAGS_FRAME_TRUNC 0x0002 +#define SA_FLAGS_BREAK_DET 0x0004 /* set conditionally by device driver, not hardware */ +#define SA_FLAGS_RCV_MASK 0xFFE6 + } rcv[NUM_RBUFFS]; +}; + +struct icom_adapter; + + +#define ICOM_MAJOR 243 +#define ICOM_MINOR_START 0 + +struct icom_port { + struct uart_port uart_port; + u8 imbed_modem; +#define ICOM_UNKNOWN 1 +#define ICOM_RVX 2 +#define ICOM_IMBED_MODEM 3 + unsigned char cable_id; + unsigned char read_status_mask; + unsigned char ignore_status_mask; + void __iomem * int_reg; + struct icom_regs __iomem *global_reg; + struct func_dram __iomem *dram; + int port; + struct statusArea *statStg; + dma_addr_t statStg_pci; + u32 *xmitRestart; + dma_addr_t xmitRestart_pci; + unsigned char *xmit_buf; + dma_addr_t xmit_buf_pci; + unsigned char *recv_buf; + dma_addr_t recv_buf_pci; + int next_rcv; + int put_length; + int status; +#define ICOM_PORT_ACTIVE 1 /* Port exists. */ +#define ICOM_PORT_OFF 0 /* Port does not exist. */ + int load_in_progress; + struct icom_adapter *adapter; +}; + +struct icom_adapter { + void __iomem * base_addr; + unsigned long base_addr_pci; + struct pci_dev *pci_dev; + struct icom_port port_info[4]; + int index; + int version; +#define ADAPTER_V1 0x0001 +#define ADAPTER_V2 0x0002 + u32 subsystem_id; +#define FOUR_PORT_MODEL 0x0252 +#define V2_TWO_PORTS_RVX 0x021A +#define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM 0x0251 + int numb_ports; + struct list_head icom_adapter_entry; + struct kref kref; +}; + +/* prototype */ +extern void iCom_sercons_init(void); + +struct lookup_proc_table { + u32 __iomem *global_control_reg; + unsigned long processor_id; +}; + +struct lookup_int_table { + u32 __iomem *global_int_mask; + unsigned long processor_id; +}; diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c new file mode 100644 index 000000000000..ab93763862d5 --- /dev/null +++ b/drivers/tty/serial/ifx6x60.c @@ -0,0 +1,1406 @@ +/**************************************************************************** + * + * Driver for the IFX 6x60 spi modem. + * + * Copyright (C) 2008 Option International + * Copyright (C) 2008 Filip Aben + * Denis Joseph Barrow + * Jan Dumon + * + * Copyright (C) 2009, 2010 Intel Corp + * Russ Gorby + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + * Driver modified by Intel from Option gtm501l_spi.c + * + * Notes + * o The driver currently assumes a single device only. If you need to + * change this then look for saved_ifx_dev and add a device lookup + * o The driver is intended to be big-endian safe but has never been + * tested that way (no suitable hardware). There are a couple of FIXME + * notes by areas that may need addressing + * o Some of the GPIO naming/setup assumptions may need revisiting if + * you need to use this driver for another platform. + * + *****************************************************************************/ +#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 + +#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_TTY_ID 0 +#define IFX_SPI_TIMEOUT_SEC 2 +#define IFX_SPI_HEADER_0 (-1) +#define IFX_SPI_HEADER_F (-2) + +/* forward reference */ +static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev); + +/* local variables */ +static int spi_b16 = 1; /* 8 or 16 bit word length */ +static struct tty_driver *tty_drv; +static struct ifx_spi_device *saved_ifx_dev; +static struct lock_class_key ifx_spi_key; + +/* GPIO/GPE settings */ + +/** + * mrdy_set_high - set MRDY GPIO + * @ifx: device we are controlling + * + */ +static inline void mrdy_set_high(struct ifx_spi_device *ifx) +{ + gpio_set_value(ifx->gpio.mrdy, 1); +} + +/** + * mrdy_set_low - clear MRDY GPIO + * @ifx: device we are controlling + * + */ +static inline void mrdy_set_low(struct ifx_spi_device *ifx) +{ + gpio_set_value(ifx->gpio.mrdy, 0); +} + +/** + * ifx_spi_power_state_set + * @ifx_dev: our SPI device + * @val: bits to set + * + * Set bit in power status and signal power system if status becomes non-0 + */ +static void +ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&ifx_dev->power_lock, flags); + + /* + * if power status is already non-0, just update, else + * tell power system + */ + if (!ifx_dev->power_status) + pm_runtime_get(&ifx_dev->spi_dev->dev); + ifx_dev->power_status |= val; + + spin_unlock_irqrestore(&ifx_dev->power_lock, flags); +} + +/** + * ifx_spi_power_state_clear - clear power bit + * @ifx_dev: our SPI device + * @val: bits to clear + * + * clear bit in power status and signal power system if status becomes 0 + */ +static void +ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&ifx_dev->power_lock, flags); + + if (ifx_dev->power_status) { + ifx_dev->power_status &= ~val; + if (!ifx_dev->power_status) + pm_runtime_put(&ifx_dev->spi_dev->dev); + } + + spin_unlock_irqrestore(&ifx_dev->power_lock, flags); +} + +/** + * swap_buf + * @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) +{ + int n; + + len = ((len + 1) >> 1); + if ((void *)&buf[len] > end) { + pr_err("swap_buf: swap exceeds boundary (%p > %p)!", + &buf[len], end); + return; + } + for (n = 0; n < len; n++) { + *buf = cpu_to_be16(*buf); + buf++; + } +} + +/** + * mrdy_assert - assert MRDY line + * @ifx_dev: our SPI device + * + * Assert mrdy and set timer to wait for SRDY interrupt, if SRDY is low + * now. + * + * FIXME: Can SRDY even go high as we are running this code ? + */ +static void mrdy_assert(struct ifx_spi_device *ifx_dev) +{ + int val = gpio_get_value(ifx_dev->gpio.srdy); + 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); + + } + } + ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_DATA_PENDING); + mrdy_set_high(ifx_dev); +} + +/** + * ifx_spi_hangup - hang up an IFX device + * @ifx_dev: our SPI device + * + * Hang up the tty attached to the IFX device if one is currently + * open. If not take no action + */ +static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) +{ + struct tty_port *pport = &ifx_dev->tty_port; + struct tty_struct *tty = tty_port_tty_get(pport); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } +} + +/** + * ifx_spi_timeout - SPI timeout + * @arg: our SPI device + * + * The SPI has timed out: hang up the tty. Users will then see a hangup + * and error events. + */ +static void ifx_spi_timeout(unsigned long arg) +{ + struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; + + dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); + ifx_spi_ttyhangup(ifx_dev); + mrdy_set_low(ifx_dev); + clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); +} + +/* char/tty operations */ + +/** + * ifx_spi_tiocmget - get modem lines + * @tty: our tty device + * @filp: file handle issuing the request + * + * Map the signal state into Linux modem flags and report the value + * in Linux terms + */ +static int ifx_spi_tiocmget(struct tty_struct *tty, struct file *filp) +{ + unsigned int value; + struct ifx_spi_device *ifx_dev = tty->driver_data; + + value = + (test_bit(IFX_SPI_RTS, &ifx_dev->signal_state) ? TIOCM_RTS : 0) | + (test_bit(IFX_SPI_DTR, &ifx_dev->signal_state) ? TIOCM_DTR : 0) | + (test_bit(IFX_SPI_CTS, &ifx_dev->signal_state) ? TIOCM_CTS : 0) | + (test_bit(IFX_SPI_DSR, &ifx_dev->signal_state) ? TIOCM_DSR : 0) | + (test_bit(IFX_SPI_DCD, &ifx_dev->signal_state) ? TIOCM_CAR : 0) | + (test_bit(IFX_SPI_RI, &ifx_dev->signal_state) ? TIOCM_RNG : 0); + return value; +} + +/** + * ifx_spi_tiocmset - set modem bits + * @tty: the tty structure + * @filp: file handle issuing the request + * @set: bits to set + * @clear: bits to clear + * + * The IFX6x60 only supports DTR and RTS. Set them accordingly + * and flag that an update to the modem is needed. + * + * FIXME: do we need to kick the tranfers when we do this ? + */ +static int ifx_spi_tiocmset(struct tty_struct *tty, struct file *filp, + unsigned int set, unsigned int clear) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + + if (set & TIOCM_RTS) + set_bit(IFX_SPI_RTS, &ifx_dev->signal_state); + if (set & TIOCM_DTR) + set_bit(IFX_SPI_DTR, &ifx_dev->signal_state); + if (clear & TIOCM_RTS) + clear_bit(IFX_SPI_RTS, &ifx_dev->signal_state); + if (clear & TIOCM_DTR) + clear_bit(IFX_SPI_DTR, &ifx_dev->signal_state); + + set_bit(IFX_SPI_UPDATE, &ifx_dev->signal_state); + return 0; +} + +/** + * ifx_spi_open - called on tty open + * @tty: our tty device + * @filp: file handle being associated with the tty + * + * Open the tty interface. We let the tty_port layer do all the work + * for us. + * + * FIXME: Remove single device assumption and saved_ifx_dev + */ +static int ifx_spi_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(&saved_ifx_dev->tty_port, tty, filp); +} + +/** + * ifx_spi_close - called when our tty closes + * @tty: the tty being closed + * @filp: the file handle being closed + * + * Perform the close of the tty. We use the tty_port layer to do all + * our hard work. + */ +static void ifx_spi_close(struct tty_struct *tty, struct file *filp) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + tty_port_close(&ifx_dev->tty_port, tty, filp); + /* FIXME: should we do an ifx_spi_reset here ? */ +} + +/** + * ifx_decode_spi_header - decode received header + * @buffer: the received data + * @length: decoded length + * @more: decoded more flag + * @received_cts: status of cts we received + * + * Note how received_cts is handled -- if header is all F it is left + * the same as it was, if header is all 0 it is set to 0 otherwise it is + * taken from the incoming header. + * + * FIXME: endianness + */ +static int ifx_spi_decode_spi_header(unsigned char *buffer, int *length, + unsigned char *more, unsigned char *received_cts) +{ + u16 h1; + u16 h2; + u16 *in_buffer = (u16 *)buffer; + + h1 = *in_buffer; + h2 = *(in_buffer+1); + + if (h1 == 0 && h2 == 0) { + *received_cts = 0; + return IFX_SPI_HEADER_0; + } else if (h1 == 0xffff && h2 == 0xffff) { + /* spi_slave_cts remains as it was */ + return IFX_SPI_HEADER_F; + } + + *length = h1 & 0xfff; /* upper bits of byte are flags */ + *more = (buffer[1] >> IFX_SPI_MORE_BIT) & 1; + *received_cts = (buffer[3] >> IFX_SPI_CTS_BIT) & 1; + return 0; +} + +/** + * ifx_setup_spi_header - set header fields + * @txbuffer: pointer to start of SPI buffer + * @tx_count: bytes + * @more: indicate if more to follow + * + * Format up an SPI header for a transfer + * + * FIXME: endianness? + */ +static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, + unsigned char more) +{ + *(u16 *)(txbuffer) = tx_count; + *(u16 *)(txbuffer+2) = IFX_SPI_PAYLOAD_SIZE; + txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK; +} + +/** + * ifx_spi_wakeup_serial - SPI space made + * @port_data: our SPI device + * + * We have emptied the FIFO enough that we want to get more data + * queued into it. Poke the line discipline via tty_wakeup so that + * it will feed us more bits + */ +static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) +{ + struct tty_struct *tty; + + tty = tty_port_tty_get(&ifx_dev->tty_port); + if (!tty) + return; + tty_wakeup(tty); + tty_kref_put(tty); +} + +/** + * ifx_spi_prepare_tx_buffer - prepare transmit frame + * @ifx_dev: our SPI device + * + * The transmit buffr needs a header and various other bits of + * information followed by as much data as we can pull from the FIFO + * and transfer. This function formats up a suitable buffer in the + * ifx_dev->tx_buffer + * + * FIXME: performance - should we wake the tty when the queue is half + * empty ? + */ +static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) +{ + int temp_count; + int queue_length; + int tx_count; + unsigned char *tx_buffer; + + tx_buffer = ifx_dev->tx_buffer; + memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE); + + /* make room for required SPI header */ + tx_buffer += IFX_SPI_HEADER_OVERHEAD; + tx_count = IFX_SPI_HEADER_OVERHEAD; + + /* clear to signal no more data if this turns out to be the + * last buffer sent in a sequence */ + ifx_dev->spi_more = 0; + + /* if modem cts is set, just send empty buffer */ + if (!ifx_dev->spi_slave_cts) { + /* see if there's tx data */ + queue_length = kfifo_len(&ifx_dev->tx_fifo); + if (queue_length != 0) { + /* data to mux -- see if there's room for it */ + temp_count = min(queue_length, IFX_SPI_PAYLOAD_SIZE); + temp_count = kfifo_out_locked(&ifx_dev->tx_fifo, + tx_buffer, temp_count, + &ifx_dev->fifo_lock); + + /* update buffer pointer and data count in message */ + tx_buffer += temp_count; + tx_count += temp_count; + if (temp_count == queue_length) + /* poke port to get more data */ + ifx_spi_wakeup_serial(ifx_dev); + else /* more data in port, use next SPI message */ + ifx_dev->spi_more = 1; + } + } + /* have data and info for header -- set up SPI header in buffer */ + /* spi header needs payload size, not entire buffer size */ + ifx_spi_setup_spi_header(ifx_dev->tx_buffer, + 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->tx_buffer[IFX_SPI_TRANSFER_SIZE]); + return tx_count; +} + +/** + * ifx_spi_write - line discipline write + * @tty: our tty device + * @buf: pointer to buffer to write (kernel space) + * @count: size of buffer + * + * Write the characters we have been given into the FIFO. If the device + * is not active then activate it, when the SRDY line is asserted back + * this will commence I/O + */ +static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + 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); + return tx_count; +} + +/** + * ifx_spi_chars_in_buffer - line discipline helper + * @tty: our tty device + * + * Report how much data we can accept before we drop bytes. As we use + * a simple FIFO this is nice and easy. + */ +static int ifx_spi_write_room(struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + return IFX_SPI_FIFO_SIZE - kfifo_len(&ifx_dev->tx_fifo); +} + +/** + * ifx_spi_chars_in_buffer - line discipline helper + * @tty: our tty device + * + * Report how many characters we have buffered. In our case this is the + * number of bytes sitting in our transmit FIFO. + */ +static int ifx_spi_chars_in_buffer(struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + return kfifo_len(&ifx_dev->tx_fifo); +} + +/** + * ifx_port_hangup + * @port: our tty port + * + * tty port hang up. Called when tty_hangup processing is invoked either + * by loss of carrier, or by software (eg vhangup). Serialized against + * activate/shutdown by the tty layer. + */ +static void ifx_spi_hangup(struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + tty_port_hangup(&ifx_dev->tty_port); +} + +/** + * ifx_port_activate + * @port: our tty port + * + * tty port activate method - called for first open. Serialized + * with hangup and shutdown by the tty layer. + */ +static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = + container_of(port, struct ifx_spi_device, tty_port); + + /* clear any old data; can't do this in 'close' */ + kfifo_reset(&ifx_dev->tx_fifo); + + /* put port data into this tty */ + tty->driver_data = ifx_dev; + + /* allows flip string push from int context */ + tty->low_latency = 1; + + return 0; +} + +/** + * ifx_port_shutdown + * @port: our tty port + * + * tty port shutdown method - called for last port close. Serialized + * with hangup and activate by the tty layer. + */ +static void ifx_port_shutdown(struct tty_port *port) +{ + struct ifx_spi_device *ifx_dev = + container_of(port, struct ifx_spi_device, tty_port); + + mrdy_set_low(ifx_dev); + clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); + tasklet_kill(&ifx_dev->io_work_tasklet); +} + +static const struct tty_port_operations ifx_tty_port_ops = { + .activate = ifx_port_activate, + .shutdown = ifx_port_shutdown, +}; + +static const struct tty_operations ifx_spi_serial_ops = { + .open = ifx_spi_open, + .close = ifx_spi_close, + .write = ifx_spi_write, + .hangup = ifx_spi_hangup, + .write_room = ifx_spi_write_room, + .chars_in_buffer = ifx_spi_chars_in_buffer, + .tiocmget = ifx_spi_tiocmget, + .tiocmset = ifx_spi_tiocmset, +}; + +/** + * ifx_spi_insert_fip_string - queue received data + * @ifx_ser: our SPI device + * @chars: buffer we have received + * @size: number of chars reeived + * + * Queue bytes to the tty assuming the tty side is currently open. If + * not the discard the data. + */ +static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, + unsigned char *chars, size_t size) +{ + struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port); + if (!tty) + return; + tty_insert_flip_string(tty, chars, size); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +/** + * ifx_spi_complete - SPI transfer completed + * @ctx: our SPI device + * + * An SPI transfer has completed. Process any received data and kick off + * any further transmits we can commence. + */ +static void ifx_spi_complete(void *ctx) +{ + struct ifx_spi_device *ifx_dev = ctx; + struct tty_struct *tty; + struct tty_ldisc *ldisc = NULL; + int length; + int actual_length; + unsigned char more; + unsigned char cts; + int local_write_pending = 0; + int queue_length; + int srdy; + int decode_result; + + mrdy_set_low(ifx_dev); + + 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->rx_buffer[IFX_SPI_HEADER_OVERHEAD]); + decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer, + &length, &more, &cts); + if (decode_result == IFX_SPI_HEADER_0) { + dev_dbg(&ifx_dev->spi_dev->dev, + "ignore input: invalid header 0"); + ifx_dev->spi_slave_cts = 0; + goto complete_exit; + } else if (decode_result == IFX_SPI_HEADER_F) { + dev_dbg(&ifx_dev->spi_dev->dev, + "ignore input: invalid header F"); + goto complete_exit; + } + + ifx_dev->spi_slave_cts = cts; + + actual_length = min((unsigned int)length, + ifx_dev->spi_msg.actual_length); + swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), + actual_length, + &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]); + ifx_spi_insert_flip_string( + ifx_dev, + ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD, + (size_t)actual_length); + } else { + dev_dbg(&ifx_dev->spi_dev->dev, "SPI transfer error %d", + ifx_dev->spi_msg.status); + } + +complete_exit: + if (ifx_dev->write_pending) { + ifx_dev->write_pending = 0; + local_write_pending = 1; + } + + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags)); + + queue_length = kfifo_len(&ifx_dev->tx_fifo); + srdy = gpio_get_value(ifx_dev->gpio.srdy); + if (!srdy) + ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY); + + /* schedule output if there is more to do */ + if (test_and_clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags)) + tasklet_schedule(&ifx_dev->io_work_tasklet); + else { + if (more || ifx_dev->spi_more || queue_length > 0 || + local_write_pending) { + if (ifx_dev->spi_slave_cts) { + if (more) + mrdy_assert(ifx_dev); + } else + mrdy_assert(ifx_dev); + } else { + /* + * poke line discipline driver if any for more data + * may or may not get more data to write + * for now, say not busy + */ + ifx_spi_power_state_clear(ifx_dev, + IFX_SPI_POWER_DATA_PENDING); + tty = tty_port_tty_get(&ifx_dev->tty_port); + if (tty) { + ldisc = tty_ldisc_ref(tty); + if (ldisc) { + ldisc->ops->write_wakeup(tty); + tty_ldisc_deref(ldisc); + } + tty_kref_put(tty); + } + } + } +} + +/** + * ifx_spio_io - I/O tasklet + * @data: our SPI device + * + * Queue data for transmission if possible and then kick off the + * transfer. + */ +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 (ifx_dev->gpio.unack_srdy_int_nb > 0) + ifx_dev->gpio.unack_srdy_int_nb--; + + ifx_spi_prepare_tx_buffer(ifx_dev); + + spi_message_init(&ifx_dev->spi_msg); + INIT_LIST_HEAD(&ifx_dev->spi_msg.queue); + + ifx_dev->spi_msg.context = ifx_dev; + ifx_dev->spi_msg.complete = ifx_spi_complete; + + /* set up our spi transfer */ + /* note len is BYTES, not transfers */ + ifx_dev->spi_xfer.len = IFX_SPI_TRANSFER_SIZE; + ifx_dev->spi_xfer.cs_change = 0; + ifx_dev->spi_xfer.speed_hz = 12500000; + /* ifx_dev->spi_xfer.speed_hz = 390625; */ + ifx_dev->spi_xfer.bits_per_word = spi_b16 ? 16 : 8; + + ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer; + ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer; + + /* + * setup dma pointers + */ + if (ifx_dev->is_6160) { + ifx_dev->spi_msg.is_dma_mapped = 1; + ifx_dev->tx_dma = ifx_dev->tx_bus; + ifx_dev->rx_dma = ifx_dev->rx_bus; + ifx_dev->spi_xfer.tx_dma = ifx_dev->tx_dma; + ifx_dev->spi_xfer.rx_dma = ifx_dev->rx_dma; + } else { + ifx_dev->spi_msg.is_dma_mapped = 0; + ifx_dev->tx_dma = (dma_addr_t)0; + ifx_dev->rx_dma = (dma_addr_t)0; + ifx_dev->spi_xfer.tx_dma = (dma_addr_t)0; + ifx_dev->spi_xfer.rx_dma = (dma_addr_t)0; + } + + spi_message_add_tail(&ifx_dev->spi_xfer, &ifx_dev->spi_msg); + + /* Assert MRDY. This may have already been done by the write + * routine. + */ + mrdy_assert(ifx_dev); + + retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg); + if (retval) { + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, + &ifx_dev->flags); + tasklet_schedule(&ifx_dev->io_work_tasklet); + return; + } + } else + ifx_dev->write_pending = 1; +} + +/** + * ifx_spi_free_port - free up the tty side + * @ifx_dev: IFX device going away + * + * Unregister and free up a port when the device goes away + */ +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); + kfifo_free(&ifx_dev->tx_fifo); +} + +/** + * ifx_spi_create_port - create a new port + * @ifx_dev: our spi device + * + * Allocate and initialise the tty port that goes with this interface + * and add it to the tty layer so that it can be opened. + */ +static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) +{ + int ret = 0; + struct tty_port *pport = &ifx_dev->tty_port; + + spin_lock_init(&ifx_dev->fifo_lock); + lockdep_set_class_and_subclass(&ifx_dev->fifo_lock, + &ifx_spi_key, 0); + + if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE, GFP_KERNEL)) { + ret = -ENOMEM; + goto error_ret; + } + + pport->ops = &ifx_tty_port_ops; + tty_port_init(pport); + ifx_dev->minor = IFX_SPI_TTY_ID; + ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor, + &ifx_dev->spi_dev->dev); + if (IS_ERR(ifx_dev->tty_dev)) { + dev_dbg(&ifx_dev->spi_dev->dev, + "%s: registering tty device failed", __func__); + ret = PTR_ERR(ifx_dev->tty_dev); + goto error_ret; + } + return 0; + +error_ret: + ifx_spi_free_port(ifx_dev); + return ret; +} + +/** + * ifx_spi_handle_srdy - handle SRDY + * @ifx_dev: device asserting SRDY + * + * Check our device state and see what we need to kick off when SRDY + * is asserted. This usually means killing the timer and firing off the + * I/O processing. + */ +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); + clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); + } + + ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_SRDY); + + if (!test_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) + tasklet_schedule(&ifx_dev->io_work_tasklet); + else + set_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); +} + +/** + * ifx_spi_srdy_interrupt - SRDY asserted + * @irq: our IRQ number + * @dev: our ifx device + * + * The modem asserted SRDY. Handle the srdy event + */ +static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev) +{ + struct ifx_spi_device *ifx_dev = dev; + ifx_dev->gpio.unack_srdy_int_nb++; + ifx_spi_handle_srdy(ifx_dev); + return IRQ_HANDLED; +} + +/** + * ifx_spi_reset_interrupt - Modem has changed reset state + * @irq: interrupt number + * @dev: our device pointer + * + * The modem has either entered or left reset state. Check the GPIO + * line to see which. + * + * FIXME: review locking on MR_INPROGRESS versus + * parallel unsolicited reset/solicited reset + */ +static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) +{ + struct ifx_spi_device *ifx_dev = dev; + int val = gpio_get_value(ifx_dev->gpio.reset_out); + int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state); + + if (val == 0) { + /* entered reset */ + set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); + if (!solreset) { + /* unsolicited reset */ + ifx_spi_ttyhangup(ifx_dev); + } + } else { + /* exited reset */ + clear_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); + if (solreset) { + set_bit(MR_COMPLETE, &ifx_dev->mdm_reset_state); + wake_up(&ifx_dev->mdm_reset_wait); + } + } + return IRQ_HANDLED; +} + +/** + * ifx_spi_free_device - free device + * @ifx_dev: device to free + * + * Free the IFX device + */ +static void ifx_spi_free_device(struct ifx_spi_device *ifx_dev) +{ + ifx_spi_free_port(ifx_dev); + dma_free_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + ifx_dev->tx_buffer, + ifx_dev->tx_bus); + dma_free_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + ifx_dev->rx_buffer, + ifx_dev->rx_bus); +} + +/** + * ifx_spi_reset - reset modem + * @ifx_dev: modem to reset + * + * Perform a reset on the modem + */ +static int ifx_spi_reset(struct ifx_spi_device *ifx_dev) +{ + int ret; + /* + * set up modem power, reset + * + * delays are required on some platforms for the modem + * to reset properly + */ + set_bit(MR_START, &ifx_dev->mdm_reset_state); + gpio_set_value(ifx_dev->gpio.po, 0); + gpio_set_value(ifx_dev->gpio.reset, 0); + msleep(25); + gpio_set_value(ifx_dev->gpio.reset, 1); + msleep(1); + gpio_set_value(ifx_dev->gpio.po, 1); + msleep(1); + gpio_set_value(ifx_dev->gpio.po, 0); + ret = wait_event_timeout(ifx_dev->mdm_reset_wait, + test_bit(MR_COMPLETE, + &ifx_dev->mdm_reset_state), + IFX_RESET_TIMEOUT); + if (!ret) + dev_warn(&ifx_dev->spi_dev->dev, "Modem reset timeout: (state:%lx)", + ifx_dev->mdm_reset_state); + + ifx_dev->mdm_reset_state = 0; + return ret; +} + +/** + * ifx_spi_spi_probe - probe callback + * @spi: our possible matching SPI device + * + * Probe for a 6x60 modem on SPI bus. Perform any needed device and + * GPIO setup. + * + * FIXME: + * - Support for multiple devices + * - Split out MID specific GPIO handling eventually + */ + +static int ifx_spi_spi_probe(struct spi_device *spi) +{ + int ret; + int srdy; + struct ifx_modem_platform_data *pl_data = NULL; + struct ifx_spi_device *ifx_dev; + + if (saved_ifx_dev) { + dev_dbg(&spi->dev, "ignoring subsequent detection"); + return -ENODEV; + } + + /* initialize structure to hold our device variables */ + ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL); + if (!ifx_dev) { + dev_err(&spi->dev, "spi device allocation failed"); + return -ENOMEM; + } + saved_ifx_dev = ifx_dev; + ifx_dev->spi_dev = spi; + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); + spin_lock_init(&ifx_dev->write_lock); + spin_lock_init(&ifx_dev->power_lock); + ifx_dev->power_status = 0; + init_timer(&ifx_dev->spi_timer); + ifx_dev->spi_timer.function = ifx_spi_timeout; + ifx_dev->spi_timer.data = (unsigned long)ifx_dev; + ifx_dev->is_6160 = pl_data->is_6160; + + /* ensure SPI protocol flags are initialized to enable transfer */ + ifx_dev->spi_more = 0; + ifx_dev->spi_slave_cts = 0; + + /*initialize transfer and dma buffers */ + ifx_dev->tx_buffer = dma_alloc_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + &ifx_dev->tx_bus, + GFP_KERNEL); + if (!ifx_dev->tx_buffer) { + dev_err(&spi->dev, "DMA-TX buffer allocation failed"); + ret = -ENOMEM; + goto error_ret; + } + ifx_dev->rx_buffer = dma_alloc_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + &ifx_dev->rx_bus, + GFP_KERNEL); + if (!ifx_dev->rx_buffer) { + dev_err(&spi->dev, "DMA-RX buffer allocation failed"); + ret = -ENOMEM; + goto error_ret; + } + + /* initialize waitq for modem reset */ + init_waitqueue_head(&ifx_dev->mdm_reset_wait); + + spi_set_drvdata(spi, ifx_dev); + tasklet_init(&ifx_dev->io_work_tasklet, ifx_spi_io, + (unsigned long)ifx_dev); + + set_bit(IFX_SPI_STATE_PRESENT, &ifx_dev->flags); + + /* create our tty port */ + ret = ifx_spi_create_port(ifx_dev); + if (ret != 0) { + dev_err(&spi->dev, "create default tty port failed"); + goto error_ret; + } + + pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; + if (pl_data) { + ifx_dev->gpio.reset = pl_data->rst_pmu; + ifx_dev->gpio.po = pl_data->pwr_on; + ifx_dev->gpio.mrdy = pl_data->mrdy; + ifx_dev->gpio.srdy = pl_data->srdy; + ifx_dev->gpio.reset_out = pl_data->rst_out; + } else { + dev_err(&spi->dev, "missing platform data!"); + ret = -ENODEV; + goto error_ret; + } + + dev_info(&spi->dev, "gpios %d, %d, %d, %d, %d", + ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy, + ifx_dev->gpio.srdy, ifx_dev->gpio.reset_out); + + /* Configure gpios */ + ret = gpio_request(ifx_dev->gpio.reset, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET)", + ifx_dev->gpio.reset); + goto error_ret; + } + ret += gpio_direction_output(ifx_dev->gpio.reset, 0); + ret += gpio_export(ifx_dev->gpio.reset, 1); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (RESET)", + ifx_dev->gpio.reset); + ret = -EBUSY; + goto error_ret2; + } + + ret = gpio_request(ifx_dev->gpio.po, "ifxModem"); + ret += gpio_direction_output(ifx_dev->gpio.po, 0); + ret += gpio_export(ifx_dev->gpio.po, 1); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (ON)", + ifx_dev->gpio.po); + ret = -EBUSY; + goto error_ret3; + } + + ret = gpio_request(ifx_dev->gpio.mrdy, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (MRDY)", + ifx_dev->gpio.mrdy); + goto error_ret3; + } + ret += gpio_export(ifx_dev->gpio.mrdy, 1); + ret += gpio_direction_output(ifx_dev->gpio.mrdy, 0); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (MRDY)", + ifx_dev->gpio.mrdy); + ret = -EBUSY; + goto error_ret4; + } + + ret = gpio_request(ifx_dev->gpio.srdy, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (SRDY)", + ifx_dev->gpio.srdy); + ret = -EBUSY; + goto error_ret4; + } + ret += gpio_export(ifx_dev->gpio.srdy, 1); + ret += gpio_direction_input(ifx_dev->gpio.srdy); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (SRDY)", + ifx_dev->gpio.srdy); + ret = -EBUSY; + goto error_ret5; + } + + ret = gpio_request(ifx_dev->gpio.reset_out, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET_OUT)", + ifx_dev->gpio.reset_out); + goto error_ret5; + } + ret += gpio_export(ifx_dev->gpio.reset_out, 1); + ret += gpio_direction_input(ifx_dev->gpio.reset_out); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (RESET_OUT)", + ifx_dev->gpio.reset_out); + ret = -EBUSY; + goto error_ret6; + } + + ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out), + ifx_spi_reset_interrupt, + IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME, + (void *)ifx_dev); + if (ret) { + dev_err(&spi->dev, "Unable to get irq %x\n", + gpio_to_irq(ifx_dev->gpio.reset_out)); + goto error_ret6; + } + + ret = ifx_spi_reset(ifx_dev); + + ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy), + ifx_spi_srdy_interrupt, + IRQF_TRIGGER_RISING, DRVNAME, + (void *)ifx_dev); + if (ret) { + dev_err(&spi->dev, "Unable to get irq %x", + gpio_to_irq(ifx_dev->gpio.srdy)); + goto error_ret7; + } + + /* set pm runtime power state and register with power system */ + pm_runtime_set_active(&spi->dev); + pm_runtime_enable(&spi->dev); + + /* handle case that modem is already signaling SRDY */ + /* no outgoing tty open at this point, this just satisfies the + * modem's read and should reset communication properly + */ + srdy = gpio_get_value(ifx_dev->gpio.srdy); + + if (srdy) { + mrdy_assert(ifx_dev); + ifx_spi_handle_srdy(ifx_dev); + } else + mrdy_set_low(ifx_dev); + return 0; + +error_ret7: + free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); +error_ret6: + gpio_free(ifx_dev->gpio.srdy); +error_ret5: + gpio_free(ifx_dev->gpio.mrdy); +error_ret4: + gpio_free(ifx_dev->gpio.reset); +error_ret3: + gpio_free(ifx_dev->gpio.po); +error_ret2: + gpio_free(ifx_dev->gpio.reset_out); +error_ret: + ifx_spi_free_device(ifx_dev); + saved_ifx_dev = NULL; + return ret; +} + +/** + * ifx_spi_spi_remove - SPI device was removed + * @spi: SPI device + * + * FIXME: We should be shutting the device down here not in + * the module unload path. + */ + +static int ifx_spi_spi_remove(struct spi_device *spi) +{ + struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); + /* stop activity */ + tasklet_kill(&ifx_dev->io_work_tasklet); + /* free irq */ + free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); + free_irq(gpio_to_irq(ifx_dev->gpio.srdy), (void *)ifx_dev); + + gpio_free(ifx_dev->gpio.srdy); + gpio_free(ifx_dev->gpio.mrdy); + gpio_free(ifx_dev->gpio.reset); + gpio_free(ifx_dev->gpio.po); + gpio_free(ifx_dev->gpio.reset_out); + + /* free allocations */ + ifx_spi_free_device(ifx_dev); + + saved_ifx_dev = NULL; + return 0; +} + +/** + * ifx_spi_spi_shutdown - called on SPI shutdown + * @spi: SPI device + * + * No action needs to be taken here + */ + +static void ifx_spi_spi_shutdown(struct spi_device *spi) +{ +} + +/* + * various suspends and resumes have nothing to do + * no hardware to save state for + */ + +/** + * ifx_spi_spi_suspend - suspend SPI on system suspend + * @dev: device being suspended + * + * Suspend the SPI side. No action needed on Intel MID platforms, may + * need extending for other systems. + */ +static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg) +{ + return 0; +} + +/** + * ifx_spi_spi_resume - resume SPI side on system resume + * @dev: device being suspended + * + * Suspend the SPI side. No action needed on Intel MID platforms, may + * need extending for other systems. + */ +static int ifx_spi_spi_resume(struct spi_device *spi) +{ + return 0; +} + +/** + * ifx_spi_pm_suspend - suspend modem on system suspend + * @dev: device being suspended + * + * Suspend the modem. No action needed on Intel MID platforms, may + * need extending for other systems. + */ +static int ifx_spi_pm_suspend(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_resume - resume modem on system resume + * @dev: device being suspended + * + * Allow the modem to resume. No action needed. + * + * FIXME: do we need to reset anything here ? + */ +static int ifx_spi_pm_resume(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_runtime_resume - suspend modem + * @dev: device being suspended + * + * Allow the modem to resume. No action needed. + */ +static int ifx_spi_pm_runtime_resume(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_runtime_suspend - suspend modem + * @dev: device being suspended + * + * Allow the modem to suspend and thus suspend to continue up the + * device tree. + */ +static int ifx_spi_pm_runtime_suspend(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_runtime_idle - check if modem idle + * @dev: our device + * + * Check conditions and queue runtime suspend if idle. + */ +static int ifx_spi_pm_runtime_idle(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); + + if (!ifx_dev->power_status) + pm_runtime_suspend(dev); + + return 0; +} + +static const struct dev_pm_ops ifx_spi_pm = { + .resume = ifx_spi_pm_resume, + .suspend = ifx_spi_pm_suspend, + .runtime_resume = ifx_spi_pm_runtime_resume, + .runtime_suspend = ifx_spi_pm_runtime_suspend, + .runtime_idle = ifx_spi_pm_runtime_idle +}; + +static const struct spi_device_id ifx_id_table[] = { + {"ifx6160", 0}, + {"ifx6260", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, ifx_id_table); + +/* spi operations */ +static const struct spi_driver ifx_spi_driver_6160 = { + .driver = { + .name = "ifx6160", + .bus = &spi_bus_type, + .pm = &ifx_spi_pm, + .owner = THIS_MODULE}, + .probe = ifx_spi_spi_probe, + .shutdown = ifx_spi_spi_shutdown, + .remove = __devexit_p(ifx_spi_spi_remove), + .suspend = ifx_spi_spi_suspend, + .resume = ifx_spi_spi_resume, + .id_table = ifx_id_table +}; + +/** + * ifx_spi_exit - module exit + * + * Unload the module. + */ + +static void __exit ifx_spi_exit(void) +{ + /* unregister */ + tty_unregister_driver(tty_drv); + spi_unregister_driver((void *)&ifx_spi_driver_6160); +} + +/** + * ifx_spi_init - module entry point + * + * Initialise the SPI and tty interfaces for the IFX SPI driver + * We need to initialize upper-edge spi driver after the tty + * driver because otherwise the spi probe will race + */ + +static int __init ifx_spi_init(void) +{ + int result; + + tty_drv = alloc_tty_driver(1); + if (!tty_drv) { + pr_err("%s: alloc_tty_driver failed", DRVNAME); + return -ENOMEM; + } + + tty_drv->magic = TTY_DRIVER_MAGIC; + tty_drv->owner = THIS_MODULE; + tty_drv->driver_name = DRVNAME; + tty_drv->name = TTYNAME; + tty_drv->minor_start = IFX_SPI_TTY_ID; + tty_drv->num = 1; + tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + tty_drv->subtype = SERIAL_TYPE_NORMAL; + tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_drv->init_termios = tty_std_termios; + + tty_set_operations(tty_drv, &ifx_spi_serial_ops); + + result = tty_register_driver(tty_drv); + if (result) { + pr_err("%s: tty_register_driver failed(%d)", + DRVNAME, result); + put_tty_driver(tty_drv); + return result; + } + + result = spi_register_driver((void *)&ifx_spi_driver_6160); + if (result) { + pr_err("%s: spi_register_driver failed(%d)", + DRVNAME, result); + tty_unregister_driver(tty_drv); + } + return result; +} + +module_init(ifx_spi_init); +module_exit(ifx_spi_exit); + +MODULE_AUTHOR("Intel"); +MODULE_DESCRIPTION("IFX6x60 spi driver"); +MODULE_LICENSE("GPL"); +MODULE_INFO(Version, "0.1-IFX6x60"); diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h new file mode 100644 index 000000000000..deb7b8d977dc --- /dev/null +++ b/drivers/tty/serial/ifx6x60.h @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * Driver for the IFX spi modem. + * + * Copyright (C) 2009, 2010 Intel Corp + * Jim Stanley + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + * + * + *****************************************************************************/ +#ifndef _IFX6X60_H +#define _IFX6X60_H + +#define DRVNAME "ifx6x60" +#define TTYNAME "ttyIFX" + +/* #define IFX_THROTTLE_CODE */ + +#define IFX_SPI_MAX_MINORS 1 +#define IFX_SPI_TRANSFER_SIZE 2048 +#define IFX_SPI_FIFO_SIZE 4096 + +#define IFX_SPI_HEADER_OVERHEAD 4 +#define IFX_RESET_TIMEOUT msecs_to_jiffies(50) + +/* device flags bitfield definitions */ +#define IFX_SPI_STATE_PRESENT 0 +#define IFX_SPI_STATE_IO_IN_PROGRESS 1 +#define IFX_SPI_STATE_IO_READY 2 +#define IFX_SPI_STATE_TIMER_PENDING 3 + +/* flow control bitfields */ +#define IFX_SPI_DCD 0 +#define IFX_SPI_CTS 1 +#define IFX_SPI_DSR 2 +#define IFX_SPI_RI 3 +#define IFX_SPI_DTR 4 +#define IFX_SPI_RTS 5 +#define IFX_SPI_TX_FC 6 +#define IFX_SPI_RX_FC 7 +#define IFX_SPI_UPDATE 8 + +#define IFX_SPI_PAYLOAD_SIZE (IFX_SPI_TRANSFER_SIZE - \ + IFX_SPI_HEADER_OVERHEAD) + +#define IFX_SPI_IRQ_TYPE DETECT_EDGE_RISING +#define IFX_SPI_GPIO_TARGET 0 +#define IFX_SPI_GPIO0 0x105 + +#define IFX_SPI_STATUS_TIMEOUT (2000*HZ) + +/* values for bits in power status byte */ +#define IFX_SPI_POWER_DATA_PENDING 1 +#define IFX_SPI_POWER_SRDY 2 + +struct ifx_spi_device { + /* Our SPI device */ + struct spi_device *spi_dev; + + /* Port specific data */ + struct kfifo tx_fifo; + spinlock_t fifo_lock; + unsigned long signal_state; + + /* TTY Layer logic */ + struct tty_port tty_port; + struct device *tty_dev; + int minor; + + /* Low level I/O work */ + struct tasklet_struct io_work_tasklet; + unsigned long flags; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + + int is_6160; /* Modem type */ + + spinlock_t write_lock; + int write_pending; + spinlock_t power_lock; + unsigned char power_status; + + unsigned char *rx_buffer; + unsigned char *tx_buffer; + dma_addr_t rx_bus; + dma_addr_t tx_bus; + unsigned char spi_more; + unsigned char spi_slave_cts; + + struct timer_list spi_timer; + + struct spi_message spi_msg; + struct spi_transfer spi_xfer; + + struct { + /* gpio lines */ + unsigned short srdy; /* slave-ready gpio */ + unsigned short mrdy; /* master-ready gpio */ + unsigned short reset; /* modem-reset gpio */ + unsigned short po; /* modem-on gpio */ + unsigned short reset_out; /* modem-in-reset gpio */ + /* state/stats */ + int unack_srdy_int_nb; + } gpio; + + /* modem reset */ + unsigned long mdm_reset_state; +#define MR_START 0 +#define MR_INPROGRESS 1 +#define MR_COMPLETE 2 + wait_queue_head_t mdm_reset_wait; +}; + +#endif /* _IFX6X60_H */ diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c new file mode 100644 index 000000000000..dfcf4b1878aa --- /dev/null +++ b/drivers/tty/serial/imx.c @@ -0,0 +1,1380 @@ +/* + * linux/drivers/serial/imx.c + * + * Driver for Motorola IMX serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Author: Sascha Hauer + * Copyright (C) 2004 Pengutronix + * + * Copyright (C) 2009 emlix GmbH + * Author: Fabian Godehardt (added IrDA support for iMX) + * + * 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 + * + * [29-Mar-2005] Mike Lee + * Added hardware handshake + */ + +#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Register definitions */ +#define URXD0 0x0 /* Receiver Register */ +#define URTX0 0x40 /* Transmitter Register */ +#define UCR1 0x80 /* Control Register 1 */ +#define UCR2 0x84 /* Control Register 2 */ +#define UCR3 0x88 /* Control Register 3 */ +#define UCR4 0x8c /* Control Register 4 */ +#define UFCR 0x90 /* FIFO Control Register */ +#define USR1 0x94 /* Status Register 1 */ +#define USR2 0x98 /* Status Register 2 */ +#define UESC 0x9c /* Escape Character Register */ +#define UTIM 0xa0 /* Escape Timer Register */ +#define UBIR 0xa4 /* BRM Incremental Register */ +#define UBMR 0xa8 /* BRM Modulator Register */ +#define UBRC 0xac /* Baud Rate Count Register */ +#define MX2_ONEMS 0xb0 /* One Millisecond register */ +#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */ + +/* UART Control Register Bit Fields.*/ +#define URXD_CHARRDY (1<<15) +#define URXD_ERR (1<<14) +#define URXD_OVRRUN (1<<13) +#define URXD_FRMERR (1<<12) +#define URXD_BRK (1<<11) +#define URXD_PRERR (1<<10) +#define UCR1_ADEN (1<<15) /* Auto dectect interrupt */ +#define UCR1_ADBR (1<<14) /* Auto detect baud rate */ +#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */ +#define UCR1_IDEN (1<<12) /* Idle condition interrupt */ +#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ +#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ +#define UCR1_IREN (1<<7) /* Infrared interface enable */ +#define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ +#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ +#define UCR1_SNDBRK (1<<4) /* Send break */ +#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ +#define MX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, mx1 only */ +#define UCR1_DOZE (1<<1) /* Doze */ +#define UCR1_UARTEN (1<<0) /* UART enabled */ +#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ +#define UCR2_IRTS (1<<14) /* Ignore RTS pin */ +#define UCR2_CTSC (1<<13) /* CTS pin control */ +#define UCR2_CTS (1<<12) /* Clear to send */ +#define UCR2_ESCEN (1<<11) /* Escape enable */ +#define UCR2_PREN (1<<8) /* Parity enable */ +#define UCR2_PROE (1<<7) /* Parity odd/even */ +#define UCR2_STPB (1<<6) /* Stop */ +#define UCR2_WS (1<<5) /* Word size */ +#define UCR2_RTSEN (1<<4) /* Request to send interrupt enable */ +#define UCR2_TXEN (1<<2) /* Transmitter enabled */ +#define UCR2_RXEN (1<<1) /* Receiver enabled */ +#define UCR2_SRST (1<<0) /* SW reset */ +#define UCR3_DTREN (1<<13) /* DTR interrupt enable */ +#define UCR3_PARERREN (1<<12) /* Parity enable */ +#define UCR3_FRAERREN (1<<11) /* Frame error interrupt enable */ +#define UCR3_DSR (1<<10) /* Data set ready */ +#define UCR3_DCD (1<<9) /* Data carrier detect */ +#define UCR3_RI (1<<8) /* Ring indicator */ +#define UCR3_TIMEOUTEN (1<<7) /* Timeout interrupt enable */ +#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ +#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ +#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ +#define MX1_UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */ +#define MX1_UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */ +#define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ +#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ +#define UCR3_BPEN (1<<0) /* Preset registers enable */ +#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ +#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ +#define UCR4_INVR (1<<9) /* Inverted infrared reception */ +#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ +#define UCR4_WKEN (1<<7) /* Wake interrupt enable */ +#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */ +#define UCR4_IRSC (1<<5) /* IR special case */ +#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */ +#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */ +#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */ +#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */ +#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */ +#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */ +#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7) +#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */ +#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */ +#define USR1_RTSS (1<<14) /* RTS pin status */ +#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */ +#define USR1_RTSD (1<<12) /* RTS delta */ +#define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ +#define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ +#define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ +#define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ +#define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ +#define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ +#define USR1_AWAKE (1<<4) /* Aysnc wake interrupt flag */ +#define USR2_ADET (1<<15) /* Auto baud rate detect complete */ +#define USR2_TXFE (1<<14) /* Transmit buffer FIFO empty */ +#define USR2_DTRF (1<<13) /* DTR edge interrupt flag */ +#define USR2_IDLE (1<<12) /* Idle condition */ +#define USR2_IRINT (1<<8) /* Serial infrared interrupt flag */ +#define USR2_WAKE (1<<7) /* Wake */ +#define USR2_RTSF (1<<4) /* RTS edge interrupt flag */ +#define USR2_TXDC (1<<3) /* Transmitter complete */ +#define USR2_BRCD (1<<2) /* Break condition */ +#define USR2_ORE (1<<1) /* Overrun error */ +#define USR2_RDR (1<<0) /* Recv data ready */ +#define UTS_FRCPERR (1<<13) /* Force parity error */ +#define UTS_LOOP (1<<12) /* Loop tx and rx */ +#define UTS_TXEMPTY (1<<6) /* TxFIFO empty */ +#define UTS_RXEMPTY (1<<5) /* RxFIFO empty */ +#define UTS_TXFULL (1<<4) /* TxFIFO full */ +#define UTS_RXFULL (1<<3) /* RxFIFO full */ +#define UTS_SOFTRST (1<<0) /* Software reset */ + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_IMX_MAJOR 207 +#define MINOR_START 16 +#define DEV_NAME "ttymxc" +#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +#define DRIVER_NAME "IMX-uart" + +#define UART_NR 8 + +struct imx_port { + struct uart_port port; + struct timer_list timer; + unsigned int old_status; + int txirq,rxirq,rtsirq; + unsigned int have_rtscts:1; + unsigned int use_irda:1; + unsigned int irda_inv_rx:1; + unsigned int irda_inv_tx:1; + unsigned short trcv_delay; /* transceiver delay */ + struct clk *clk; +}; + +#ifdef CONFIG_IRDA +#define USE_IRDA(sport) ((sport)->use_irda) +#else +#define USE_IRDA(sport) (0) +#endif + +/* + * Handle any change of modem status signal since we were last called. + */ +static void imx_mctrl_check(struct imx_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void imx_timeout(unsigned long data) +{ + struct imx_port *sport = (struct imx_port *)data; + unsigned long flags; + + if (sport->port.state) { + spin_lock_irqsave(&sport->port.lock, flags); + imx_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +/* + * interrupts disabled on entry + */ +static void imx_stop_tx(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; + + if (USE_IRDA(sport)) { + /* half duplex - wait for end of transmission */ + int n = 256; + while ((--n > 0) && + !(readl(sport->port.membase + USR2) & USR2_TXDC)) { + udelay(5); + barrier(); + } + /* + * irda transceiver - wait a bit more to avoid + * cutoff, hardware dependent + */ + udelay(sport->trcv_delay); + + /* + * half duplex - reactivate receive mode, + * flush receive pipe echo crap + */ + if (readl(sport->port.membase + USR2) & USR2_TXDC) { + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN); + writel(temp, sport->port.membase + UCR1); + + temp = readl(sport->port.membase + UCR4); + temp &= ~(UCR4_TCEN); + writel(temp, sport->port.membase + UCR4); + + while (readl(sport->port.membase + URXD0) & + URXD_CHARRDY) + barrier(); + + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_RRDYEN; + writel(temp, sport->port.membase + UCR1); + + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_DREN; + writel(temp, sport->port.membase + UCR4); + } + return; + } + + temp = readl(sport->port.membase + UCR1); + writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1); +} + +/* + * interrupts disabled on entry + */ +static void imx_stop_rx(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; + + temp = readl(sport->port.membase + UCR2); + writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void imx_enable_ms(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static inline void imx_transmit_buffer(struct imx_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + while (!uart_circ_empty(xmit) && + !(readl(sport->port.membase + UTS) & UTS_TXFULL)) { + /* send xmit->buf[xmit->tail] + * out the port here */ + writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + imx_stop_tx(&sport->port); +} + +/* + * interrupts disabled on entry + */ +static void imx_start_tx(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; + + if (USE_IRDA(sport)) { + /* half duplex in IrDA mode; have to disable receive mode */ + temp = readl(sport->port.membase + UCR4); + temp &= ~(UCR4_DREN); + writel(temp, sport->port.membase + UCR4); + + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_RRDYEN); + writel(temp, sport->port.membase + UCR1); + } + + temp = readl(sport->port.membase + UCR1); + writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1); + + if (USE_IRDA(sport)) { + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_TRDYEN; + writel(temp, sport->port.membase + UCR1); + + temp = readl(sport->port.membase + UCR4); + temp |= UCR4_TCEN; + writel(temp, sport->port.membase + UCR4); + } + + if (readl(sport->port.membase + UTS) & UTS_TXEMPTY) + imx_transmit_buffer(sport); +} + +static irqreturn_t imx_rtsint(int irq, void *dev_id) +{ + struct imx_port *sport = dev_id; + unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock, flags); + + writel(USR1_RTSD, sport->port.membase + USR1); + uart_handle_cts_change(&sport->port, !!val); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); + + spin_unlock_irqrestore(&sport->port.lock, flags); + return IRQ_HANDLED; +} + +static irqreturn_t imx_txint(int irq, void *dev_id) +{ + struct imx_port *sport = dev_id; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned long flags; + + spin_lock_irqsave(&sport->port.lock,flags); + if (sport->port.x_char) + { + /* Send next char */ + writel(sport->port.x_char, sport->port.membase + URTX0); + goto out; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + imx_stop_tx(&sport->port); + goto out; + } + + imx_transmit_buffer(sport); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + +out: + spin_unlock_irqrestore(&sport->port.lock,flags); + return IRQ_HANDLED; +} + +static irqreturn_t imx_rxint(int irq, void *dev_id) +{ + struct imx_port *sport = dev_id; + unsigned int rx,flg,ignored = 0; + struct tty_struct *tty = sport->port.state->port.tty; + unsigned long flags, temp; + + spin_lock_irqsave(&sport->port.lock,flags); + + while (readl(sport->port.membase + USR2) & USR2_RDR) { + flg = TTY_NORMAL; + sport->port.icount.rx++; + + rx = readl(sport->port.membase + URXD0); + + temp = readl(sport->port.membase + USR2); + if (temp & USR2_BRCD) { + writel(USR2_BRCD, sport->port.membase + USR2); + if (uart_handle_break(&sport->port)) + continue; + } + + if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx)) + continue; + + if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) { + if (rx & URXD_PRERR) + sport->port.icount.parity++; + else if (rx & URXD_FRMERR) + sport->port.icount.frame++; + if (rx & URXD_OVRRUN) + sport->port.icount.overrun++; + + if (rx & sport->port.ignore_status_mask) { + if (++ignored > 100) + goto out; + continue; + } + + rx &= sport->port.read_status_mask; + + if (rx & URXD_PRERR) + flg = TTY_PARITY; + else if (rx & URXD_FRMERR) + flg = TTY_FRAME; + if (rx & URXD_OVRRUN) + flg = TTY_OVERRUN; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + tty_insert_flip_char(tty, rx, flg); + } + +out: + spin_unlock_irqrestore(&sport->port.lock,flags); + tty_flip_buffer_push(tty); + return IRQ_HANDLED; +} + +static irqreturn_t imx_int(int irq, void *dev_id) +{ + struct imx_port *sport = dev_id; + unsigned int sts; + + sts = readl(sport->port.membase + USR1); + + if (sts & USR1_RRDY) + imx_rxint(irq, dev_id); + + if (sts & USR1_TRDY && + readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) + imx_txint(irq, dev_id); + + if (sts & USR1_RTSD) + imx_rtsint(irq, dev_id); + + return IRQ_HANDLED; +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int imx_tx_empty(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + + return (readl(sport->port.membase + USR2) & USR2_TXDC) ? TIOCSER_TEMT : 0; +} + +/* + * We have a modem side uart, so the meanings of RTS and CTS are inverted. + */ +static unsigned int imx_get_mctrl(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned int tmp = TIOCM_DSR | TIOCM_CAR; + + if (readl(sport->port.membase + USR1) & USR1_RTSS) + tmp |= TIOCM_CTS; + + if (readl(sport->port.membase + UCR2) & UCR2_CTS) + tmp |= TIOCM_RTS; + + return tmp; +} + +static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; + + temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS; + + if (mctrl & TIOCM_RTS) + temp |= UCR2_CTS; + + writel(temp, sport->port.membase + UCR2); +} + +/* + * Interrupts always disabled. + */ +static void imx_break_ctl(struct uart_port *port, int break_state) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long flags, temp; + + spin_lock_irqsave(&sport->port.lock, flags); + + temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK; + + if ( break_state != 0 ) + temp |= UCR1_SNDBRK; + + writel(temp, sport->port.membase + UCR1); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +#define TXTL 2 /* reset default */ +#define RXTL 1 /* reset default */ + +static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) +{ + unsigned int val; + unsigned int ufcr_rfdiv; + + /* set receiver / transmitter trigger level. + * RFDIV is set such way to satisfy requested uartclk value + */ + val = TXTL << 10 | RXTL; + ufcr_rfdiv = (clk_get_rate(sport->clk) + sport->port.uartclk / 2) + / sport->port.uartclk; + + if(!ufcr_rfdiv) + ufcr_rfdiv = 1; + + val |= UFCR_RFDIV_REG(ufcr_rfdiv); + + writel(val, sport->port.membase + UFCR); + + return 0; +} + +/* half the RX buffer size */ +#define CTSTL 16 + +static int imx_startup(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + int retval; + unsigned long flags, temp; + + imx_setup_ufcr(sport, 0); + + /* disable the DREN bit (Data Ready interrupt enable) before + * requesting IRQs + */ + temp = readl(sport->port.membase + UCR4); + + if (USE_IRDA(sport)) + temp |= UCR4_IRSC; + + /* set the trigger level for CTS */ + temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); + temp |= CTSTL<< UCR4_CTSTL_SHF; + + writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); + + if (USE_IRDA(sport)) { + /* reset fifo's and state machines */ + int i = 100; + temp = readl(sport->port.membase + UCR2); + temp &= ~UCR2_SRST; + writel(temp, sport->port.membase + UCR2); + while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && + (--i > 0)) { + udelay(1); + } + } + + /* + * Allocate the IRQ(s) i.MX1 has three interrupts whereas later + * chips only have one interrupt. + */ + if (sport->txirq > 0) { + retval = request_irq(sport->rxirq, imx_rxint, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out1; + + retval = request_irq(sport->txirq, imx_txint, 0, + DRIVER_NAME, sport); + if (retval) + goto error_out2; + + /* do not use RTS IRQ on IrDA */ + if (!USE_IRDA(sport)) { + retval = request_irq(sport->rtsirq, imx_rtsint, + (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, + DRIVER_NAME, sport); + if (retval) + goto error_out3; + } + } else { + retval = request_irq(sport->port.irq, imx_int, 0, + DRIVER_NAME, sport); + if (retval) { + free_irq(sport->port.irq, sport); + goto error_out1; + } + } + + /* + * Finally, clear and enable interrupts + */ + writel(USR1_RTSD, sport->port.membase + USR1); + + temp = readl(sport->port.membase + UCR1); + temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; + + if (USE_IRDA(sport)) { + temp |= UCR1_IREN; + temp &= ~(UCR1_RTSDEN); + } + + writel(temp, sport->port.membase + UCR1); + + temp = readl(sport->port.membase + UCR2); + temp |= (UCR2_RXEN | UCR2_TXEN); + writel(temp, sport->port.membase + UCR2); + + if (USE_IRDA(sport)) { + /* clear RX-FIFO */ + int i = 64; + while ((--i > 0) && + (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) { + barrier(); + } + } + + if (!cpu_is_mx1()) { + temp = readl(sport->port.membase + UCR3); + temp |= MX2_UCR3_RXDMUXSEL; + writel(temp, sport->port.membase + UCR3); + } + + if (USE_IRDA(sport)) { + temp = readl(sport->port.membase + UCR4); + if (sport->irda_inv_rx) + temp |= UCR4_INVR; + else + temp &= ~(UCR4_INVR); + writel(temp | UCR4_DREN, sport->port.membase + UCR4); + + temp = readl(sport->port.membase + UCR3); + if (sport->irda_inv_tx) + temp |= UCR3_INVT; + else + temp &= ~(UCR3_INVT); + writel(temp, sport->port.membase + UCR3); + } + + /* + * Enable modem status interrupts + */ + spin_lock_irqsave(&sport->port.lock,flags); + imx_enable_ms(&sport->port); + spin_unlock_irqrestore(&sport->port.lock,flags); + + if (USE_IRDA(sport)) { + struct imxuart_platform_data *pdata; + pdata = sport->port.dev->platform_data; + sport->irda_inv_rx = pdata->irda_inv_rx; + sport->irda_inv_tx = pdata->irda_inv_tx; + sport->trcv_delay = pdata->transceiver_delay; + if (pdata->irda_enable) + pdata->irda_enable(1); + } + + return 0; + +error_out3: + if (sport->txirq) + free_irq(sport->txirq, sport); +error_out2: + if (sport->rxirq) + free_irq(sport->rxirq, sport); +error_out1: + return retval; +} + +static void imx_shutdown(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long temp; + + temp = readl(sport->port.membase + UCR2); + temp &= ~(UCR2_TXEN); + writel(temp, sport->port.membase + UCR2); + + if (USE_IRDA(sport)) { + struct imxuart_platform_data *pdata; + pdata = sport->port.dev->platform_data; + if (pdata->irda_enable) + pdata->irda_enable(0); + } + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Free the interrupts + */ + if (sport->txirq > 0) { + if (!USE_IRDA(sport)) + free_irq(sport->rtsirq, sport); + free_irq(sport->txirq, sport); + free_irq(sport->rxirq, sport); + } else + free_irq(sport->port.irq, sport); + + /* + * Disable all interrupts, port and break condition. + */ + + temp = readl(sport->port.membase + UCR1); + temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); + if (USE_IRDA(sport)) + temp &= ~(UCR1_IREN); + + writel(temp, sport->port.membase + UCR1); +} + +static void +imx_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct imx_port *sport = (struct imx_port *)port; + unsigned long flags; + unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + unsigned int div, ufcr; + unsigned long num, denom; + uint64_t tdiv64; + + /* + * If we don't support modem control lines, don't allow + * these to be set. + */ + if (0) { + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); + termios->c_cflag |= CLOCAL; + } + + /* + * We only support CS7 and CS8. + */ + while ((termios->c_cflag & CSIZE) != CS7 && + (termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8) + ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS; + else + ucr2 = UCR2_SRST | UCR2_IRTS; + + if (termios->c_cflag & CRTSCTS) { + if( sport->have_rtscts ) { + ucr2 &= ~UCR2_IRTS; + ucr2 |= UCR2_CTSC; + } else { + termios->c_cflag &= ~CRTSCTS; + } + } + + if (termios->c_cflag & CSTOPB) + ucr2 |= UCR2_STPB; + if (termios->c_cflag & PARENB) { + ucr2 |= UCR2_PREN; + if (termios->c_cflag & PARODD) + ucr2 |= UCR2_PROE; + } + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = 0; + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= URXD_BRK; + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= URXD_PRERR; + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= URXD_BRK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= URXD_OVRRUN; + } + + del_timer_sync(&sport->timer); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * disable interrupts and drain transmitter + */ + old_ucr1 = readl(sport->port.membase + UCR1); + writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), + sport->port.membase + UCR1); + + while ( !(readl(sport->port.membase + USR2) & USR2_TXDC)) + barrier(); + + /* then, disable everything */ + old_txrxen = readl(sport->port.membase + UCR2); + writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN), + sport->port.membase + UCR2); + old_txrxen &= (UCR2_TXEN | UCR2_RXEN); + + if (USE_IRDA(sport)) { + /* + * use maximum available submodule frequency to + * avoid missing short pulses due to low sampling rate + */ + div = 1; + } else { + div = sport->port.uartclk / (baud * 16); + if (div > 7) + div = 7; + if (!div) + div = 1; + } + + rational_best_approximation(16 * div * baud, sport->port.uartclk, + 1 << 16, 1 << 16, &num, &denom); + + tdiv64 = sport->port.uartclk; + tdiv64 *= num; + do_div(tdiv64, denom * 16 * div); + tty_termios_encode_baud_rate(termios, + (speed_t)tdiv64, (speed_t)tdiv64); + + num -= 1; + denom -= 1; + + ufcr = readl(sport->port.membase + UFCR); + ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div); + writel(ufcr, sport->port.membase + UFCR); + + writel(num, sport->port.membase + UBIR); + writel(denom, sport->port.membase + UBMR); + + if (!cpu_is_mx1()) + writel(sport->port.uartclk / div / 1000, + sport->port.membase + MX2_ONEMS); + + writel(old_ucr1, sport->port.membase + UCR1); + + /* set the parity, stop bits and data size */ + writel(ucr2 | old_txrxen, sport->port.membase + UCR2); + + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + imx_enable_ms(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *imx_type(struct uart_port *port) +{ + struct imx_port *sport = (struct imx_port *)port; + + return sport->port.type == PORT_IMX ? "IMX" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void imx_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mmres->start, mmres->end - mmres->start + 1); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int imx_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *mmres; + void *ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mmres) + return -ENODEV; + + ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1, + "imx-uart"); + + return ret ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void imx_config_port(struct uart_port *port, int flags) +{ + struct imx_port *sport = (struct imx_port *)port; + + if (flags & UART_CONFIG_TYPE && + imx_request_port(&sport->port) == 0) + sport->port.type = PORT_IMX; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_IMX and PORT_UNKNOWN + */ +static int +imx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct imx_port *sport = (struct imx_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops imx_pops = { + .tx_empty = imx_tx_empty, + .set_mctrl = imx_set_mctrl, + .get_mctrl = imx_get_mctrl, + .stop_tx = imx_stop_tx, + .start_tx = imx_start_tx, + .stop_rx = imx_stop_rx, + .enable_ms = imx_enable_ms, + .break_ctl = imx_break_ctl, + .startup = imx_startup, + .shutdown = imx_shutdown, + .set_termios = imx_set_termios, + .type = imx_type, + .release_port = imx_release_port, + .request_port = imx_request_port, + .config_port = imx_config_port, + .verify_port = imx_verify_port, +}; + +static struct imx_port *imx_ports[UART_NR]; + +#ifdef CONFIG_SERIAL_IMX_CONSOLE +static void imx_console_putchar(struct uart_port *port, int ch) +{ + struct imx_port *sport = (struct imx_port *)port; + + while (readl(sport->port.membase + UTS) & UTS_TXFULL) + barrier(); + + writel(ch, sport->port.membase + URTX0); +} + +/* + * Interrupts are disabled on entering + */ +static void +imx_console_write(struct console *co, const char *s, unsigned int count) +{ + struct imx_port *sport = imx_ports[co->index]; + unsigned int old_ucr1, old_ucr2, ucr1; + + /* + * First, save UCR1/2 and then disable interrupts + */ + ucr1 = old_ucr1 = readl(sport->port.membase + UCR1); + old_ucr2 = readl(sport->port.membase + UCR2); + + if (cpu_is_mx1()) + ucr1 |= MX1_UCR1_UARTCLKEN; + ucr1 |= UCR1_UARTEN; + ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN); + + writel(ucr1, sport->port.membase + UCR1); + + writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); + + uart_console_write(&sport->port, s, count, imx_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore UCR1/2 + */ + while (!(readl(sport->port.membase + USR2) & USR2_TXDC)); + + writel(old_ucr1, sport->port.membase + UCR1); + writel(old_ucr2, sport->port.membase + UCR2); +} + +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +imx_console_get_options(struct imx_port *sport, int *baud, + int *parity, int *bits) +{ + + if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) { + /* ok, the port was enabled */ + unsigned int ucr2, ubir,ubmr, uartclk; + unsigned int baud_raw; + unsigned int ucfr_rfdiv; + + ucr2 = readl(sport->port.membase + UCR2); + + *parity = 'n'; + if (ucr2 & UCR2_PREN) { + if (ucr2 & UCR2_PROE) + *parity = 'o'; + else + *parity = 'e'; + } + + if (ucr2 & UCR2_WS) + *bits = 8; + else + *bits = 7; + + ubir = readl(sport->port.membase + UBIR) & 0xffff; + ubmr = readl(sport->port.membase + UBMR) & 0xffff; + + ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7; + if (ucfr_rfdiv == 6) + ucfr_rfdiv = 7; + else + ucfr_rfdiv = 6 - ucfr_rfdiv; + + uartclk = clk_get_rate(sport->clk); + uartclk /= ucfr_rfdiv; + + { /* + * The next code provides exact computation of + * baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1)) + * without need of float support or long long division, + * which would be required to prevent 32bit arithmetic overflow + */ + unsigned int mul = ubir + 1; + unsigned int div = 16 * (ubmr + 1); + unsigned int rem = uartclk % div; + + baud_raw = (uartclk / div) * mul; + baud_raw += (rem * mul + div / 2) / div; + *baud = (baud_raw + 50) / 100 * 100; + } + + if(*baud != baud_raw) + printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n", + baud_raw, *baud); + } +} + +static int __init +imx_console_setup(struct console *co, char *options) +{ + struct imx_port *sport; + int baud = 9600; + 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. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports)) + co->index = 0; + sport = imx_ports[co->index]; + if(sport == NULL) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + imx_console_get_options(sport, &baud, &parity, &bits); + + imx_setup_ufcr(sport, 0); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver imx_reg; +static struct console imx_console = { + .name = DEV_NAME, + .write = imx_console_write, + .device = uart_console_device, + .setup = imx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &imx_reg, +}; + +#define IMX_CONSOLE &imx_console +#else +#define IMX_CONSOLE NULL +#endif + +static struct uart_driver imx_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = DEV_NAME, + .major = SERIAL_IMX_MAJOR, + .minor = MINOR_START, + .nr = ARRAY_SIZE(imx_ports), + .cons = IMX_CONSOLE, +}; + +static int serial_imx_suspend(struct platform_device *dev, pm_message_t state) +{ + struct imx_port *sport = platform_get_drvdata(dev); + + if (sport) + uart_suspend_port(&imx_reg, &sport->port); + + return 0; +} + +static int serial_imx_resume(struct platform_device *dev) +{ + struct imx_port *sport = platform_get_drvdata(dev); + + if (sport) + uart_resume_port(&imx_reg, &sport->port); + + return 0; +} + +static int serial_imx_probe(struct platform_device *pdev) +{ + struct imx_port *sport; + struct imxuart_platform_data *pdata; + void __iomem *base; + int ret = 0; + struct resource *res; + + sport = kzalloc(sizeof(*sport), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto free; + } + + base = ioremap(res->start, PAGE_SIZE); + if (!base) { + ret = -ENOMEM; + goto free; + } + + sport->port.dev = &pdev->dev; + sport->port.mapbase = res->start; + sport->port.membase = base; + sport->port.type = PORT_IMX, + sport->port.iotype = UPIO_MEM; + sport->port.irq = platform_get_irq(pdev, 0); + sport->rxirq = platform_get_irq(pdev, 0); + sport->txirq = platform_get_irq(pdev, 1); + sport->rtsirq = platform_get_irq(pdev, 2); + sport->port.fifosize = 32; + sport->port.ops = &imx_pops; + sport->port.flags = UPF_BOOT_AUTOCONF; + sport->port.line = pdev->id; + init_timer(&sport->timer); + sport->timer.function = imx_timeout; + sport->timer.data = (unsigned long)sport; + + sport->clk = clk_get(&pdev->dev, "uart"); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + goto unmap; + } + clk_enable(sport->clk); + + sport->port.uartclk = clk_get_rate(sport->clk); + + imx_ports[pdev->id] = sport; + + pdata = pdev->dev.platform_data; + if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) + sport->have_rtscts = 1; + +#ifdef CONFIG_IRDA + if (pdata && (pdata->flags & IMXUART_IRDA)) + sport->use_irda = 1; +#endif + + if (pdata && pdata->init) { + ret = pdata->init(pdev); + if (ret) + goto clkput; + } + + ret = uart_add_one_port(&imx_reg, &sport->port); + if (ret) + goto deinit; + platform_set_drvdata(pdev, &sport->port); + + return 0; +deinit: + if (pdata && pdata->exit) + pdata->exit(pdev); +clkput: + clk_put(sport->clk); + clk_disable(sport->clk); +unmap: + iounmap(sport->port.membase); +free: + kfree(sport); + + return ret; +} + +static int serial_imx_remove(struct platform_device *pdev) +{ + struct imxuart_platform_data *pdata; + struct imx_port *sport = platform_get_drvdata(pdev); + + pdata = pdev->dev.platform_data; + + platform_set_drvdata(pdev, NULL); + + if (sport) { + uart_remove_one_port(&imx_reg, &sport->port); + clk_put(sport->clk); + } + + clk_disable(sport->clk); + + if (pdata && pdata->exit) + pdata->exit(pdev); + + iounmap(sport->port.membase); + kfree(sport); + + return 0; +} + +static struct platform_driver serial_imx_driver = { + .probe = serial_imx_probe, + .remove = serial_imx_remove, + + .suspend = serial_imx_suspend, + .resume = serial_imx_resume, + .driver = { + .name = "imx-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init imx_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: IMX driver\n"); + + ret = uart_register_driver(&imx_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_imx_driver); + if (ret != 0) + uart_unregister_driver(&imx_reg); + + return 0; +} + +static void __exit imx_serial_exit(void) +{ + platform_driver_unregister(&serial_imx_driver); + uart_unregister_driver(&imx_reg); +} + +module_init(imx_serial_init); +module_exit(imx_serial_exit); + +MODULE_AUTHOR("Sascha Hauer"); +MODULE_DESCRIPTION("IMX generic serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx-uart"); diff --git a/drivers/tty/serial/ioc3_serial.c b/drivers/tty/serial/ioc3_serial.c new file mode 100644 index 000000000000..ee43efc7bdcc --- /dev/null +++ b/drivers/tty/serial/ioc3_serial.c @@ -0,0 +1,2199 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved. + */ + +/* + * This file contains a module version of the ioc3 serial driver. This + * includes all the support functions needed (support functions, etc.) + * and the serial driver itself. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Interesting things about the ioc3 + */ + +#define LOGICAL_PORTS 2 /* rs232(0) and rs422(1) */ +#define PORTS_PER_CARD 2 +#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS) +#define MAX_CARDS 8 +#define MAX_LOGICAL_PORTS (LOGICAL_PORTS_PER_CARD * MAX_CARDS) + +/* determine given the sio_ir what port it applies to */ +#define GET_PORT_FROM_SIO_IR(_x) (_x & SIO_IR_SA) ? 0 : 1 + + +/* + * we have 2 logical ports (rs232, rs422) for each physical port + * evens are rs232, odds are rs422 + */ +#define GET_PHYSICAL_PORT(_x) ((_x) >> 1) +#define GET_LOGICAL_PORT(_x) ((_x) & 1) +#define IS_PHYSICAL_PORT(_x) !((_x) & 1) +#define IS_RS232(_x) !((_x) & 1) + +static unsigned int Num_of_ioc3_cards; +static unsigned int Submodule_slot; + +/* defining this will get you LOTS of great debug info */ +//#define DEBUG_INTERRUPTS +#define DPRINT_CONFIG(_x...) ; +//#define DPRINT_CONFIG(_x...) printk _x +#define NOT_PROGRESS() ; +//#define NOT_PROGRESS() printk("%s : fails %d\n", __func__, __LINE__) + +/* number of characters we want to transmit to the lower level at a time */ +#define MAX_CHARS 256 +#define FIFO_SIZE (MAX_CHARS-1) /* it's a uchar */ + +/* Device name we're using */ +#define DEVICE_NAME "ttySIOC" +#define DEVICE_MAJOR 204 +#define DEVICE_MINOR 116 + +/* flags for next_char_state */ +#define NCS_BREAK 0x1 +#define NCS_PARITY 0x2 +#define NCS_FRAMING 0x4 +#define NCS_OVERRUN 0x8 + +/* cause we need SOME parameters ... */ +#define MIN_BAUD_SUPPORTED 1200 +#define MAX_BAUD_SUPPORTED 115200 + +/* protocol types supported */ +#define PROTO_RS232 0 +#define PROTO_RS422 1 + +/* Notification types */ +#define N_DATA_READY 0x01 +#define N_OUTPUT_LOWAT 0x02 +#define N_BREAK 0x04 +#define N_PARITY_ERROR 0x08 +#define N_FRAMING_ERROR 0x10 +#define N_OVERRUN_ERROR 0x20 +#define N_DDCD 0x40 +#define N_DCTS 0x80 + +#define N_ALL_INPUT (N_DATA_READY | N_BREAK \ + | N_PARITY_ERROR | N_FRAMING_ERROR \ + | N_OVERRUN_ERROR | N_DDCD | N_DCTS) + +#define N_ALL_OUTPUT N_OUTPUT_LOWAT + +#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR \ + | N_OVERRUN_ERROR) + +#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK \ + | N_PARITY_ERROR | N_FRAMING_ERROR \ + | N_OVERRUN_ERROR | N_DDCD | N_DCTS) + +#define SER_CLK_SPEED(prediv) ((22000000 << 1) / prediv) +#define SER_DIVISOR(x, clk) (((clk) + (x) * 8) / ((x) * 16)) +#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) + +/* Some masks */ +#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ + | UART_LCR_WLEN7 | UART_LCR_WLEN8) +#define LCR_MASK_STOP_BITS (UART_LCR_STOP) + +#define PENDING(_a, _p) (readl(&(_p)->vma->sio_ir) & (_a)->ic_enable) + +#define RING_BUF_SIZE 4096 +#define BUF_SIZE_BIT SBBR_L_SIZE +#define PROD_CONS_MASK PROD_CONS_PTR_4K + +#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) + +/* driver specific - one per card */ +struct ioc3_card { + struct { + /* uart ports are allocated here */ + struct uart_port icp_uart_port[LOGICAL_PORTS]; + /* the ioc3_port used for this port */ + struct ioc3_port *icp_port; + } ic_port[PORTS_PER_CARD]; + /* currently enabled interrupts */ + uint32_t ic_enable; +}; + +/* Local port info for each IOC3 serial port */ +struct ioc3_port { + /* handy reference material */ + struct uart_port *ip_port; + struct ioc3_card *ip_card; + struct ioc3_driver_data *ip_idd; + struct ioc3_submodule *ip_is; + + /* pci mem addresses for this port */ + struct ioc3_serialregs __iomem *ip_serial_regs; + struct ioc3_uartregs __iomem *ip_uart_regs; + + /* Ring buffer page for this port */ + dma_addr_t ip_dma_ringbuf; + /* vaddr of ring buffer */ + struct ring_buffer *ip_cpu_ringbuf; + + /* Rings for this port */ + struct ring *ip_inring; + struct ring *ip_outring; + + /* Hook to port specific values */ + struct port_hooks *ip_hooks; + + spinlock_t ip_lock; + + /* Various rx/tx parameters */ + int ip_baud; + int ip_tx_lowat; + int ip_rx_timeout; + + /* Copy of notification bits */ + int ip_notify; + + /* Shadow copies of various registers so we don't need to PIO + * read them constantly + */ + uint32_t ip_sscr; + uint32_t ip_tx_prod; + uint32_t ip_rx_cons; + unsigned char ip_flags; +}; + +/* tx low water mark. We need to notify the driver whenever tx is getting + * close to empty so it can refill the tx buffer and keep things going. + * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll + * have no trouble getting in more chars in time (I certainly hope so). + */ +#define TX_LOWAT_LATENCY 1000 +#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY) +#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) + +/* Flags per port */ +#define INPUT_HIGH 0x01 + /* used to signify that we have turned off the rx_high + * temporarily - we need to drain the fifo and don't + * want to get blasted with interrupts. + */ +#define DCD_ON 0x02 + /* DCD state is on */ +#define LOWAT_WRITTEN 0x04 +#define READ_ABORTED 0x08 + /* the read was aborted - used to avaoid infinate looping + * in the interrupt handler + */ +#define INPUT_ENABLE 0x10 + +/* Since each port has different register offsets and bitmasks + * for everything, we'll store those that we need in tables so we + * don't have to be constantly checking the port we are dealing with. + */ +struct port_hooks { + uint32_t intr_delta_dcd; + uint32_t intr_delta_cts; + uint32_t intr_tx_mt; + uint32_t intr_rx_timer; + uint32_t intr_rx_high; + uint32_t intr_tx_explicit; + uint32_t intr_clear; + uint32_t intr_all; + char rs422_select_pin; +}; + +static struct port_hooks hooks_array[PORTS_PER_CARD] = { + /* values for port A */ + { + .intr_delta_dcd = SIO_IR_SA_DELTA_DCD, + .intr_delta_cts = SIO_IR_SA_DELTA_CTS, + .intr_tx_mt = SIO_IR_SA_TX_MT, + .intr_rx_timer = SIO_IR_SA_RX_TIMER, + .intr_rx_high = SIO_IR_SA_RX_HIGH, + .intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT, + .intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL + | SIO_IR_SA_RX_HIGH + | SIO_IR_SA_RX_TIMER + | SIO_IR_SA_DELTA_DCD + | SIO_IR_SA_DELTA_CTS + | SIO_IR_SA_INT + | SIO_IR_SA_TX_EXPLICIT + | SIO_IR_SA_MEMERR), + .intr_all = SIO_IR_SA, + .rs422_select_pin = GPPR_UARTA_MODESEL_PIN, + }, + + /* values for port B */ + { + .intr_delta_dcd = SIO_IR_SB_DELTA_DCD, + .intr_delta_cts = SIO_IR_SB_DELTA_CTS, + .intr_tx_mt = SIO_IR_SB_TX_MT, + .intr_rx_timer = SIO_IR_SB_RX_TIMER, + .intr_rx_high = SIO_IR_SB_RX_HIGH, + .intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT, + .intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL + | SIO_IR_SB_RX_HIGH + | SIO_IR_SB_RX_TIMER + | SIO_IR_SB_DELTA_DCD + | SIO_IR_SB_DELTA_CTS + | SIO_IR_SB_INT + | SIO_IR_SB_TX_EXPLICIT + | SIO_IR_SB_MEMERR), + .intr_all = SIO_IR_SB, + .rs422_select_pin = GPPR_UARTB_MODESEL_PIN, + } +}; + +struct ring_entry { + union { + struct { + uint32_t alldata; + uint32_t allsc; + } all; + struct { + char data[4]; /* data bytes */ + char sc[4]; /* status/control */ + } s; + } u; +}; + +/* Test the valid bits in any of the 4 sc chars using "allsc" member */ +#define RING_ANY_VALID \ + ((uint32_t)(RXSB_MODEM_VALID | RXSB_DATA_VALID) * 0x01010101) + +#define ring_sc u.s.sc +#define ring_data u.s.data +#define ring_allsc u.all.allsc + +/* Number of entries per ring buffer. */ +#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) + +/* An individual ring */ +struct ring { + struct ring_entry entries[ENTRIES_PER_RING]; +}; + +/* The whole enchilada */ +struct ring_buffer { + struct ring TX_A; + struct ring RX_A; + struct ring TX_B; + struct ring RX_B; +}; + +/* Get a ring from a port struct */ +#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) + +/* for Infinite loop detection */ +#define MAXITER 10000000 + + +/** + * set_baud - Baud rate setting code + * @port: port to set + * @baud: baud rate to use + */ +static int set_baud(struct ioc3_port *port, int baud) +{ + int divisor; + int actual_baud; + int diff; + int lcr, prediv; + struct ioc3_uartregs __iomem *uart; + + for (prediv = 6; prediv < 64; prediv++) { + divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv)); + if (!divisor) + continue; /* invalid divisor */ + actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv)); + + diff = actual_baud - baud; + if (diff < 0) + diff = -diff; + + /* if we're within 1% we've found a match */ + if (diff * 100 <= actual_baud) + break; + } + + /* if the above loop completed, we didn't match + * the baud rate. give up. + */ + if (prediv == 64) { + NOT_PROGRESS(); + return 1; + } + + uart = port->ip_uart_regs; + lcr = readb(&uart->iu_lcr); + + writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr); + writeb((unsigned char)divisor, &uart->iu_dll); + writeb((unsigned char)(divisor >> 8), &uart->iu_dlm); + writeb((unsigned char)prediv, &uart->iu_scr); + writeb((unsigned char)lcr, &uart->iu_lcr); + + return 0; +} + +/** + * get_ioc3_port - given a uart port, return the control structure + * @the_port: uart port to find + */ +static struct ioc3_port *get_ioc3_port(struct uart_port *the_port) +{ + struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev); + struct ioc3_card *card_ptr = idd->data[Submodule_slot]; + int ii, jj; + + if (!card_ptr) { + NOT_PROGRESS(); + return NULL; + } + for (ii = 0; ii < PORTS_PER_CARD; ii++) { + for (jj = 0; jj < LOGICAL_PORTS; jj++) { + if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj]) + return card_ptr->ic_port[ii].icp_port; + } + } + NOT_PROGRESS(); + return NULL; +} + +/** + * port_init - Initialize the sio and ioc3 hardware for a given port + * called per port from attach... + * @port: port to initialize + */ +static int inline port_init(struct ioc3_port *port) +{ + uint32_t sio_cr; + struct port_hooks *hooks = port->ip_hooks; + struct ioc3_uartregs __iomem *uart; + int reset_loop_counter = 0xfffff; + struct ioc3_driver_data *idd = port->ip_idd; + + /* Idle the IOC3 serial interface */ + writel(SSCR_RESET, &port->ip_serial_regs->sscr); + + /* Wait until any pending bus activity for this port has ceased */ + do { + sio_cr = readl(&idd->vma->sio_cr); + if (reset_loop_counter-- <= 0) { + printk(KERN_WARNING + "IOC3 unable to come out of reset" + " scr 0x%x\n", sio_cr); + return -1; + } + } while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) && + (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA) + || sio_cr == SIO_CR_ARB_DIAG_TXB + || sio_cr == SIO_CR_ARB_DIAG_RXA + || sio_cr == SIO_CR_ARB_DIAG_RXB)); + + /* Finish reset sequence */ + writel(0, &port->ip_serial_regs->sscr); + + /* Once RESET is done, reload cached tx_prod and rx_cons values + * and set rings to empty by making prod == cons + */ + port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; + writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); + port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; + writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); + + /* Disable interrupts for this 16550 */ + uart = port->ip_uart_regs; + writeb(0, &uart->iu_lcr); + writeb(0, &uart->iu_ier); + + /* Set the default baud */ + set_baud(port, port->ip_baud); + + /* Set line control to 8 bits no parity */ + writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr); + /* UART_LCR_STOP == 1 stop */ + + /* Enable the FIFOs */ + writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr); + /* then reset 16550 FIFOs */ + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + &uart->iu_fcr); + + /* Clear modem control register */ + writeb(0, &uart->iu_mcr); + + /* Clear deltas in modem status register */ + writel(0, &port->ip_serial_regs->shadow); + + /* Only do this once per port pair */ + if (port->ip_hooks == &hooks_array[0]) { + unsigned long ring_pci_addr; + uint32_t __iomem *sbbr_l, *sbbr_h; + + sbbr_l = &idd->vma->sbbr_l; + sbbr_h = &idd->vma->sbbr_h; + ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; + DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n", + __func__, (void *)ring_pci_addr)); + + writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h); + writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l); + } + + /* Set the receive timeout value to 10 msec */ + writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr); + + /* Set rx threshold, enable DMA */ + /* Set high water mark at 3/4 of full ring */ + port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); + + /* uart experiences pauses at high baud rate reducing actual + * throughput by 10% or so unless we enable high speed polling + * XXX when this hardware bug is resolved we should revert to + * normal polling speed + */ + port->ip_sscr |= SSCR_HIGH_SPD; + + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Disable and clear all serial related interrupt bits */ + port->ip_card->ic_enable &= ~hooks->intr_clear; + ioc3_disable(port->ip_is, idd, hooks->intr_clear); + ioc3_ack(port->ip_is, idd, hooks->intr_clear); + return 0; +} + +/** + * enable_intrs - enable interrupts + * @port: port to enable + * @mask: mask to use + */ +static void enable_intrs(struct ioc3_port *port, uint32_t mask) +{ + if ((port->ip_card->ic_enable & mask) != mask) { + port->ip_card->ic_enable |= mask; + ioc3_enable(port->ip_is, port->ip_idd, mask); + } +} + +/** + * local_open - local open a port + * @port: port to open + */ +static inline int local_open(struct ioc3_port *port) +{ + int spiniter = 0; + + port->ip_flags = INPUT_ENABLE; + + /* Pause the DMA interface if necessary */ + if (port->ip_sscr & SSCR_DMA_EN) { + writel(port->ip_sscr | SSCR_DMA_PAUSE, + &port->ip_serial_regs->sscr); + while ((readl(&port->ip_serial_regs->sscr) + & SSCR_PAUSE_STATE) == 0) { + spiniter++; + if (spiniter > MAXITER) { + NOT_PROGRESS(); + return -1; + } + } + } + + /* Reset the input fifo. If the uart received chars while the port + * was closed and DMA is not enabled, the uart may have a bunch of + * chars hanging around in its rx fifo which will not be discarded + * by rclr in the upper layer. We must get rid of them here. + */ + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, + &port->ip_uart_regs->iu_fcr); + + writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr); + /* UART_LCR_STOP == 1 stop */ + + /* Re-enable DMA, set default threshold to intr whenever there is + * data available. + */ + port->ip_sscr &= ~SSCR_RX_THRESHOLD; + port->ip_sscr |= 1; /* default threshold */ + + /* Plug in the new sscr. This implicitly clears the DMA_PAUSE + * flag if it was set above + */ + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + port->ip_tx_lowat = 1; + return 0; +} + +/** + * set_rx_timeout - Set rx timeout and threshold values. + * @port: port to use + * @timeout: timeout value in ticks + */ +static inline int set_rx_timeout(struct ioc3_port *port, int timeout) +{ + int threshold; + + port->ip_rx_timeout = timeout; + + /* Timeout is in ticks. Let's figure out how many chars we + * can receive at the current baud rate in that interval + * and set the rx threshold to that amount. There are 4 chars + * per ring entry, so we'll divide the number of chars that will + * arrive in timeout by 4. + * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. + */ + threshold = timeout * port->ip_baud / 4000; + if (threshold == 0) + threshold = 1; /* otherwise we'll intr all the time! */ + + if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD) + return 1; + + port->ip_sscr &= ~SSCR_RX_THRESHOLD; + port->ip_sscr |= threshold; + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Now set the rx timeout to the given value + * again timeout * SRTR_HZ / HZ + */ + timeout = timeout * SRTR_HZ / 100; + if (timeout > SRTR_CNT) + timeout = SRTR_CNT; + writel(timeout, &port->ip_serial_regs->srtr); + return 0; +} + +/** + * config_port - config the hardware + * @port: port to config + * @baud: baud rate for the port + * @byte_size: data size + * @stop_bits: number of stop bits + * @parenb: parity enable ? + * @parodd: odd parity ? + */ +static inline int +config_port(struct ioc3_port *port, + int baud, int byte_size, int stop_bits, int parenb, int parodd) +{ + char lcr, sizebits; + int spiniter = 0; + + DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d " + "parodd %d\n", + __func__, ((struct uart_port *)port->ip_port)->line, + baud, byte_size, stop_bits, parenb, parodd)); + + if (set_baud(port, baud)) + return 1; + + switch (byte_size) { + case 5: + sizebits = UART_LCR_WLEN5; + break; + case 6: + sizebits = UART_LCR_WLEN6; + break; + case 7: + sizebits = UART_LCR_WLEN7; + break; + case 8: + sizebits = UART_LCR_WLEN8; + break; + default: + return 1; + } + + /* Pause the DMA interface if necessary */ + if (port->ip_sscr & SSCR_DMA_EN) { + writel(port->ip_sscr | SSCR_DMA_PAUSE, + &port->ip_serial_regs->sscr); + while ((readl(&port->ip_serial_regs->sscr) + & SSCR_PAUSE_STATE) == 0) { + spiniter++; + if (spiniter > MAXITER) + return -1; + } + } + + /* Clear relevant fields in lcr */ + lcr = readb(&port->ip_uart_regs->iu_lcr); + lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | + UART_LCR_PARITY | LCR_MASK_STOP_BITS); + + /* Set byte size in lcr */ + lcr |= sizebits; + + /* Set parity */ + if (parenb) { + lcr |= UART_LCR_PARITY; + if (!parodd) + lcr |= UART_LCR_EPAR; + } + + /* Set stop bits */ + if (stop_bits) + lcr |= UART_LCR_STOP /* 2 stop bits */ ; + + writeb(lcr, &port->ip_uart_regs->iu_lcr); + + /* Re-enable the DMA interface if necessary */ + if (port->ip_sscr & SSCR_DMA_EN) { + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + port->ip_baud = baud; + + /* When we get within this number of ring entries of filling the + * entire ring on tx, place an EXPLICIT intr to generate a lowat + * notification when output has drained. + */ + port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; + if (port->ip_tx_lowat == 0) + port->ip_tx_lowat = 1; + + set_rx_timeout(port, 2); + return 0; +} + +/** + * do_write - Write bytes to the port. Returns the number of bytes + * actually written. Called from transmit_chars + * @port: port to use + * @buf: the stuff to write + * @len: how many bytes in 'buf' + */ +static inline int do_write(struct ioc3_port *port, char *buf, int len) +{ + int prod_ptr, cons_ptr, total = 0; + struct ring *outring; + struct ring_entry *entry; + struct port_hooks *hooks = port->ip_hooks; + + BUG_ON(!(len >= 0)); + + prod_ptr = port->ip_tx_prod; + cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; + outring = port->ip_outring; + + /* Maintain a 1-entry red-zone. The ring buffer is full when + * (cons - prod) % ring_size is 1. Rather than do this subtraction + * in the body of the loop, I'll do it now. + */ + cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; + + /* Stuff the bytes into the output */ + while ((prod_ptr != cons_ptr) && (len > 0)) { + int xx; + + /* Get 4 bytes (one ring entry) at a time */ + entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); + + /* Invalidate all entries */ + entry->ring_allsc = 0; + + /* Copy in some bytes */ + for (xx = 0; (xx < 4) && (len > 0); xx++) { + entry->ring_data[xx] = *buf++; + entry->ring_sc[xx] = TXCB_VALID; + len--; + total++; + } + + /* If we are within some small threshold of filling up the + * entire ring buffer, we must place an EXPLICIT intr here + * to generate a lowat interrupt in case we subsequently + * really do fill up the ring and the caller goes to sleep. + * No need to place more than one though. + */ + if (!(port->ip_flags & LOWAT_WRITTEN) && + ((cons_ptr - prod_ptr) & PROD_CONS_MASK) + <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) { + port->ip_flags |= LOWAT_WRITTEN; + entry->ring_sc[0] |= TXCB_INT_WHEN_DONE; + } + + /* Go on to next entry */ + prod_ptr += sizeof(struct ring_entry); + prod_ptr &= PROD_CONS_MASK; + } + + /* If we sent something, start DMA if necessary */ + if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) { + port->ip_sscr |= SSCR_DMA_EN; + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + + /* Store the new producer pointer. If tx is disabled, we stuff the + * data into the ring buffer, but we don't actually start tx. + */ + if (!uart_tx_stopped(port->ip_port)) { + writel(prod_ptr, &port->ip_serial_regs->stpir); + + /* If we are now transmitting, enable tx_mt interrupt so we + * can disable DMA if necessary when the tx finishes. + */ + if (total > 0) + enable_intrs(port, hooks->intr_tx_mt); + } + port->ip_tx_prod = prod_ptr; + + return total; +} + +/** + * disable_intrs - disable interrupts + * @port: port to enable + * @mask: mask to use + */ +static inline void disable_intrs(struct ioc3_port *port, uint32_t mask) +{ + if (port->ip_card->ic_enable & mask) { + ioc3_disable(port->ip_is, port->ip_idd, mask); + port->ip_card->ic_enable &= ~mask; + } +} + +/** + * set_notification - Modify event notification + * @port: port to use + * @mask: events mask + * @set_on: set ? + */ +static int set_notification(struct ioc3_port *port, int mask, int set_on) +{ + struct port_hooks *hooks = port->ip_hooks; + uint32_t intrbits, sscrbits; + + BUG_ON(!mask); + + intrbits = sscrbits = 0; + + if (mask & N_DATA_READY) + intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); + if (mask & N_OUTPUT_LOWAT) + intrbits |= hooks->intr_tx_explicit; + if (mask & N_DDCD) { + intrbits |= hooks->intr_delta_dcd; + sscrbits |= SSCR_RX_RING_DCD; + } + if (mask & N_DCTS) + intrbits |= hooks->intr_delta_cts; + + if (set_on) { + enable_intrs(port, intrbits); + port->ip_notify |= mask; + port->ip_sscr |= sscrbits; + } else { + disable_intrs(port, intrbits); + port->ip_notify &= ~mask; + port->ip_sscr &= ~sscrbits; + } + + /* We require DMA if either DATA_READY or DDCD notification is + * currently requested. If neither of these is requested and + * there is currently no tx in progress, DMA may be disabled. + */ + if (port->ip_notify & (N_DATA_READY | N_DDCD)) + port->ip_sscr |= SSCR_DMA_EN; + else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt)) + port->ip_sscr &= ~SSCR_DMA_EN; + + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + return 0; +} + +/** + * set_mcr - set the master control reg + * @the_port: port to use + * @mask1: mcr mask + * @mask2: shadow mask + */ +static inline int set_mcr(struct uart_port *the_port, + int mask1, int mask2) +{ + struct ioc3_port *port = get_ioc3_port(the_port); + uint32_t shadow; + int spiniter = 0; + char mcr; + + if (!port) + return -1; + + /* Pause the DMA interface if necessary */ + if (port->ip_sscr & SSCR_DMA_EN) { + writel(port->ip_sscr | SSCR_DMA_PAUSE, + &port->ip_serial_regs->sscr); + while ((readl(&port->ip_serial_regs->sscr) + & SSCR_PAUSE_STATE) == 0) { + spiniter++; + if (spiniter > MAXITER) + return -1; + } + } + shadow = readl(&port->ip_serial_regs->shadow); + mcr = (shadow & 0xff000000) >> 24; + + /* Set new value */ + mcr |= mask1; + shadow |= mask2; + writeb(mcr, &port->ip_uart_regs->iu_mcr); + writel(shadow, &port->ip_serial_regs->shadow); + + /* Re-enable the DMA interface if necessary */ + if (port->ip_sscr & SSCR_DMA_EN) { + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + return 0; +} + +/** + * ioc3_set_proto - set the protocol for the port + * @port: port to use + * @proto: protocol to use + */ +static int ioc3_set_proto(struct ioc3_port *port, int proto) +{ + struct port_hooks *hooks = port->ip_hooks; + + switch (proto) { + default: + case PROTO_RS232: + /* Clear the appropriate GIO pin */ + DPRINT_CONFIG(("%s: rs232\n", __func__)); + writel(0, (&port->ip_idd->vma->gppr[0] + + hooks->rs422_select_pin)); + break; + + case PROTO_RS422: + /* Set the appropriate GIO pin */ + DPRINT_CONFIG(("%s: rs422\n", __func__)); + writel(1, (&port->ip_idd->vma->gppr[0] + + hooks->rs422_select_pin)); + break; + } + return 0; +} + +/** + * transmit_chars - upper level write, called with the_port->lock + * @the_port: port to write + */ +static void transmit_chars(struct uart_port *the_port) +{ + int xmit_count, tail, head; + int result; + char *start; + struct tty_struct *tty; + struct ioc3_port *port = get_ioc3_port(the_port); + struct uart_state *state; + + if (!the_port) + return; + if (!port) + return; + + state = the_port->state; + tty = state->port.tty; + + if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { + /* Nothing to do or hw stopped */ + set_notification(port, N_ALL_OUTPUT, 0); + return; + } + + head = state->xmit.head; + tail = state->xmit.tail; + start = (char *)&state->xmit.buf[tail]; + + /* write out all the data or until the end of the buffer */ + xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); + if (xmit_count > 0) { + result = do_write(port, start, xmit_count); + if (result > 0) { + /* booking */ + xmit_count -= result; + the_port->icount.tx += result; + /* advance the pointers */ + tail += result; + tail &= UART_XMIT_SIZE - 1; + state->xmit.tail = tail; + start = (char *)&state->xmit.buf[tail]; + } + } + if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(the_port); + + if (uart_circ_empty(&state->xmit)) { + set_notification(port, N_OUTPUT_LOWAT, 0); + } else { + set_notification(port, N_OUTPUT_LOWAT, 1); + } +} + +/** + * ioc3_change_speed - change the speed of the port + * @the_port: port to change + * @new_termios: new termios settings + * @old_termios: old termios settings + */ +static void +ioc3_change_speed(struct uart_port *the_port, + struct ktermios *new_termios, struct ktermios *old_termios) +{ + struct ioc3_port *port = get_ioc3_port(the_port); + unsigned int cflag, iflag; + int baud; + int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; + struct uart_state *state = the_port->state; + + cflag = new_termios->c_cflag; + iflag = new_termios->c_iflag; + + switch (cflag & CSIZE) { + case CS5: + new_data = 5; + break; + case CS6: + new_data = 6; + break; + case CS7: + new_data = 7; + break; + case CS8: + new_data = 8; + break; + default: + /* cuz we always need a default ... */ + new_data = 5; + break; + } + if (cflag & CSTOPB) { + new_stop = 1; + } + if (cflag & PARENB) { + new_parity_enable = 1; + if (cflag & PARODD) + new_parity = 1; + } + baud = uart_get_baud_rate(the_port, new_termios, old_termios, + MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); + DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud, + the_port->line)); + + if (!the_port->fifosize) + the_port->fifosize = FIFO_SIZE; + uart_update_timeout(the_port, cflag, baud); + + the_port->ignore_status_mask = N_ALL_INPUT; + + state->port.tty->low_latency = 1; + + if (iflag & IGNPAR) + the_port->ignore_status_mask &= ~(N_PARITY_ERROR + | N_FRAMING_ERROR); + if (iflag & IGNBRK) { + the_port->ignore_status_mask &= ~N_BREAK; + if (iflag & IGNPAR) + the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; + } + if (!(cflag & CREAD)) { + /* ignore everything */ + the_port->ignore_status_mask &= ~N_DATA_READY; + } + + if (cflag & CRTSCTS) { + /* enable hardware flow control */ + port->ip_sscr |= SSCR_HFC_EN; + } + else { + /* disable hardware flow control */ + port->ip_sscr &= ~SSCR_HFC_EN; + } + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Set the configuration and proper notification call */ + DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o " + "config_port(baud %d data %d stop %d penable %d " + " parity %d), notification 0x%x\n", + __func__, (void *)port, the_port->line, cflag, baud, + new_data, new_stop, new_parity_enable, new_parity, + the_port->ignore_status_mask)); + + if ((config_port(port, baud, /* baud */ + new_data, /* byte size */ + new_stop, /* stop bits */ + new_parity_enable, /* set parity */ + new_parity)) >= 0) { /* parity 1==odd */ + set_notification(port, the_port->ignore_status_mask, 1); + } +} + +/** + * ic3_startup_local - Start up the serial port - returns >= 0 if no errors + * @the_port: Port to operate on + */ +static inline int ic3_startup_local(struct uart_port *the_port) +{ + struct ioc3_port *port; + + if (!the_port) { + NOT_PROGRESS(); + return -1; + } + + port = get_ioc3_port(the_port); + if (!port) { + NOT_PROGRESS(); + return -1; + } + + local_open(port); + + /* set the protocol */ + ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 : + PROTO_RS422); + return 0; +} + +/* + * ioc3_cb_output_lowat - called when the output low water mark is hit + * @port: port to output + */ +static void ioc3_cb_output_lowat(struct ioc3_port *port) +{ + unsigned long pflags; + + /* the_port->lock is set on the call here */ + if (port->ip_port) { + spin_lock_irqsave(&port->ip_port->lock, pflags); + transmit_chars(port->ip_port); + spin_unlock_irqrestore(&port->ip_port->lock, pflags); + } +} + +/* + * ioc3_cb_post_ncs - called for some basic errors + * @port: port to use + * @ncs: event + */ +static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs) +{ + struct uart_icount *icount; + + icount = &the_port->icount; + + if (ncs & NCS_BREAK) + icount->brk++; + if (ncs & NCS_FRAMING) + icount->frame++; + if (ncs & NCS_OVERRUN) + icount->overrun++; + if (ncs & NCS_PARITY) + icount->parity++; +} + +/** + * do_read - Read in bytes from the port. Return the number of bytes + * actually read. + * @the_port: port to use + * @buf: place to put the stuff we read + * @len: how big 'buf' is + */ + +static inline int do_read(struct uart_port *the_port, char *buf, int len) +{ + int prod_ptr, cons_ptr, total; + struct ioc3_port *port = get_ioc3_port(the_port); + struct ring *inring; + struct ring_entry *entry; + struct port_hooks *hooks = port->ip_hooks; + int byte_num; + char *sc; + int loop_counter; + + BUG_ON(!(len >= 0)); + BUG_ON(!port); + + /* There is a nasty timing issue in the IOC3. When the rx_timer + * expires or the rx_high condition arises, we take an interrupt. + * At some point while servicing the interrupt, we read bytes from + * the ring buffer and re-arm the rx_timer. However the rx_timer is + * not started until the first byte is received *after* it is armed, + * and any bytes pending in the rx construction buffers are not drained + * to memory until either there are 4 bytes available or the rx_timer + * expires. This leads to a potential situation where data is left + * in the construction buffers forever - 1 to 3 bytes were received + * after the interrupt was generated but before the rx_timer was + * re-armed. At that point as long as no subsequent bytes are received + * the timer will never be started and the bytes will remain in the + * construction buffer forever. The solution is to execute a DRAIN + * command after rearming the timer. This way any bytes received before + * the DRAIN will be drained to memory, and any bytes received after + * the DRAIN will start the TIMER and be drained when it expires. + * Luckily, this only needs to be done when the DMA buffer is empty + * since there is no requirement that this function return all + * available data as long as it returns some. + */ + /* Re-arm the timer */ + + writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir); + + prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; + cons_ptr = port->ip_rx_cons; + + if (prod_ptr == cons_ptr) { + int reset_dma = 0; + + /* Input buffer appears empty, do a flush. */ + + /* DMA must be enabled for this to work. */ + if (!(port->ip_sscr & SSCR_DMA_EN)) { + port->ip_sscr |= SSCR_DMA_EN; + reset_dma = 1; + } + + /* Potential race condition: we must reload the srpir after + * issuing the drain command, otherwise we could think the rx + * buffer is empty, then take a very long interrupt, and when + * we come back it's full and we wait forever for the drain to + * complete. + */ + writel(port->ip_sscr | SSCR_RX_DRAIN, + &port->ip_serial_regs->sscr); + prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; + + /* We must not wait for the DRAIN to complete unless there are + * at least 8 bytes (2 ring entries) available to receive the + * data otherwise the DRAIN will never complete and we'll + * deadlock here. + * In fact, to make things easier, I'll just ignore the flush if + * there is any data at all now available. + */ + if (prod_ptr == cons_ptr) { + loop_counter = 0; + while (readl(&port->ip_serial_regs->sscr) & + SSCR_RX_DRAIN) { + loop_counter++; + if (loop_counter > MAXITER) + return -1; + } + + /* SIGH. We have to reload the prod_ptr *again* since + * the drain may have caused it to change + */ + prod_ptr = readl(&port->ip_serial_regs->srpir) + & PROD_CONS_MASK; + } + if (reset_dma) { + port->ip_sscr &= ~SSCR_DMA_EN; + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + } + inring = port->ip_inring; + port->ip_flags &= ~READ_ABORTED; + + total = 0; + loop_counter = 0xfffff; /* to avoid hangs */ + + /* Grab bytes from the hardware */ + while ((prod_ptr != cons_ptr) && (len > 0)) { + entry = (struct ring_entry *)((caddr_t) inring + cons_ptr); + + if (loop_counter-- <= 0) { + printk(KERN_WARNING "IOC3 serial: " + "possible hang condition/" + "port stuck on read (line %d).\n", + the_port->line); + break; + } + + /* According to the producer pointer, this ring entry + * must contain some data. But if the PIO happened faster + * than the DMA, the data may not be available yet, so let's + * wait until it arrives. + */ + if ((entry->ring_allsc & RING_ANY_VALID) == 0) { + /* Indicate the read is aborted so we don't disable + * the interrupt thinking that the consumer is + * congested. + */ + port->ip_flags |= READ_ABORTED; + len = 0; + break; + } + + /* Load the bytes/status out of the ring entry */ + for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { + sc = &(entry->ring_sc[byte_num]); + + /* Check for change in modem state or overrun */ + if ((*sc & RXSB_MODEM_VALID) + && (port->ip_notify & N_DDCD)) { + /* Notify upper layer if DCD dropped */ + if ((port->ip_flags & DCD_ON) + && !(*sc & RXSB_DCD)) { + /* If we have already copied some data, + * return it. We'll pick up the carrier + * drop on the next pass. That way we + * don't throw away the data that has + * already been copied back to + * the caller's buffer. + */ + if (total > 0) { + len = 0; + break; + } + port->ip_flags &= ~DCD_ON; + + /* Turn off this notification so the + * carrier drop protocol won't see it + * again when it does a read. + */ + *sc &= ~RXSB_MODEM_VALID; + + /* To keep things consistent, we need + * to update the consumer pointer so + * the next reader won't come in and + * try to read the same ring entries + * again. This must be done here before + * the dcd change. + */ + + if ((entry->ring_allsc & RING_ANY_VALID) + == 0) { + cons_ptr += (int)sizeof + (struct ring_entry); + cons_ptr &= PROD_CONS_MASK; + } + writel(cons_ptr, + &port->ip_serial_regs->srcir); + port->ip_rx_cons = cons_ptr; + + /* Notify upper layer of carrier drop */ + if ((port->ip_notify & N_DDCD) + && port->ip_port) { + uart_handle_dcd_change + (port->ip_port, 0); + wake_up_interruptible + (&the_port->state-> + port.delta_msr_wait); + } + + /* If we had any data to return, we + * would have returned it above. + */ + return 0; + } + } + if (*sc & RXSB_MODEM_VALID) { + /* Notify that an input overrun occurred */ + if ((*sc & RXSB_OVERRUN) + && (port->ip_notify & N_OVERRUN_ERROR)) { + ioc3_cb_post_ncs(the_port, NCS_OVERRUN); + } + /* Don't look at this byte again */ + *sc &= ~RXSB_MODEM_VALID; + } + + /* Check for valid data or RX errors */ + if ((*sc & RXSB_DATA_VALID) && + ((*sc & (RXSB_PAR_ERR + | RXSB_FRAME_ERR | RXSB_BREAK)) + && (port->ip_notify & (N_PARITY_ERROR + | N_FRAMING_ERROR + | N_BREAK)))) { + /* There is an error condition on the next byte. + * If we have already transferred some bytes, + * we'll stop here. Otherwise if this is the + * first byte to be read, we'll just transfer + * it alone after notifying the + * upper layer of its status. + */ + if (total > 0) { + len = 0; + break; + } else { + if ((*sc & RXSB_PAR_ERR) && + (port-> + ip_notify & N_PARITY_ERROR)) { + ioc3_cb_post_ncs(the_port, + NCS_PARITY); + } + if ((*sc & RXSB_FRAME_ERR) && + (port-> + ip_notify & N_FRAMING_ERROR)) { + ioc3_cb_post_ncs(the_port, + NCS_FRAMING); + } + if ((*sc & RXSB_BREAK) + && (port->ip_notify & N_BREAK)) { + ioc3_cb_post_ncs + (the_port, NCS_BREAK); + } + len = 1; + } + } + if (*sc & RXSB_DATA_VALID) { + *sc &= ~RXSB_DATA_VALID; + *buf = entry->ring_data[byte_num]; + buf++; + len--; + total++; + } + } + + /* If we used up this entry entirely, go on to the next one, + * otherwise we must have run out of buffer space, so + * leave the consumer pointer here for the next read in case + * there are still unread bytes in this entry. + */ + if ((entry->ring_allsc & RING_ANY_VALID) == 0) { + cons_ptr += (int)sizeof(struct ring_entry); + cons_ptr &= PROD_CONS_MASK; + } + } + + /* Update consumer pointer and re-arm rx timer interrupt */ + writel(cons_ptr, &port->ip_serial_regs->srcir); + port->ip_rx_cons = cons_ptr; + + /* If we have now dipped below the rx high water mark and we have + * rx_high interrupt turned off, we can now turn it back on again. + */ + if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) + & PROD_CONS_MASK) < + ((port-> + ip_sscr & + SSCR_RX_THRESHOLD) + << PROD_CONS_PTR_OFF))) { + port->ip_flags &= ~INPUT_HIGH; + enable_intrs(port, hooks->intr_rx_high); + } + return total; +} + +/** + * receive_chars - upper level read. + * @the_port: port to read from + */ +static int receive_chars(struct uart_port *the_port) +{ + struct tty_struct *tty; + unsigned char ch[MAX_CHARS]; + int read_count = 0, read_room, flip = 0; + struct uart_state *state = the_port->state; + struct ioc3_port *port = get_ioc3_port(the_port); + unsigned long pflags; + + /* Make sure all the pointers are "good" ones */ + if (!state) + return 0; + if (!state->port.tty) + return 0; + + if (!(port->ip_flags & INPUT_ENABLE)) + return 0; + + spin_lock_irqsave(&the_port->lock, pflags); + tty = state->port.tty; + + read_count = do_read(the_port, ch, MAX_CHARS); + if (read_count > 0) { + flip = 1; + read_room = tty_insert_flip_string(tty, ch, read_count); + the_port->icount.rx += read_count; + } + spin_unlock_irqrestore(&the_port->lock, pflags); + + if (flip) + tty_flip_buffer_push(tty); + + return read_count; +} + +/** + * ioc3uart_intr_one - lowest level (per port) interrupt handler. + * @is : submodule + * @idd: driver data + * @pending: interrupts to handle + */ + +static int inline +ioc3uart_intr_one(struct ioc3_submodule *is, + struct ioc3_driver_data *idd, + unsigned int pending) +{ + int port_num = GET_PORT_FROM_SIO_IR(pending); + struct port_hooks *hooks; + unsigned int rx_high_rd_aborted = 0; + unsigned long flags; + struct uart_port *the_port; + struct ioc3_port *port; + int loop_counter; + struct ioc3_card *card_ptr; + unsigned int sio_ir; + + card_ptr = idd->data[is->id]; + port = card_ptr->ic_port[port_num].icp_port; + hooks = port->ip_hooks; + + /* Possible race condition here: The tx_mt interrupt bit may be + * cleared without the intervention of the interrupt handler, + * e.g. by a write. If the top level interrupt handler reads a + * tx_mt, then some other processor does a write, starting up + * output, then we come in here, see the tx_mt and stop DMA, the + * output started by the other processor will hang. Thus we can + * only rely on tx_mt being legitimate if it is read while the + * port lock is held. Therefore this bit must be ignored in the + * passed in interrupt mask which was read by the top level + * interrupt handler since the port lock was not held at the time + * it was read. We can only rely on this bit being accurate if it + * is read while the port lock is held. So we'll clear it for now, + * and reload it later once we have the port lock. + */ + + sio_ir = pending & ~(hooks->intr_tx_mt); + spin_lock_irqsave(&port->ip_lock, flags); + + loop_counter = MAXITER; /* to avoid hangs */ + + do { + uint32_t shadow; + + if (loop_counter-- <= 0) { + printk(KERN_WARNING "IOC3 serial: " + "possible hang condition/" + "port stuck on interrupt (line %d).\n", + ((struct uart_port *)port->ip_port)->line); + break; + } + /* Handle a DCD change */ + if (sio_ir & hooks->intr_delta_dcd) { + ioc3_ack(is, idd, hooks->intr_delta_dcd); + shadow = readl(&port->ip_serial_regs->shadow); + + if ((port->ip_notify & N_DDCD) + && (shadow & SHADOW_DCD) + && (port->ip_port)) { + the_port = port->ip_port; + uart_handle_dcd_change(the_port, + shadow & SHADOW_DCD); + wake_up_interruptible + (&the_port->state->port.delta_msr_wait); + } else if ((port->ip_notify & N_DDCD) + && !(shadow & SHADOW_DCD)) { + /* Flag delta DCD/no DCD */ + uart_handle_dcd_change(port->ip_port, + shadow & SHADOW_DCD); + port->ip_flags |= DCD_ON; + } + } + + /* Handle a CTS change */ + if (sio_ir & hooks->intr_delta_cts) { + ioc3_ack(is, idd, hooks->intr_delta_cts); + shadow = readl(&port->ip_serial_regs->shadow); + + if ((port->ip_notify & N_DCTS) && (port->ip_port)) { + the_port = port->ip_port; + uart_handle_cts_change(the_port, shadow + & SHADOW_CTS); + wake_up_interruptible + (&the_port->state->port.delta_msr_wait); + } + } + + /* rx timeout interrupt. Must be some data available. Put this + * before the check for rx_high since servicing this condition + * may cause that condition to clear. + */ + if (sio_ir & hooks->intr_rx_timer) { + ioc3_ack(is, idd, hooks->intr_rx_timer); + if ((port->ip_notify & N_DATA_READY) + && (port->ip_port)) { + receive_chars(port->ip_port); + } + } + + /* rx high interrupt. Must be after rx_timer. */ + else if (sio_ir & hooks->intr_rx_high) { + /* Data available, notify upper layer */ + if ((port->ip_notify & N_DATA_READY) && port->ip_port) { + receive_chars(port->ip_port); + } + + /* We can't ACK this interrupt. If receive_chars didn't + * cause the condition to clear, we'll have to disable + * the interrupt until the data is drained. + * If the read was aborted, don't disable the interrupt + * as this may cause us to hang indefinitely. An + * aborted read generally means that this interrupt + * hasn't been delivered to the cpu yet anyway, even + * though we see it as asserted when we read the sio_ir. + */ + if ((sio_ir = PENDING(card_ptr, idd)) + & hooks->intr_rx_high) { + if (port->ip_flags & READ_ABORTED) { + rx_high_rd_aborted++; + } + else { + card_ptr->ic_enable &= ~hooks->intr_rx_high; + port->ip_flags |= INPUT_HIGH; + } + } + } + + /* We got a low water interrupt: notify upper layer to + * send more data. Must come before tx_mt since servicing + * this condition may cause that condition to clear. + */ + if (sio_ir & hooks->intr_tx_explicit) { + port->ip_flags &= ~LOWAT_WRITTEN; + ioc3_ack(is, idd, hooks->intr_tx_explicit); + if (port->ip_notify & N_OUTPUT_LOWAT) + ioc3_cb_output_lowat(port); + } + + /* Handle tx_mt. Must come after tx_explicit. */ + else if (sio_ir & hooks->intr_tx_mt) { + /* If we are expecting a lowat notification + * and we get to this point it probably means that for + * some reason the tx_explicit didn't work as expected + * (that can legitimately happen if the output buffer is + * filled up in just the right way). + * So send the notification now. + */ + if (port->ip_notify & N_OUTPUT_LOWAT) { + ioc3_cb_output_lowat(port); + + /* We need to reload the sio_ir since the lowat + * call may have caused another write to occur, + * clearing the tx_mt condition. + */ + sio_ir = PENDING(card_ptr, idd); + } + + /* If the tx_mt condition still persists even after the + * lowat call, we've got some work to do. + */ + if (sio_ir & hooks->intr_tx_mt) { + /* If we are not currently expecting DMA input, + * and the transmitter has just gone idle, + * there is no longer any reason for DMA, so + * disable it. + */ + if (!(port->ip_notify + & (N_DATA_READY | N_DDCD))) { + BUG_ON(!(port->ip_sscr + & SSCR_DMA_EN)); + port->ip_sscr &= ~SSCR_DMA_EN; + writel(port->ip_sscr, + &port->ip_serial_regs->sscr); + } + /* Prevent infinite tx_mt interrupt */ + card_ptr->ic_enable &= ~hooks->intr_tx_mt; + } + } + sio_ir = PENDING(card_ptr, idd); + + /* if the read was aborted and only hooks->intr_rx_high, + * clear hooks->intr_rx_high, so we do not loop forever. + */ + + if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { + sio_ir &= ~hooks->intr_rx_high; + } + } while (sio_ir & hooks->intr_all); + + spin_unlock_irqrestore(&port->ip_lock, flags); + ioc3_enable(is, idd, card_ptr->ic_enable); + return 0; +} + +/** + * ioc3uart_intr - field all serial interrupts + * @is : submodule + * @idd: driver data + * @pending: interrupts to handle + * + */ + +static int ioc3uart_intr(struct ioc3_submodule *is, + struct ioc3_driver_data *idd, + unsigned int pending) +{ + int ret = 0; + + /* + * The upper level interrupt handler sends interrupts for both ports + * here. So we need to call for each port with its interrupts. + */ + + if (pending & SIO_IR_SA) + ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SA); + if (pending & SIO_IR_SB) + ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SB); + + return ret; +} + +/** + * ic3_type + * @port: Port to operate with (we ignore since we only have one port) + * + */ +static const char *ic3_type(struct uart_port *the_port) +{ + if (IS_RS232(the_port->line)) + return "SGI IOC3 Serial [rs232]"; + else + return "SGI IOC3 Serial [rs422]"; +} + +/** + * ic3_tx_empty - Is the transmitter empty? + * @port: Port to operate on + * + */ +static unsigned int ic3_tx_empty(struct uart_port *the_port) +{ + unsigned int ret = 0; + struct ioc3_port *port = get_ioc3_port(the_port); + + if (readl(&port->ip_serial_regs->shadow) & SHADOW_TEMT) + ret = TIOCSER_TEMT; + return ret; +} + +/** + * ic3_stop_tx - stop the transmitter + * @port: Port to operate on + * + */ +static void ic3_stop_tx(struct uart_port *the_port) +{ + struct ioc3_port *port = get_ioc3_port(the_port); + + if (port) + set_notification(port, N_OUTPUT_LOWAT, 0); +} + +/** + * ic3_stop_rx - stop the receiver + * @port: Port to operate on + * + */ +static void ic3_stop_rx(struct uart_port *the_port) +{ + struct ioc3_port *port = get_ioc3_port(the_port); + + if (port) + port->ip_flags &= ~INPUT_ENABLE; +} + +/** + * null_void_function + * @port: Port to operate on + * + */ +static void null_void_function(struct uart_port *the_port) +{ +} + +/** + * ic3_shutdown - shut down the port - free irq and disable + * @port: port to shut down + * + */ +static void ic3_shutdown(struct uart_port *the_port) +{ + unsigned long port_flags; + struct ioc3_port *port; + struct uart_state *state; + + port = get_ioc3_port(the_port); + if (!port) + return; + + state = the_port->state; + wake_up_interruptible(&state->port.delta_msr_wait); + + spin_lock_irqsave(&the_port->lock, port_flags); + set_notification(port, N_ALL, 0); + spin_unlock_irqrestore(&the_port->lock, port_flags); +} + +/** + * ic3_set_mctrl - set control lines (dtr, rts, etc) + * @port: Port to operate on + * @mctrl: Lines to set/unset + * + */ +static void ic3_set_mctrl(struct uart_port *the_port, unsigned int mctrl) +{ + unsigned char mcr = 0; + + 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; + + set_mcr(the_port, mcr, SHADOW_DTR); +} + +/** + * ic3_get_mctrl - get control line info + * @port: port to operate on + * + */ +static unsigned int ic3_get_mctrl(struct uart_port *the_port) +{ + struct ioc3_port *port = get_ioc3_port(the_port); + uint32_t shadow; + unsigned int ret = 0; + + if (!port) + return 0; + + shadow = readl(&port->ip_serial_regs->shadow); + if (shadow & SHADOW_DCD) + ret |= TIOCM_CD; + if (shadow & SHADOW_DR) + ret |= TIOCM_DSR; + if (shadow & SHADOW_CTS) + ret |= TIOCM_CTS; + return ret; +} + +/** + * ic3_start_tx - Start transmitter. Called with the_port->lock + * @port: Port to operate on + * + */ +static void ic3_start_tx(struct uart_port *the_port) +{ + struct ioc3_port *port = get_ioc3_port(the_port); + + if (port) { + set_notification(port, N_OUTPUT_LOWAT, 1); + enable_intrs(port, port->ip_hooks->intr_tx_mt); + } +} + +/** + * ic3_break_ctl - handle breaks + * @port: Port to operate on + * @break_state: Break state + * + */ +static void ic3_break_ctl(struct uart_port *the_port, int break_state) +{ +} + +/** + * ic3_startup - Start up the serial port - always return 0 (We're always on) + * @port: Port to operate on + * + */ +static int ic3_startup(struct uart_port *the_port) +{ + int retval; + struct ioc3_port *port; + struct ioc3_card *card_ptr; + unsigned long port_flags; + + if (!the_port) { + NOT_PROGRESS(); + return -ENODEV; + } + port = get_ioc3_port(the_port); + if (!port) { + NOT_PROGRESS(); + return -ENODEV; + } + card_ptr = port->ip_card; + port->ip_port = the_port; + + if (!card_ptr) { + NOT_PROGRESS(); + return -ENODEV; + } + + /* Start up the serial port */ + spin_lock_irqsave(&the_port->lock, port_flags); + retval = ic3_startup_local(the_port); + spin_unlock_irqrestore(&the_port->lock, port_flags); + return retval; +} + +/** + * ic3_set_termios - set termios stuff + * @port: port to operate on + * @termios: New settings + * @termios: Old + * + */ +static void +ic3_set_termios(struct uart_port *the_port, + struct ktermios *termios, struct ktermios *old_termios) +{ + unsigned long port_flags; + + spin_lock_irqsave(&the_port->lock, port_flags); + ioc3_change_speed(the_port, termios, old_termios); + spin_unlock_irqrestore(&the_port->lock, port_flags); +} + +/** + * ic3_request_port - allocate resources for port - no op.... + * @port: port to operate on + * + */ +static int ic3_request_port(struct uart_port *port) +{ + return 0; +} + +/* Associate the uart functions above - given to serial core */ +static struct uart_ops ioc3_ops = { + .tx_empty = ic3_tx_empty, + .set_mctrl = ic3_set_mctrl, + .get_mctrl = ic3_get_mctrl, + .stop_tx = ic3_stop_tx, + .start_tx = ic3_start_tx, + .stop_rx = ic3_stop_rx, + .enable_ms = null_void_function, + .break_ctl = ic3_break_ctl, + .startup = ic3_startup, + .shutdown = ic3_shutdown, + .set_termios = ic3_set_termios, + .type = ic3_type, + .release_port = null_void_function, + .request_port = ic3_request_port, +}; + +/* + * Boot-time initialization code + */ + +static struct uart_driver ioc3_uart = { + .owner = THIS_MODULE, + .driver_name = "ioc3_serial", + .dev_name = DEVICE_NAME, + .major = DEVICE_MAJOR, + .minor = DEVICE_MINOR, + .nr = MAX_LOGICAL_PORTS +}; + +/** + * ioc3_serial_core_attach - register with serial core + * This is done during pci probing + * @is: submodule struct for this + * @idd: handle for this card + */ +static inline int ioc3_serial_core_attach( struct ioc3_submodule *is, + struct ioc3_driver_data *idd) +{ + struct ioc3_port *port; + struct uart_port *the_port; + struct ioc3_card *card_ptr = idd->data[is->id]; + int ii, phys_port; + struct pci_dev *pdev = idd->pdev; + + DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n", + __func__, pdev, (void *)card_ptr)); + + if (!card_ptr) + return -ENODEV; + + /* once around for each logical port on this card */ + for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { + phys_port = GET_PHYSICAL_PORT(ii); + the_port = &card_ptr->ic_port[phys_port]. + icp_uart_port[GET_LOGICAL_PORT(ii)]; + port = card_ptr->ic_port[phys_port].icp_port; + port->ip_port = the_port; + + DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n", + __func__, (void *)the_port, (void *)port, + phys_port, ii)); + + /* membase, iobase and mapbase just need to be non-0 */ + the_port->membase = (unsigned char __iomem *)1; + the_port->iobase = (pdev->bus->number << 16) | ii; + the_port->line = (Num_of_ioc3_cards << 2) | ii; + the_port->mapbase = 1; + the_port->type = PORT_16550A; + the_port->fifosize = FIFO_SIZE; + the_port->ops = &ioc3_ops; + the_port->irq = idd->irq_io; + the_port->dev = &pdev->dev; + + if (uart_add_one_port(&ioc3_uart, the_port) < 0) { + printk(KERN_WARNING + "%s: unable to add port %d bus %d\n", + __func__, the_port->line, pdev->bus->number); + } else { + DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n", + the_port->line, the_port->irq, pdev->bus->number)); + } + + /* all ports are rs232 for now */ + if (IS_PHYSICAL_PORT(ii)) + ioc3_set_proto(port, PROTO_RS232); + } + return 0; +} + +/** + * ioc3uart_remove - register detach function + * @is: submodule struct for this submodule + * @idd: ioc3 driver data for this submodule + */ + +static int ioc3uart_remove(struct ioc3_submodule *is, + struct ioc3_driver_data *idd) +{ + struct ioc3_card *card_ptr = idd->data[is->id]; + struct uart_port *the_port; + struct ioc3_port *port; + int ii; + + if (card_ptr) { + for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) { + the_port = &card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. + icp_uart_port[GET_LOGICAL_PORT(ii)]; + if (the_port) + uart_remove_one_port(&ioc3_uart, the_port); + port = card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].icp_port; + if (port && IS_PHYSICAL_PORT(ii) + && (GET_PHYSICAL_PORT(ii) == 0)) { + pci_free_consistent(port->ip_idd->pdev, + TOTAL_RING_BUF_SIZE, + (void *)port->ip_cpu_ringbuf, + port->ip_dma_ringbuf); + kfree(port); + card_ptr->ic_port[GET_PHYSICAL_PORT(ii)]. + icp_port = NULL; + } + } + kfree(card_ptr); + idd->data[is->id] = NULL; + } + return 0; +} + +/** + * ioc3uart_probe - card probe function called from shim driver + * @is: submodule struct for this submodule + * @idd: ioc3 driver data for this card + */ + +static int __devinit +ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) +{ + struct pci_dev *pdev = idd->pdev; + struct ioc3_card *card_ptr; + int ret = 0; + struct ioc3_port *port; + struct ioc3_port *ports[PORTS_PER_CARD]; + int phys_port; + int cnt; + + DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd)); + + card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL); + if (!card_ptr) { + printk(KERN_WARNING "ioc3_attach_one" + ": unable to get memory for the IOC3\n"); + return -ENOMEM; + } + idd->data[is->id] = card_ptr; + Submodule_slot = is->id; + + writel(((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) | + ((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) | + (0xf << SIO_CR_CMD_PULSE_SHIFT), &idd->vma->sio_cr); + + pci_write_config_dword(pdev, PCI_LAT, 0xff00); + + /* Enable serial port mode select generic PIO pins as outputs */ + ioc3_gpcr_set(idd, GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL); + + /* Create port structures for each port */ + for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) { + port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL); + if (!port) { + printk(KERN_WARNING + "IOC3 serial memory not available for port\n"); + ret = -ENOMEM; + goto out4; + } + spin_lock_init(&port->ip_lock); + + /* we need to remember the previous ones, to point back to + * them farther down - setting up the ring buffers. + */ + ports[phys_port] = port; + + /* init to something useful */ + card_ptr->ic_port[phys_port].icp_port = port; + port->ip_is = is; + port->ip_idd = idd; + port->ip_baud = 9600; + port->ip_card = card_ptr; + port->ip_hooks = &hooks_array[phys_port]; + + /* Setup each port */ + if (phys_port == 0) { + port->ip_serial_regs = &idd->vma->port_a; + port->ip_uart_regs = &idd->vma->sregs.uarta; + + DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p " + "ip_uart_regs 0x%p\n", + __func__, + (void *)port->ip_serial_regs, + (void *)port->ip_uart_regs)); + + /* setup ring buffers */ + port->ip_cpu_ringbuf = pci_alloc_consistent(pdev, + TOTAL_RING_BUF_SIZE, &port->ip_dma_ringbuf); + + BUG_ON(!((((int64_t) port->ip_dma_ringbuf) & + (TOTAL_RING_BUF_SIZE - 1)) == 0)); + port->ip_inring = RING(port, RX_A); + port->ip_outring = RING(port, TX_A); + DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p " + "ip_dma_ringbuf 0x%p, ip_inring 0x%p " + "ip_outring 0x%p\n", + __func__, + (void *)port->ip_cpu_ringbuf, + (void *)port->ip_dma_ringbuf, + (void *)port->ip_inring, + (void *)port->ip_outring)); + } + else { + port->ip_serial_regs = &idd->vma->port_b; + port->ip_uart_regs = &idd->vma->sregs.uartb; + + DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p " + "ip_uart_regs 0x%p\n", + __func__, + (void *)port->ip_serial_regs, + (void *)port->ip_uart_regs)); + + /* share the ring buffers */ + port->ip_dma_ringbuf = + ports[phys_port - 1]->ip_dma_ringbuf; + port->ip_cpu_ringbuf = + ports[phys_port - 1]->ip_cpu_ringbuf; + port->ip_inring = RING(port, RX_B); + port->ip_outring = RING(port, TX_B); + DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p " + "ip_dma_ringbuf 0x%p, ip_inring 0x%p " + "ip_outring 0x%p\n", + __func__, + (void *)port->ip_cpu_ringbuf, + (void *)port->ip_dma_ringbuf, + (void *)port->ip_inring, + (void *)port->ip_outring)); + } + + DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p", + __func__, + phys_port, (void *)port, (void *)card_ptr)); + DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", + (void *)port->ip_serial_regs, + (void *)port->ip_uart_regs)); + + /* Initialize the hardware for IOC3 */ + port_init(port); + + DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p " + "outring 0x%p\n", + __func__, + phys_port, (void *)port, + (void *)port->ip_inring, + (void *)port->ip_outring)); + + } + + /* register port with the serial core */ + + if ((ret = ioc3_serial_core_attach(is, idd))) + goto out4; + + Num_of_ioc3_cards++; + + return ret; + + /* error exits that give back resources */ +out4: + for (cnt = 0; cnt < phys_port; cnt++) + kfree(ports[cnt]); + + kfree(card_ptr); + return ret; +} + +static struct ioc3_submodule ioc3uart_ops = { + .name = "IOC3uart", + .probe = ioc3uart_probe, + .remove = ioc3uart_remove, + /* call .intr for both ports initially */ + .irq_mask = SIO_IR_SA | SIO_IR_SB, + .intr = ioc3uart_intr, + .owner = THIS_MODULE, +}; + +/** + * ioc3_detect - module init called, + */ +static int __init ioc3uart_init(void) +{ + int ret; + + /* register with serial core */ + if ((ret = uart_register_driver(&ioc3_uart)) < 0) { + printk(KERN_WARNING + "%s: Couldn't register IOC3 uart serial driver\n", + __func__); + return ret; + } + ret = ioc3_register_submodule(&ioc3uart_ops); + if (ret) + uart_unregister_driver(&ioc3_uart); + return ret; +} + +static void __exit ioc3uart_exit(void) +{ + ioc3_unregister_submodule(&ioc3uart_ops); + uart_unregister_driver(&ioc3_uart); +} + +module_init(ioc3uart_init); +module_exit(ioc3uart_exit); + +MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) "); +MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC3 card"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c new file mode 100644 index 000000000000..fcfe82653ac8 --- /dev/null +++ b/drivers/tty/serial/ioc4_serial.c @@ -0,0 +1,2953 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003-2006 Silicon Graphics, Inc. All Rights Reserved. + */ + + +/* + * This file contains a module version of the ioc4 serial driver. This + * includes all the support functions needed (support functions, etc.) + * and the serial driver itself. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * interesting things about the ioc4 + */ + +#define IOC4_NUM_SERIAL_PORTS 4 /* max ports per card */ +#define IOC4_NUM_CARDS 8 /* max cards per partition */ + +#define GET_SIO_IR(_n) (_n == 0) ? (IOC4_SIO_IR_S0) : \ + (_n == 1) ? (IOC4_SIO_IR_S1) : \ + (_n == 2) ? (IOC4_SIO_IR_S2) : \ + (IOC4_SIO_IR_S3) + +#define GET_OTHER_IR(_n) (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \ + (_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \ + (_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \ + (IOC4_OTHER_IR_S3_MEMERR) + + +/* + * All IOC4 registers are 32 bits wide. + */ + +/* + * PCI Memory Space Map + */ +#define IOC4_PCI_ERR_ADDR_L 0x000 /* Low Error Address */ +#define IOC4_PCI_ERR_ADDR_VLD (0x1 << 0) +#define IOC4_PCI_ERR_ADDR_MST_ID_MSK (0xf << 1) +#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK (0xe << 1) +#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK (0x1 << 1) +#define IOC4_PCI_ERR_ADDR_MUL_ERR (0x1 << 5) +#define IOC4_PCI_ERR_ADDR_ADDR_MSK (0x3ffffff << 6) + +/* Interrupt types */ +#define IOC4_SIO_INTR_TYPE 0 +#define IOC4_OTHER_INTR_TYPE 1 +#define IOC4_NUM_INTR_TYPES 2 + +/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES */ +#define IOC4_SIO_IR_S0_TX_MT 0x00000001 /* Serial port 0 TX empty */ +#define IOC4_SIO_IR_S0_RX_FULL 0x00000002 /* Port 0 RX buf full */ +#define IOC4_SIO_IR_S0_RX_HIGH 0x00000004 /* Port 0 RX hiwat */ +#define IOC4_SIO_IR_S0_RX_TIMER 0x00000008 /* Port 0 RX timeout */ +#define IOC4_SIO_IR_S0_DELTA_DCD 0x00000010 /* Port 0 delta DCD */ +#define IOC4_SIO_IR_S0_DELTA_CTS 0x00000020 /* Port 0 delta CTS */ +#define IOC4_SIO_IR_S0_INT 0x00000040 /* Port 0 pass-thru intr */ +#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080 /* Port 0 explicit TX thru */ +#define IOC4_SIO_IR_S1_TX_MT 0x00000100 /* Serial port 1 */ +#define IOC4_SIO_IR_S1_RX_FULL 0x00000200 /* */ +#define IOC4_SIO_IR_S1_RX_HIGH 0x00000400 /* */ +#define IOC4_SIO_IR_S1_RX_TIMER 0x00000800 /* */ +#define IOC4_SIO_IR_S1_DELTA_DCD 0x00001000 /* */ +#define IOC4_SIO_IR_S1_DELTA_CTS 0x00002000 /* */ +#define IOC4_SIO_IR_S1_INT 0x00004000 /* */ +#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000 /* */ +#define IOC4_SIO_IR_S2_TX_MT 0x00010000 /* Serial port 2 */ +#define IOC4_SIO_IR_S2_RX_FULL 0x00020000 /* */ +#define IOC4_SIO_IR_S2_RX_HIGH 0x00040000 /* */ +#define IOC4_SIO_IR_S2_RX_TIMER 0x00080000 /* */ +#define IOC4_SIO_IR_S2_DELTA_DCD 0x00100000 /* */ +#define IOC4_SIO_IR_S2_DELTA_CTS 0x00200000 /* */ +#define IOC4_SIO_IR_S2_INT 0x00400000 /* */ +#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000 /* */ +#define IOC4_SIO_IR_S3_TX_MT 0x01000000 /* Serial port 3 */ +#define IOC4_SIO_IR_S3_RX_FULL 0x02000000 /* */ +#define IOC4_SIO_IR_S3_RX_HIGH 0x04000000 /* */ +#define IOC4_SIO_IR_S3_RX_TIMER 0x08000000 /* */ +#define IOC4_SIO_IR_S3_DELTA_DCD 0x10000000 /* */ +#define IOC4_SIO_IR_S3_DELTA_CTS 0x20000000 /* */ +#define IOC4_SIO_IR_S3_INT 0x40000000 /* */ +#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000 /* */ + +/* Per device interrupt masks */ +#define IOC4_SIO_IR_S0 (IOC4_SIO_IR_S0_TX_MT | \ + IOC4_SIO_IR_S0_RX_FULL | \ + IOC4_SIO_IR_S0_RX_HIGH | \ + IOC4_SIO_IR_S0_RX_TIMER | \ + IOC4_SIO_IR_S0_DELTA_DCD | \ + IOC4_SIO_IR_S0_DELTA_CTS | \ + IOC4_SIO_IR_S0_INT | \ + IOC4_SIO_IR_S0_TX_EXPLICIT) +#define IOC4_SIO_IR_S1 (IOC4_SIO_IR_S1_TX_MT | \ + IOC4_SIO_IR_S1_RX_FULL | \ + IOC4_SIO_IR_S1_RX_HIGH | \ + IOC4_SIO_IR_S1_RX_TIMER | \ + IOC4_SIO_IR_S1_DELTA_DCD | \ + IOC4_SIO_IR_S1_DELTA_CTS | \ + IOC4_SIO_IR_S1_INT | \ + IOC4_SIO_IR_S1_TX_EXPLICIT) +#define IOC4_SIO_IR_S2 (IOC4_SIO_IR_S2_TX_MT | \ + IOC4_SIO_IR_S2_RX_FULL | \ + IOC4_SIO_IR_S2_RX_HIGH | \ + IOC4_SIO_IR_S2_RX_TIMER | \ + IOC4_SIO_IR_S2_DELTA_DCD | \ + IOC4_SIO_IR_S2_DELTA_CTS | \ + IOC4_SIO_IR_S2_INT | \ + IOC4_SIO_IR_S2_TX_EXPLICIT) +#define IOC4_SIO_IR_S3 (IOC4_SIO_IR_S3_TX_MT | \ + IOC4_SIO_IR_S3_RX_FULL | \ + IOC4_SIO_IR_S3_RX_HIGH | \ + IOC4_SIO_IR_S3_RX_TIMER | \ + IOC4_SIO_IR_S3_DELTA_DCD | \ + IOC4_SIO_IR_S3_DELTA_CTS | \ + IOC4_SIO_IR_S3_INT | \ + IOC4_SIO_IR_S3_TX_EXPLICIT) + +/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES */ +#define IOC4_OTHER_IR_ATA_INT 0x00000001 /* ATAPI intr pass-thru */ +#define IOC4_OTHER_IR_ATA_MEMERR 0x00000002 /* ATAPI DMA PCI error */ +#define IOC4_OTHER_IR_S0_MEMERR 0x00000004 /* Port 0 PCI error */ +#define IOC4_OTHER_IR_S1_MEMERR 0x00000008 /* Port 1 PCI error */ +#define IOC4_OTHER_IR_S2_MEMERR 0x00000010 /* Port 2 PCI error */ +#define IOC4_OTHER_IR_S3_MEMERR 0x00000020 /* Port 3 PCI error */ +#define IOC4_OTHER_IR_KBD_INT 0x00000040 /* Keyboard/mouse */ +#define IOC4_OTHER_IR_RESERVED 0x007fff80 /* Reserved */ +#define IOC4_OTHER_IR_RT_INT 0x00800000 /* INT_OUT section output */ +#define IOC4_OTHER_IR_GEN_INT 0xff000000 /* Generic pins */ + +#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \ + IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR) + +/* Bitmasks for IOC4_SIO_CR */ +#define IOC4_SIO_CR_CMD_PULSE_SHIFT 0 /* byte bus strobe shift */ +#define IOC4_SIO_CR_ARB_DIAG_TX0 0x00000000 +#define IOC4_SIO_CR_ARB_DIAG_RX0 0x00000010 +#define IOC4_SIO_CR_ARB_DIAG_TX1 0x00000020 +#define IOC4_SIO_CR_ARB_DIAG_RX1 0x00000030 +#define IOC4_SIO_CR_ARB_DIAG_TX2 0x00000040 +#define IOC4_SIO_CR_ARB_DIAG_RX2 0x00000050 +#define IOC4_SIO_CR_ARB_DIAG_TX3 0x00000060 +#define IOC4_SIO_CR_ARB_DIAG_RX3 0x00000070 +#define IOC4_SIO_CR_SIO_DIAG_IDLE 0x00000080 /* 0 -> active request among + serial ports (ro) */ +/* Defs for some of the generic I/O pins */ +#define IOC4_GPCR_UART0_MODESEL 0x10 /* Pin is output to port 0 + mode sel */ +#define IOC4_GPCR_UART1_MODESEL 0x20 /* Pin is output to port 1 + mode sel */ +#define IOC4_GPCR_UART2_MODESEL 0x40 /* Pin is output to port 2 + mode sel */ +#define IOC4_GPCR_UART3_MODESEL 0x80 /* Pin is output to port 3 + mode sel */ + +#define IOC4_GPPR_UART0_MODESEL_PIN 4 /* GIO pin controlling + uart 0 mode select */ +#define IOC4_GPPR_UART1_MODESEL_PIN 5 /* GIO pin controlling + uart 1 mode select */ +#define IOC4_GPPR_UART2_MODESEL_PIN 6 /* GIO pin controlling + uart 2 mode select */ +#define IOC4_GPPR_UART3_MODESEL_PIN 7 /* GIO pin controlling + uart 3 mode select */ + +/* Bitmasks for serial RX status byte */ +#define IOC4_RXSB_OVERRUN 0x01 /* Char(s) lost */ +#define IOC4_RXSB_PAR_ERR 0x02 /* Parity error */ +#define IOC4_RXSB_FRAME_ERR 0x04 /* Framing error */ +#define IOC4_RXSB_BREAK 0x08 /* Break character */ +#define IOC4_RXSB_CTS 0x10 /* State of CTS */ +#define IOC4_RXSB_DCD 0x20 /* State of DCD */ +#define IOC4_RXSB_MODEM_VALID 0x40 /* DCD, CTS, and OVERRUN are valid */ +#define IOC4_RXSB_DATA_VALID 0x80 /* Data byte, FRAME_ERR PAR_ERR + * & BREAK valid */ + +/* Bitmasks for serial TX control byte */ +#define IOC4_TXCB_INT_WHEN_DONE 0x20 /* Interrupt after this byte is sent */ +#define IOC4_TXCB_INVALID 0x00 /* Byte is invalid */ +#define IOC4_TXCB_VALID 0x40 /* Byte is valid */ +#define IOC4_TXCB_MCR 0x80 /* Data<7:0> to modem control reg */ +#define IOC4_TXCB_DELAY 0xc0 /* Delay data<7:0> mSec */ + +/* Bitmasks for IOC4_SBBR_L */ +#define IOC4_SBBR_L_SIZE 0x00000001 /* 0 == 1KB rings, 1 == 4KB rings */ + +/* Bitmasks for IOC4_SSCR_<3:0> */ +#define IOC4_SSCR_RX_THRESHOLD 0x000001ff /* Hiwater mark */ +#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000 /* TX timer in progress */ +#define IOC4_SSCR_HFC_EN 0x00020000 /* Hardware flow control enabled */ +#define IOC4_SSCR_RX_RING_DCD 0x00040000 /* Post RX record on delta-DCD */ +#define IOC4_SSCR_RX_RING_CTS 0x00080000 /* Post RX record on delta-CTS */ +#define IOC4_SSCR_DIAG 0x00200000 /* Bypass clock divider for sim */ +#define IOC4_SSCR_RX_DRAIN 0x08000000 /* Drain RX buffer to memory */ +#define IOC4_SSCR_DMA_EN 0x10000000 /* Enable ring buffer DMA */ +#define IOC4_SSCR_DMA_PAUSE 0x20000000 /* Pause DMA */ +#define IOC4_SSCR_PAUSE_STATE 0x40000000 /* Sets when PAUSE takes effect */ +#define IOC4_SSCR_RESET 0x80000000 /* Reset DMA channels */ + +/* All producer/comsumer pointers are the same bitfield */ +#define IOC4_PROD_CONS_PTR_4K 0x00000ff8 /* For 4K buffers */ +#define IOC4_PROD_CONS_PTR_1K 0x000003f8 /* For 1K buffers */ +#define IOC4_PROD_CONS_PTR_OFF 3 + +/* Bitmasks for IOC4_SRCIR_<3:0> */ +#define IOC4_SRCIR_ARM 0x80000000 /* Arm RX timer */ + +/* Bitmasks for IOC4_SHADOW_<3:0> */ +#define IOC4_SHADOW_DR 0x00000001 /* Data ready */ +#define IOC4_SHADOW_OE 0x00000002 /* Overrun error */ +#define IOC4_SHADOW_PE 0x00000004 /* Parity error */ +#define IOC4_SHADOW_FE 0x00000008 /* Framing error */ +#define IOC4_SHADOW_BI 0x00000010 /* Break interrupt */ +#define IOC4_SHADOW_THRE 0x00000020 /* Xmit holding register empty */ +#define IOC4_SHADOW_TEMT 0x00000040 /* Xmit shift register empty */ +#define IOC4_SHADOW_RFCE 0x00000080 /* Char in RX fifo has an error */ +#define IOC4_SHADOW_DCTS 0x00010000 /* Delta clear to send */ +#define IOC4_SHADOW_DDCD 0x00080000 /* Delta data carrier detect */ +#define IOC4_SHADOW_CTS 0x00100000 /* Clear to send */ +#define IOC4_SHADOW_DCD 0x00800000 /* Data carrier detect */ +#define IOC4_SHADOW_DTR 0x01000000 /* Data terminal ready */ +#define IOC4_SHADOW_RTS 0x02000000 /* Request to send */ +#define IOC4_SHADOW_OUT1 0x04000000 /* 16550 OUT1 bit */ +#define IOC4_SHADOW_OUT2 0x08000000 /* 16550 OUT2 bit */ +#define IOC4_SHADOW_LOOP 0x10000000 /* Loopback enabled */ + +/* Bitmasks for IOC4_SRTR_<3:0> */ +#define IOC4_SRTR_CNT 0x00000fff /* Reload value for RX timer */ +#define IOC4_SRTR_CNT_VAL 0x0fff0000 /* Current value of RX timer */ +#define IOC4_SRTR_CNT_VAL_SHIFT 16 +#define IOC4_SRTR_HZ 16000 /* SRTR clock frequency */ + +/* Serial port register map used for DMA and PIO serial I/O */ +struct ioc4_serialregs { + uint32_t sscr; + uint32_t stpir; + uint32_t stcir; + uint32_t srpir; + uint32_t srcir; + uint32_t srtr; + uint32_t shadow; +}; + +/* IOC4 UART register map */ +struct ioc4_uartregs { + char i4u_lcr; + union { + char iir; /* read only */ + char fcr; /* write only */ + } u3; + union { + char ier; /* DLAB == 0 */ + char dlm; /* DLAB == 1 */ + } u2; + union { + char rbr; /* read only, DLAB == 0 */ + char thr; /* write only, DLAB == 0 */ + char dll; /* DLAB == 1 */ + } u1; + char i4u_scr; + char i4u_msr; + char i4u_lsr; + char i4u_mcr; +}; + +/* short names */ +#define i4u_dll u1.dll +#define i4u_ier u2.ier +#define i4u_dlm u2.dlm +#define i4u_fcr u3.fcr + +/* Serial port registers used for DMA serial I/O */ +struct ioc4_serial { + uint32_t sbbr01_l; + uint32_t sbbr01_h; + uint32_t sbbr23_l; + uint32_t sbbr23_h; + + struct ioc4_serialregs port_0; + struct ioc4_serialregs port_1; + struct ioc4_serialregs port_2; + struct ioc4_serialregs port_3; + struct ioc4_uartregs uart_0; + struct ioc4_uartregs uart_1; + struct ioc4_uartregs uart_2; + struct ioc4_uartregs uart_3; +} ioc4_serial; + +/* UART clock speed */ +#define IOC4_SER_XIN_CLK_66 66666667 +#define IOC4_SER_XIN_CLK_33 33333333 + +#define IOC4_W_IES 0 +#define IOC4_W_IEC 1 + +typedef void ioc4_intr_func_f(void *, uint32_t); +typedef ioc4_intr_func_f *ioc4_intr_func_t; + +static unsigned int Num_of_ioc4_cards; + +/* defining this will get you LOTS of great debug info */ +//#define DEBUG_INTERRUPTS +#define DPRINT_CONFIG(_x...) ; +//#define DPRINT_CONFIG(_x...) printk _x + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* number of characters we want to transmit to the lower level at a time */ +#define IOC4_MAX_CHARS 256 +#define IOC4_FIFO_CHARS 255 + +/* Device name we're using */ +#define DEVICE_NAME_RS232 "ttyIOC" +#define DEVICE_NAME_RS422 "ttyAIOC" +#define DEVICE_MAJOR 204 +#define DEVICE_MINOR_RS232 50 +#define DEVICE_MINOR_RS422 84 + + +/* register offsets */ +#define IOC4_SERIAL_OFFSET 0x300 + +/* flags for next_char_state */ +#define NCS_BREAK 0x1 +#define NCS_PARITY 0x2 +#define NCS_FRAMING 0x4 +#define NCS_OVERRUN 0x8 + +/* cause we need SOME parameters ... */ +#define MIN_BAUD_SUPPORTED 1200 +#define MAX_BAUD_SUPPORTED 115200 + +/* protocol types supported */ +#define PROTO_RS232 3 +#define PROTO_RS422 7 + +/* Notification types */ +#define N_DATA_READY 0x01 +#define N_OUTPUT_LOWAT 0x02 +#define N_BREAK 0x04 +#define N_PARITY_ERROR 0x08 +#define N_FRAMING_ERROR 0x10 +#define N_OVERRUN_ERROR 0x20 +#define N_DDCD 0x40 +#define N_DCTS 0x80 + +#define N_ALL_INPUT (N_DATA_READY | N_BREAK | \ + N_PARITY_ERROR | N_FRAMING_ERROR | \ + N_OVERRUN_ERROR | N_DDCD | N_DCTS) + +#define N_ALL_OUTPUT N_OUTPUT_LOWAT + +#define N_ALL_ERRORS (N_PARITY_ERROR | N_FRAMING_ERROR | N_OVERRUN_ERROR) + +#define N_ALL (N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK | \ + N_PARITY_ERROR | N_FRAMING_ERROR | \ + N_OVERRUN_ERROR | N_DDCD | N_DCTS) + +#define SER_DIVISOR(_x, clk) (((clk) + (_x) * 8) / ((_x) * 16)) +#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div)) + +/* Some masks */ +#define LCR_MASK_BITS_CHAR (UART_LCR_WLEN5 | UART_LCR_WLEN6 \ + | UART_LCR_WLEN7 | UART_LCR_WLEN8) +#define LCR_MASK_STOP_BITS (UART_LCR_STOP) + +#define PENDING(_p) (readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb) +#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw) + +/* Default to 4k buffers */ +#ifdef IOC4_1K_BUFFERS +#define RING_BUF_SIZE 1024 +#define IOC4_BUF_SIZE_BIT 0 +#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K +#else +#define RING_BUF_SIZE 4096 +#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE +#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K +#endif + +#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4) + +/* + * This is the entry saved by the driver - one per card + */ + +#define UART_PORT_MIN 0 +#define UART_PORT_RS232 UART_PORT_MIN +#define UART_PORT_RS422 1 +#define UART_PORT_COUNT 2 /* one for each mode */ + +struct ioc4_control { + int ic_irq; + struct { + /* uart ports are allocated here - 1 for rs232, 1 for rs422 */ + struct uart_port icp_uart_port[UART_PORT_COUNT]; + /* Handy reference material */ + struct ioc4_port *icp_port; + } ic_port[IOC4_NUM_SERIAL_PORTS]; + struct ioc4_soft *ic_soft; +}; + +/* + * per-IOC4 data structure + */ +#define MAX_IOC4_INTR_ENTS (8 * sizeof(uint32_t)) +struct ioc4_soft { + struct ioc4_misc_regs __iomem *is_ioc4_misc_addr; + struct ioc4_serial __iomem *is_ioc4_serial_addr; + + /* Each interrupt type has an entry in the array */ + struct ioc4_intr_type { + + /* + * Each in-use entry in this array contains at least + * one nonzero bit in sd_bits; no two entries in this + * array have overlapping sd_bits values. + */ + struct ioc4_intr_info { + uint32_t sd_bits; + ioc4_intr_func_f *sd_intr; + void *sd_info; + } is_intr_info[MAX_IOC4_INTR_ENTS]; + + /* Number of entries active in the above array */ + atomic_t is_num_intrs; + } is_intr_type[IOC4_NUM_INTR_TYPES]; + + /* is_ir_lock must be held while + * modifying sio_ie values, so + * we can be sure that sio_ie is + * not changing when we read it + * along with sio_ir. + */ + spinlock_t is_ir_lock; /* SIO_IE[SC] mod lock */ +}; + +/* Local port info for each IOC4 serial ports */ +struct ioc4_port { + struct uart_port *ip_port; /* current active port ptr */ + /* Ptrs for all ports */ + struct uart_port *ip_all_ports[UART_PORT_COUNT]; + /* Back ptrs for this port */ + struct ioc4_control *ip_control; + struct pci_dev *ip_pdev; + struct ioc4_soft *ip_ioc4_soft; + + /* pci mem addresses */ + struct ioc4_misc_regs __iomem *ip_mem; + struct ioc4_serial __iomem *ip_serial; + struct ioc4_serialregs __iomem *ip_serial_regs; + struct ioc4_uartregs __iomem *ip_uart_regs; + + /* Ring buffer page for this port */ + dma_addr_t ip_dma_ringbuf; + /* vaddr of ring buffer */ + struct ring_buffer *ip_cpu_ringbuf; + + /* Rings for this port */ + struct ring *ip_inring; + struct ring *ip_outring; + + /* Hook to port specific values */ + struct hooks *ip_hooks; + + spinlock_t ip_lock; + + /* Various rx/tx parameters */ + int ip_baud; + int ip_tx_lowat; + int ip_rx_timeout; + + /* Copy of notification bits */ + int ip_notify; + + /* Shadow copies of various registers so we don't need to PIO + * read them constantly + */ + uint32_t ip_ienb; /* Enabled interrupts */ + uint32_t ip_sscr; + uint32_t ip_tx_prod; + uint32_t ip_rx_cons; + int ip_pci_bus_speed; + unsigned char ip_flags; +}; + +/* tx low water mark. We need to notify the driver whenever tx is getting + * close to empty so it can refill the tx buffer and keep things going. + * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll + * have no trouble getting in more chars in time (I certainly hope so). + */ +#define TX_LOWAT_LATENCY 1000 +#define TX_LOWAT_HZ (1000000 / TX_LOWAT_LATENCY) +#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ) + +/* Flags per port */ +#define INPUT_HIGH 0x01 +#define DCD_ON 0x02 +#define LOWAT_WRITTEN 0x04 +#define READ_ABORTED 0x08 +#define PORT_ACTIVE 0x10 +#define PORT_INACTIVE 0 /* This is the value when "off" */ + + +/* Since each port has different register offsets and bitmasks + * for everything, we'll store those that we need in tables so we + * don't have to be constantly checking the port we are dealing with. + */ +struct hooks { + uint32_t intr_delta_dcd; + uint32_t intr_delta_cts; + uint32_t intr_tx_mt; + uint32_t intr_rx_timer; + uint32_t intr_rx_high; + uint32_t intr_tx_explicit; + uint32_t intr_dma_error; + uint32_t intr_clear; + uint32_t intr_all; + int rs422_select_pin; +}; + +static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = { + /* Values for port 0 */ + { + IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS, + IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER, + IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT, + IOC4_OTHER_IR_S0_MEMERR, + (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL | + IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER | + IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS | + IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT), + IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN, + }, + + /* Values for port 1 */ + { + IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS, + IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER, + IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT, + IOC4_OTHER_IR_S1_MEMERR, + (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL | + IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER | + IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS | + IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT), + IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN, + }, + + /* Values for port 2 */ + { + IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS, + IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER, + IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT, + IOC4_OTHER_IR_S2_MEMERR, + (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL | + IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER | + IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS | + IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT), + IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN, + }, + + /* Values for port 3 */ + { + IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS, + IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER, + IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT, + IOC4_OTHER_IR_S3_MEMERR, + (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL | + IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER | + IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS | + IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT), + IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN, + } +}; + +/* A ring buffer entry */ +struct ring_entry { + union { + struct { + uint32_t alldata; + uint32_t allsc; + } all; + struct { + char data[4]; /* data bytes */ + char sc[4]; /* status/control */ + } s; + } u; +}; + +/* Test the valid bits in any of the 4 sc chars using "allsc" member */ +#define RING_ANY_VALID \ + ((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101) + +#define ring_sc u.s.sc +#define ring_data u.s.data +#define ring_allsc u.all.allsc + +/* Number of entries per ring buffer. */ +#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry)) + +/* An individual ring */ +struct ring { + struct ring_entry entries[ENTRIES_PER_RING]; +}; + +/* The whole enchilada */ +struct ring_buffer { + struct ring TX_0_OR_2; + struct ring RX_0_OR_2; + struct ring TX_1_OR_3; + struct ring RX_1_OR_3; +}; + +/* Get a ring from a port struct */ +#define RING(_p, _wh) &(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh) + +/* Infinite loop detection. + */ +#define MAXITER 10000000 + +/* Prototypes */ +static void receive_chars(struct uart_port *); +static void handle_intr(void *arg, uint32_t sio_ir); + +/* + * port_is_active - determines if this port is currently active + * @port: ptr to soft struct for this port + * @uart_port: uart port to test for + */ +static inline int port_is_active(struct ioc4_port *port, + struct uart_port *uart_port) +{ + if (port) { + if ((port->ip_flags & PORT_ACTIVE) + && (port->ip_port == uart_port)) + return 1; + } + return 0; +} + + +/** + * write_ireg - write the interrupt regs + * @ioc4_soft: ptr to soft struct for this port + * @val: value to write + * @which: which register + * @type: which ireg set + */ +static inline void +write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type) +{ + struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr; + unsigned long flags; + + spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags); + + switch (type) { + case IOC4_SIO_INTR_TYPE: + switch (which) { + case IOC4_W_IES: + writel(val, &mem->sio_ies.raw); + break; + + case IOC4_W_IEC: + writel(val, &mem->sio_iec.raw); + break; + } + break; + + case IOC4_OTHER_INTR_TYPE: + switch (which) { + case IOC4_W_IES: + writel(val, &mem->other_ies.raw); + break; + + case IOC4_W_IEC: + writel(val, &mem->other_iec.raw); + break; + } + break; + + default: + break; + } + spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags); +} + +/** + * set_baud - Baud rate setting code + * @port: port to set + * @baud: baud rate to use + */ +static int set_baud(struct ioc4_port *port, int baud) +{ + int actual_baud; + int diff; + int lcr; + unsigned short divisor; + struct ioc4_uartregs __iomem *uart; + + divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed); + if (!divisor) + return 1; + actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed); + + diff = actual_baud - baud; + if (diff < 0) + diff = -diff; + + /* If we're within 1%, we've found a match */ + if (diff * 100 > actual_baud) + return 1; + + uart = port->ip_uart_regs; + lcr = readb(&uart->i4u_lcr); + writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr); + writeb((unsigned char)divisor, &uart->i4u_dll); + writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm); + writeb(lcr, &uart->i4u_lcr); + return 0; +} + + +/** + * get_ioc4_port - given a uart port, return the control structure + * @port: uart port + * @set: set this port as current + */ +static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set) +{ + struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev); + struct ioc4_control *control = idd->idd_serial_data; + struct ioc4_port *port; + int port_num, port_type; + + if (control) { + for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; + port_num++ ) { + port = control->ic_port[port_num].icp_port; + if (!port) + continue; + for (port_type = UART_PORT_MIN; + port_type < UART_PORT_COUNT; + port_type++) { + if (the_port == port->ip_all_ports + [port_type]) { + /* set local copy */ + if (set) { + port->ip_port = the_port; + } + return port; + } + } + } + } + return NULL; +} + +/* The IOC4 hardware provides no atomic way to determine if interrupts + * are pending since two reads are required to do so. The handler must + * read the SIO_IR and the SIO_IES, and take the logical and of the + * two. When this value is zero, all interrupts have been serviced and + * the handler may return. + * + * This has the unfortunate "hole" that, if some other CPU or + * some other thread or some higher level interrupt manages to + * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may + * think we have observed SIO_IR&SIO_IE==0 when in fact this + * condition never really occurred. + * + * To solve this, we use a simple spinlock that must be held + * whenever modifying SIO_IE; holding this lock while observing + * both SIO_IR and SIO_IE guarantees that we do not falsely + * conclude that no enabled interrupts are pending. + */ + +static inline uint32_t +pending_intrs(struct ioc4_soft *soft, int type) +{ + struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; + unsigned long flag; + uint32_t intrs = 0; + + BUG_ON(!((type == IOC4_SIO_INTR_TYPE) + || (type == IOC4_OTHER_INTR_TYPE))); + + spin_lock_irqsave(&soft->is_ir_lock, flag); + + switch (type) { + case IOC4_SIO_INTR_TYPE: + intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw); + break; + + case IOC4_OTHER_INTR_TYPE: + intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw); + + /* Don't process any ATA interrupte */ + intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); + break; + + default: + break; + } + spin_unlock_irqrestore(&soft->is_ir_lock, flag); + return intrs; +} + +/** + * port_init - Initialize the sio and ioc4 hardware for a given port + * called per port from attach... + * @port: port to initialize + */ +static int inline port_init(struct ioc4_port *port) +{ + uint32_t sio_cr; + struct hooks *hooks = port->ip_hooks; + struct ioc4_uartregs __iomem *uart; + + /* Idle the IOC4 serial interface */ + writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr); + + /* Wait until any pending bus activity for this port has ceased */ + do + sio_cr = readl(&port->ip_mem->sio_cr.raw); + while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE)); + + /* Finish reset sequence */ + writel(0, &port->ip_serial_regs->sscr); + + /* Once RESET is done, reload cached tx_prod and rx_cons values + * and set rings to empty by making prod == cons + */ + port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; + writel(port->ip_tx_prod, &port->ip_serial_regs->stpir); + port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; + writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); + + /* Disable interrupts for this 16550 */ + uart = port->ip_uart_regs; + writeb(0, &uart->i4u_lcr); + writeb(0, &uart->i4u_ier); + + /* Set the default baud */ + set_baud(port, port->ip_baud); + + /* Set line control to 8 bits no parity */ + writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr); + /* UART_LCR_STOP == 1 stop */ + + /* Enable the FIFOs */ + writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr); + /* then reset 16550 FIFOs */ + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + &uart->i4u_fcr); + + /* Clear modem control register */ + writeb(0, &uart->i4u_mcr); + + /* Clear deltas in modem status register */ + readb(&uart->i4u_msr); + + /* Only do this once per port pair */ + if (port->ip_hooks == &hooks_array[0] + || port->ip_hooks == &hooks_array[2]) { + unsigned long ring_pci_addr; + uint32_t __iomem *sbbr_l; + uint32_t __iomem *sbbr_h; + + if (port->ip_hooks == &hooks_array[0]) { + sbbr_l = &port->ip_serial->sbbr01_l; + sbbr_h = &port->ip_serial->sbbr01_h; + } else { + sbbr_l = &port->ip_serial->sbbr23_l; + sbbr_h = &port->ip_serial->sbbr23_h; + } + + ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf; + DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n", + __func__, ring_pci_addr)); + + writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h); + writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l); + } + + /* Set the receive timeout value to 10 msec */ + writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr); + + /* Set rx threshold, enable DMA */ + /* Set high water mark at 3/4 of full ring */ + port->ip_sscr = (ENTRIES_PER_RING * 3 / 4); + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Disable and clear all serial related interrupt bits */ + write_ireg(port->ip_ioc4_soft, hooks->intr_clear, + IOC4_W_IEC, IOC4_SIO_INTR_TYPE); + port->ip_ienb &= ~hooks->intr_clear; + writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw); + return 0; +} + +/** + * handle_dma_error_intr - service any pending DMA error interrupts for the + * given port - 2nd level called via sd_intr + * @arg: handler arg + * @other_ir: ioc4regs + */ +static void handle_dma_error_intr(void *arg, uint32_t other_ir) +{ + struct ioc4_port *port = (struct ioc4_port *)arg; + struct hooks *hooks = port->ip_hooks; + unsigned long flags; + + spin_lock_irqsave(&port->ip_lock, flags); + + /* ACK the interrupt */ + writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw); + + if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) { + printk(KERN_ERR + "PCI error address is 0x%llx, " + "master is serial port %c %s\n", + (((uint64_t)readl(&port->ip_mem->pci_err_addr_h) + << 32) + | readl(&port->ip_mem->pci_err_addr_l.raw)) + & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' + + ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) & + IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1), + (readl(&port->ip_mem->pci_err_addr_l.raw) + & IOC4_PCI_ERR_ADDR_MST_TYP_MSK) + ? "RX" : "TX"); + + if (readl(&port->ip_mem->pci_err_addr_l.raw) + & IOC4_PCI_ERR_ADDR_MUL_ERR) { + printk(KERN_ERR + "Multiple errors occurred\n"); + } + } + spin_unlock_irqrestore(&port->ip_lock, flags); + + /* Re-enable DMA error interrupts */ + write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES, + IOC4_OTHER_INTR_TYPE); +} + +/** + * intr_connect - interrupt connect function + * @soft: soft struct for this card + * @type: interrupt type + * @intrbits: bit pattern to set + * @intr: handler function + * @info: handler arg + */ +static void +intr_connect(struct ioc4_soft *soft, int type, + uint32_t intrbits, ioc4_intr_func_f * intr, void *info) +{ + int i; + struct ioc4_intr_info *intr_ptr; + + BUG_ON(!((type == IOC4_SIO_INTR_TYPE) + || (type == IOC4_OTHER_INTR_TYPE))); + + i = atomic_inc(&soft-> is_intr_type[type].is_num_intrs) - 1; + BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0))); + + /* Save off the lower level interrupt handler */ + intr_ptr = &soft->is_intr_type[type].is_intr_info[i]; + intr_ptr->sd_bits = intrbits; + intr_ptr->sd_intr = intr; + intr_ptr->sd_info = info; +} + +/** + * ioc4_intr - Top level IOC4 interrupt handler. + * @irq: irq value + * @arg: handler arg + */ + +static irqreturn_t ioc4_intr(int irq, void *arg) +{ + struct ioc4_soft *soft; + uint32_t this_ir, this_mir; + int xx, num_intrs = 0; + int intr_type; + int handled = 0; + struct ioc4_intr_info *intr_info; + + soft = arg; + for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) { + num_intrs = (int)atomic_read( + &soft->is_intr_type[intr_type].is_num_intrs); + + this_mir = this_ir = pending_intrs(soft, intr_type); + + /* Farm out the interrupt to the various drivers depending on + * which interrupt bits are set. + */ + for (xx = 0; xx < num_intrs; xx++) { + intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx]; + if ((this_mir = this_ir & intr_info->sd_bits)) { + /* Disable owned interrupts, call handler */ + handled++; + write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC, + intr_type); + intr_info->sd_intr(intr_info->sd_info, this_mir); + this_ir &= ~this_mir; + } + } + } +#ifdef DEBUG_INTERRUPTS + { + struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr; + unsigned long flag; + + spin_lock_irqsave(&soft->is_ir_lock, flag); + printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x " + "other_ir 0x%x other_ies 0x%x mask 0x%x\n", + __func__, __LINE__, + (void *)mem, readl(&mem->sio_ir.raw), + readl(&mem->sio_ies.raw), + readl(&mem->other_ir.raw), + readl(&mem->other_ies.raw), + IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR); + spin_unlock_irqrestore(&soft->is_ir_lock, flag); + } +#endif + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +/** + * ioc4_attach_local - Device initialization. + * Called at *_attach() time for each + * IOC4 with serial ports in the system. + * @idd: Master module data for this IOC4 + */ +static int inline ioc4_attach_local(struct ioc4_driver_data *idd) +{ + struct ioc4_port *port; + struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS]; + int port_number; + uint16_t ioc4_revid_min = 62; + uint16_t ioc4_revid; + struct pci_dev *pdev = idd->idd_pdev; + struct ioc4_control* control = idd->idd_serial_data; + struct ioc4_soft *soft = control->ic_soft; + void __iomem *ioc4_misc = idd->idd_misc_regs; + void __iomem *ioc4_serial = soft->is_ioc4_serial_addr; + + /* IOC4 firmware must be at least rev 62 */ + pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid); + + printk(KERN_INFO "IOC4 firmware revision %d\n", ioc4_revid); + if (ioc4_revid < ioc4_revid_min) { + printk(KERN_WARNING + "IOC4 serial not supported on firmware rev %d, " + "please upgrade to rev %d or higher\n", + ioc4_revid, ioc4_revid_min); + return -EPERM; + } + BUG_ON(ioc4_misc == NULL); + BUG_ON(ioc4_serial == NULL); + + /* Create port structures for each port */ + for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS; + port_number++) { + port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL); + if (!port) { + printk(KERN_WARNING + "IOC4 serial memory not available for port\n"); + return -ENOMEM; + } + spin_lock_init(&port->ip_lock); + + /* we need to remember the previous ones, to point back to + * them farther down - setting up the ring buffers. + */ + ports[port_number] = port; + + /* Allocate buffers and jumpstart the hardware. */ + control->ic_port[port_number].icp_port = port; + port->ip_ioc4_soft = soft; + port->ip_pdev = pdev; + port->ip_ienb = 0; + /* Use baud rate calculations based on detected PCI + * bus speed. Simply test whether the PCI clock is + * running closer to 66MHz or 33MHz. + */ + if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) { + port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66; + } else { + port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33; + } + port->ip_baud = 9600; + port->ip_control = control; + port->ip_mem = ioc4_misc; + port->ip_serial = ioc4_serial; + + /* point to the right hook */ + port->ip_hooks = &hooks_array[port_number]; + + /* Get direct hooks to the serial regs and uart regs + * for this port + */ + switch (port_number) { + case 0: + port->ip_serial_regs = &(port->ip_serial->port_0); + port->ip_uart_regs = &(port->ip_serial->uart_0); + break; + case 1: + port->ip_serial_regs = &(port->ip_serial->port_1); + port->ip_uart_regs = &(port->ip_serial->uart_1); + break; + case 2: + port->ip_serial_regs = &(port->ip_serial->port_2); + port->ip_uart_regs = &(port->ip_serial->uart_2); + break; + default: + case 3: + port->ip_serial_regs = &(port->ip_serial->port_3); + port->ip_uart_regs = &(port->ip_serial->uart_3); + break; + } + + /* ring buffers are 1 to a pair of ports */ + if (port_number && (port_number & 1)) { + /* odd use the evens buffer */ + port->ip_dma_ringbuf = + ports[port_number - 1]->ip_dma_ringbuf; + port->ip_cpu_ringbuf = + ports[port_number - 1]->ip_cpu_ringbuf; + port->ip_inring = RING(port, RX_1_OR_3); + port->ip_outring = RING(port, TX_1_OR_3); + + } else { + if (port->ip_dma_ringbuf == 0) { + port->ip_cpu_ringbuf = pci_alloc_consistent + (pdev, TOTAL_RING_BUF_SIZE, + &port->ip_dma_ringbuf); + + } + BUG_ON(!((((int64_t)port->ip_dma_ringbuf) & + (TOTAL_RING_BUF_SIZE - 1)) == 0)); + DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p " + "ip_dma_ringbuf 0x%p\n", + __func__, + (void *)port->ip_cpu_ringbuf, + (void *)port->ip_dma_ringbuf)); + port->ip_inring = RING(port, RX_0_OR_2); + port->ip_outring = RING(port, TX_0_OR_2); + } + DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p", + __func__, + port_number, (void *)port, (void *)control)); + DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n", + (void *)port->ip_serial_regs, + (void *)port->ip_uart_regs)); + + /* Initialize the hardware for IOC4 */ + port_init(port); + + DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p " + "outring 0x%p\n", + __func__, + port_number, (void *)port, + (void *)port->ip_inring, + (void *)port->ip_outring)); + + /* Attach interrupt handlers */ + intr_connect(soft, IOC4_SIO_INTR_TYPE, + GET_SIO_IR(port_number), + handle_intr, port); + + intr_connect(soft, IOC4_OTHER_INTR_TYPE, + GET_OTHER_IR(port_number), + handle_dma_error_intr, port); + } + return 0; +} + +/** + * enable_intrs - enable interrupts + * @port: port to enable + * @mask: mask to use + */ +static void enable_intrs(struct ioc4_port *port, uint32_t mask) +{ + struct hooks *hooks = port->ip_hooks; + + if ((port->ip_ienb & mask) != mask) { + write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IES, + IOC4_SIO_INTR_TYPE); + port->ip_ienb |= mask; + } + + if (port->ip_ienb) + write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, + IOC4_W_IES, IOC4_OTHER_INTR_TYPE); +} + +/** + * local_open - local open a port + * @port: port to open + */ +static inline int local_open(struct ioc4_port *port) +{ + int spiniter = 0; + + port->ip_flags = PORT_ACTIVE; + + /* Pause the DMA interface if necessary */ + if (port->ip_sscr & IOC4_SSCR_DMA_EN) { + writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, + &port->ip_serial_regs->sscr); + while((readl(&port->ip_serial_regs-> sscr) + & IOC4_SSCR_PAUSE_STATE) == 0) { + spiniter++; + if (spiniter > MAXITER) { + port->ip_flags = PORT_INACTIVE; + return -1; + } + } + } + + /* Reset the input fifo. If the uart received chars while the port + * was closed and DMA is not enabled, the uart may have a bunch of + * chars hanging around in its rx fifo which will not be discarded + * by rclr in the upper layer. We must get rid of them here. + */ + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR, + &port->ip_uart_regs->i4u_fcr); + + writeb(UART_LCR_WLEN8, &port->ip_uart_regs->i4u_lcr); + /* UART_LCR_STOP == 1 stop */ + + /* Re-enable DMA, set default threshold to intr whenever there is + * data available. + */ + port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; + port->ip_sscr |= 1; /* default threshold */ + + /* Plug in the new sscr. This implicitly clears the DMA_PAUSE + * flag if it was set above + */ + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + port->ip_tx_lowat = 1; + return 0; +} + +/** + * set_rx_timeout - Set rx timeout and threshold values. + * @port: port to use + * @timeout: timeout value in ticks + */ +static inline int set_rx_timeout(struct ioc4_port *port, int timeout) +{ + int threshold; + + port->ip_rx_timeout = timeout; + + /* Timeout is in ticks. Let's figure out how many chars we + * can receive at the current baud rate in that interval + * and set the rx threshold to that amount. There are 4 chars + * per ring entry, so we'll divide the number of chars that will + * arrive in timeout by 4. + * So .... timeout * baud / 10 / HZ / 4, with HZ = 100. + */ + threshold = timeout * port->ip_baud / 4000; + if (threshold == 0) + threshold = 1; /* otherwise we'll intr all the time! */ + + if ((unsigned)threshold > (unsigned)IOC4_SSCR_RX_THRESHOLD) + return 1; + + port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD; + port->ip_sscr |= threshold; + + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Now set the rx timeout to the given value + * again timeout * IOC4_SRTR_HZ / HZ + */ + timeout = timeout * IOC4_SRTR_HZ / 100; + if (timeout > IOC4_SRTR_CNT) + timeout = IOC4_SRTR_CNT; + + writel(timeout, &port->ip_serial_regs->srtr); + return 0; +} + +/** + * config_port - config the hardware + * @port: port to config + * @baud: baud rate for the port + * @byte_size: data size + * @stop_bits: number of stop bits + * @parenb: parity enable ? + * @parodd: odd parity ? + */ +static inline int +config_port(struct ioc4_port *port, + int baud, int byte_size, int stop_bits, int parenb, int parodd) +{ + char lcr, sizebits; + int spiniter = 0; + + DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n", + __func__, baud, byte_size, stop_bits, parenb, parodd)); + + if (set_baud(port, baud)) + return 1; + + switch (byte_size) { + case 5: + sizebits = UART_LCR_WLEN5; + break; + case 6: + sizebits = UART_LCR_WLEN6; + break; + case 7: + sizebits = UART_LCR_WLEN7; + break; + case 8: + sizebits = UART_LCR_WLEN8; + break; + default: + return 1; + } + + /* Pause the DMA interface if necessary */ + if (port->ip_sscr & IOC4_SSCR_DMA_EN) { + writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, + &port->ip_serial_regs->sscr); + while((readl(&port->ip_serial_regs->sscr) + & IOC4_SSCR_PAUSE_STATE) == 0) { + spiniter++; + if (spiniter > MAXITER) + return -1; + } + } + + /* Clear relevant fields in lcr */ + lcr = readb(&port->ip_uart_regs->i4u_lcr); + lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR | + UART_LCR_PARITY | LCR_MASK_STOP_BITS); + + /* Set byte size in lcr */ + lcr |= sizebits; + + /* Set parity */ + if (parenb) { + lcr |= UART_LCR_PARITY; + if (!parodd) + lcr |= UART_LCR_EPAR; + } + + /* Set stop bits */ + if (stop_bits) + lcr |= UART_LCR_STOP /* 2 stop bits */ ; + + writeb(lcr, &port->ip_uart_regs->i4u_lcr); + + /* Re-enable the DMA interface if necessary */ + if (port->ip_sscr & IOC4_SSCR_DMA_EN) { + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + port->ip_baud = baud; + + /* When we get within this number of ring entries of filling the + * entire ring on tx, place an EXPLICIT intr to generate a lowat + * notification when output has drained. + */ + port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4; + if (port->ip_tx_lowat == 0) + port->ip_tx_lowat = 1; + + set_rx_timeout(port, 2); + + return 0; +} + +/** + * do_write - Write bytes to the port. Returns the number of bytes + * actually written. Called from transmit_chars + * @port: port to use + * @buf: the stuff to write + * @len: how many bytes in 'buf' + */ +static inline int do_write(struct ioc4_port *port, char *buf, int len) +{ + int prod_ptr, cons_ptr, total = 0; + struct ring *outring; + struct ring_entry *entry; + struct hooks *hooks = port->ip_hooks; + + BUG_ON(!(len >= 0)); + + prod_ptr = port->ip_tx_prod; + cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK; + outring = port->ip_outring; + + /* Maintain a 1-entry red-zone. The ring buffer is full when + * (cons - prod) % ring_size is 1. Rather than do this subtraction + * in the body of the loop, I'll do it now. + */ + cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK; + + /* Stuff the bytes into the output */ + while ((prod_ptr != cons_ptr) && (len > 0)) { + int xx; + + /* Get 4 bytes (one ring entry) at a time */ + entry = (struct ring_entry *)((caddr_t) outring + prod_ptr); + + /* Invalidate all entries */ + entry->ring_allsc = 0; + + /* Copy in some bytes */ + for (xx = 0; (xx < 4) && (len > 0); xx++) { + entry->ring_data[xx] = *buf++; + entry->ring_sc[xx] = IOC4_TXCB_VALID; + len--; + total++; + } + + /* If we are within some small threshold of filling up the + * entire ring buffer, we must place an EXPLICIT intr here + * to generate a lowat interrupt in case we subsequently + * really do fill up the ring and the caller goes to sleep. + * No need to place more than one though. + */ + if (!(port->ip_flags & LOWAT_WRITTEN) && + ((cons_ptr - prod_ptr) & PROD_CONS_MASK) + <= port->ip_tx_lowat + * (int)sizeof(struct ring_entry)) { + port->ip_flags |= LOWAT_WRITTEN; + entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE; + } + + /* Go on to next entry */ + prod_ptr += sizeof(struct ring_entry); + prod_ptr &= PROD_CONS_MASK; + } + + /* If we sent something, start DMA if necessary */ + if (total > 0 && !(port->ip_sscr & IOC4_SSCR_DMA_EN)) { + port->ip_sscr |= IOC4_SSCR_DMA_EN; + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + + /* Store the new producer pointer. If tx is disabled, we stuff the + * data into the ring buffer, but we don't actually start tx. + */ + if (!uart_tx_stopped(port->ip_port)) { + writel(prod_ptr, &port->ip_serial_regs->stpir); + + /* If we are now transmitting, enable tx_mt interrupt so we + * can disable DMA if necessary when the tx finishes. + */ + if (total > 0) + enable_intrs(port, hooks->intr_tx_mt); + } + port->ip_tx_prod = prod_ptr; + return total; +} + +/** + * disable_intrs - disable interrupts + * @port: port to enable + * @mask: mask to use + */ +static void disable_intrs(struct ioc4_port *port, uint32_t mask) +{ + struct hooks *hooks = port->ip_hooks; + + if (port->ip_ienb & mask) { + write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IEC, + IOC4_SIO_INTR_TYPE); + port->ip_ienb &= ~mask; + } + + if (!port->ip_ienb) + write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, + IOC4_W_IEC, IOC4_OTHER_INTR_TYPE); +} + +/** + * set_notification - Modify event notification + * @port: port to use + * @mask: events mask + * @set_on: set ? + */ +static int set_notification(struct ioc4_port *port, int mask, int set_on) +{ + struct hooks *hooks = port->ip_hooks; + uint32_t intrbits, sscrbits; + + BUG_ON(!mask); + + intrbits = sscrbits = 0; + + if (mask & N_DATA_READY) + intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high); + if (mask & N_OUTPUT_LOWAT) + intrbits |= hooks->intr_tx_explicit; + if (mask & N_DDCD) { + intrbits |= hooks->intr_delta_dcd; + sscrbits |= IOC4_SSCR_RX_RING_DCD; + } + if (mask & N_DCTS) + intrbits |= hooks->intr_delta_cts; + + if (set_on) { + enable_intrs(port, intrbits); + port->ip_notify |= mask; + port->ip_sscr |= sscrbits; + } else { + disable_intrs(port, intrbits); + port->ip_notify &= ~mask; + port->ip_sscr &= ~sscrbits; + } + + /* We require DMA if either DATA_READY or DDCD notification is + * currently requested. If neither of these is requested and + * there is currently no tx in progress, DMA may be disabled. + */ + if (port->ip_notify & (N_DATA_READY | N_DDCD)) + port->ip_sscr |= IOC4_SSCR_DMA_EN; + else if (!(port->ip_ienb & hooks->intr_tx_mt)) + port->ip_sscr &= ~IOC4_SSCR_DMA_EN; + + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + return 0; +} + +/** + * set_mcr - set the master control reg + * @the_port: port to use + * @mask1: mcr mask + * @mask2: shadow mask + */ +static inline int set_mcr(struct uart_port *the_port, + int mask1, int mask2) +{ + struct ioc4_port *port = get_ioc4_port(the_port, 0); + uint32_t shadow; + int spiniter = 0; + char mcr; + + if (!port) + return -1; + + /* Pause the DMA interface if necessary */ + if (port->ip_sscr & IOC4_SSCR_DMA_EN) { + writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE, + &port->ip_serial_regs->sscr); + while ((readl(&port->ip_serial_regs->sscr) + & IOC4_SSCR_PAUSE_STATE) == 0) { + spiniter++; + if (spiniter > MAXITER) + return -1; + } + } + shadow = readl(&port->ip_serial_regs->shadow); + mcr = (shadow & 0xff000000) >> 24; + + /* Set new value */ + mcr |= mask1; + shadow |= mask2; + + writeb(mcr, &port->ip_uart_regs->i4u_mcr); + writel(shadow, &port->ip_serial_regs->shadow); + + /* Re-enable the DMA interface if necessary */ + if (port->ip_sscr & IOC4_SSCR_DMA_EN) { + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + return 0; +} + +/** + * ioc4_set_proto - set the protocol for the port + * @port: port to use + * @proto: protocol to use + */ +static int ioc4_set_proto(struct ioc4_port *port, int proto) +{ + struct hooks *hooks = port->ip_hooks; + + switch (proto) { + case PROTO_RS232: + /* Clear the appropriate GIO pin */ + writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw)); + break; + + case PROTO_RS422: + /* Set the appropriate GIO pin */ + writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw)); + break; + + default: + return 1; + } + return 0; +} + +/** + * transmit_chars - upper level write, called with ip_lock + * @the_port: port to write + */ +static void transmit_chars(struct uart_port *the_port) +{ + int xmit_count, tail, head; + int result; + char *start; + struct tty_struct *tty; + struct ioc4_port *port = get_ioc4_port(the_port, 0); + struct uart_state *state; + + if (!the_port) + return; + if (!port) + return; + + state = the_port->state; + tty = state->port.tty; + + if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { + /* Nothing to do or hw stopped */ + set_notification(port, N_ALL_OUTPUT, 0); + return; + } + + head = state->xmit.head; + tail = state->xmit.tail; + start = (char *)&state->xmit.buf[tail]; + + /* write out all the data or until the end of the buffer */ + xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); + if (xmit_count > 0) { + result = do_write(port, start, xmit_count); + if (result > 0) { + /* booking */ + xmit_count -= result; + the_port->icount.tx += result; + /* advance the pointers */ + tail += result; + tail &= UART_XMIT_SIZE - 1; + state->xmit.tail = tail; + start = (char *)&state->xmit.buf[tail]; + } + } + if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) + uart_write_wakeup(the_port); + + if (uart_circ_empty(&state->xmit)) { + set_notification(port, N_OUTPUT_LOWAT, 0); + } else { + set_notification(port, N_OUTPUT_LOWAT, 1); + } +} + +/** + * ioc4_change_speed - change the speed of the port + * @the_port: port to change + * @new_termios: new termios settings + * @old_termios: old termios settings + */ +static void +ioc4_change_speed(struct uart_port *the_port, + struct ktermios *new_termios, struct ktermios *old_termios) +{ + struct ioc4_port *port = get_ioc4_port(the_port, 0); + int baud, bits; + unsigned cflag, iflag; + int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; + struct uart_state *state = the_port->state; + + cflag = new_termios->c_cflag; + iflag = new_termios->c_iflag; + + switch (cflag & CSIZE) { + case CS5: + new_data = 5; + bits = 7; + break; + case CS6: + new_data = 6; + bits = 8; + break; + case CS7: + new_data = 7; + bits = 9; + break; + case CS8: + new_data = 8; + bits = 10; + break; + default: + /* cuz we always need a default ... */ + new_data = 5; + bits = 7; + break; + } + if (cflag & CSTOPB) { + bits++; + new_stop = 1; + } + if (cflag & PARENB) { + bits++; + new_parity_enable = 1; + if (cflag & PARODD) + new_parity = 1; + } + baud = uart_get_baud_rate(the_port, new_termios, old_termios, + MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED); + DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud)); + + /* default is 9600 */ + if (!baud) + baud = 9600; + + if (!the_port->fifosize) + the_port->fifosize = IOC4_FIFO_CHARS; + the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10)); + the_port->timeout += HZ / 50; /* Add .02 seconds of slop */ + + the_port->ignore_status_mask = N_ALL_INPUT; + + state->port.tty->low_latency = 1; + + if (iflag & IGNPAR) + the_port->ignore_status_mask &= ~(N_PARITY_ERROR + | N_FRAMING_ERROR); + if (iflag & IGNBRK) { + the_port->ignore_status_mask &= ~N_BREAK; + if (iflag & IGNPAR) + the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; + } + if (!(cflag & CREAD)) { + /* ignore everything */ + the_port->ignore_status_mask &= ~N_DATA_READY; + } + + if (cflag & CRTSCTS) { + port->ip_sscr |= IOC4_SSCR_HFC_EN; + } + else { + port->ip_sscr &= ~IOC4_SSCR_HFC_EN; + } + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + + /* Set the configuration and proper notification call */ + DPRINT_CONFIG(("%s : port 0x%p cflag 0%o " + "config_port(baud %d data %d stop %d p enable %d parity %d)," + " notification 0x%x\n", + __func__, (void *)port, cflag, baud, new_data, new_stop, + new_parity_enable, new_parity, the_port->ignore_status_mask)); + + if ((config_port(port, baud, /* baud */ + new_data, /* byte size */ + new_stop, /* stop bits */ + new_parity_enable, /* set parity */ + new_parity)) >= 0) { /* parity 1==odd */ + set_notification(port, the_port->ignore_status_mask, 1); + } +} + +/** + * ic4_startup_local - Start up the serial port - returns >= 0 if no errors + * @the_port: Port to operate on + */ +static inline int ic4_startup_local(struct uart_port *the_port) +{ + struct ioc4_port *port; + struct uart_state *state; + + if (!the_port) + return -1; + + port = get_ioc4_port(the_port, 0); + if (!port) + return -1; + + state = the_port->state; + + local_open(port); + + /* set the protocol - mapbase has the port type */ + ioc4_set_proto(port, the_port->mapbase); + + /* set the speed of the serial port */ + ioc4_change_speed(the_port, state->port.tty->termios, + (struct ktermios *)0); + + return 0; +} + +/* + * ioc4_cb_output_lowat - called when the output low water mark is hit + * @the_port: port to output + */ +static void ioc4_cb_output_lowat(struct uart_port *the_port) +{ + unsigned long pflags; + + /* ip_lock is set on the call here */ + if (the_port) { + spin_lock_irqsave(&the_port->lock, pflags); + transmit_chars(the_port); + spin_unlock_irqrestore(&the_port->lock, pflags); + } +} + +/** + * handle_intr - service any interrupts for the given port - 2nd level + * called via sd_intr + * @arg: handler arg + * @sio_ir: ioc4regs + */ +static void handle_intr(void *arg, uint32_t sio_ir) +{ + struct ioc4_port *port = (struct ioc4_port *)arg; + struct hooks *hooks = port->ip_hooks; + unsigned int rx_high_rd_aborted = 0; + unsigned long flags; + struct uart_port *the_port; + int loop_counter; + + /* Possible race condition here: The tx_mt interrupt bit may be + * cleared without the intervention of the interrupt handler, + * e.g. by a write. If the top level interrupt handler reads a + * tx_mt, then some other processor does a write, starting up + * output, then we come in here, see the tx_mt and stop DMA, the + * output started by the other processor will hang. Thus we can + * only rely on tx_mt being legitimate if it is read while the + * port lock is held. Therefore this bit must be ignored in the + * passed in interrupt mask which was read by the top level + * interrupt handler since the port lock was not held at the time + * it was read. We can only rely on this bit being accurate if it + * is read while the port lock is held. So we'll clear it for now, + * and reload it later once we have the port lock. + */ + sio_ir &= ~(hooks->intr_tx_mt); + + spin_lock_irqsave(&port->ip_lock, flags); + + loop_counter = MAXITER; /* to avoid hangs */ + + do { + uint32_t shadow; + + if ( loop_counter-- <= 0 ) { + printk(KERN_WARNING "IOC4 serial: " + "possible hang condition/" + "port stuck on interrupt.\n"); + break; + } + + /* Handle a DCD change */ + if (sio_ir & hooks->intr_delta_dcd) { + /* ACK the interrupt */ + writel(hooks->intr_delta_dcd, + &port->ip_mem->sio_ir.raw); + + shadow = readl(&port->ip_serial_regs->shadow); + + if ((port->ip_notify & N_DDCD) + && (shadow & IOC4_SHADOW_DCD) + && (port->ip_port)) { + the_port = port->ip_port; + the_port->icount.dcd = 1; + wake_up_interruptible + (&the_port->state->port.delta_msr_wait); + } else if ((port->ip_notify & N_DDCD) + && !(shadow & IOC4_SHADOW_DCD)) { + /* Flag delta DCD/no DCD */ + port->ip_flags |= DCD_ON; + } + } + + /* Handle a CTS change */ + if (sio_ir & hooks->intr_delta_cts) { + /* ACK the interrupt */ + writel(hooks->intr_delta_cts, + &port->ip_mem->sio_ir.raw); + + shadow = readl(&port->ip_serial_regs->shadow); + + if ((port->ip_notify & N_DCTS) + && (port->ip_port)) { + the_port = port->ip_port; + the_port->icount.cts = + (shadow & IOC4_SHADOW_CTS) ? 1 : 0; + wake_up_interruptible + (&the_port->state->port.delta_msr_wait); + } + } + + /* rx timeout interrupt. Must be some data available. Put this + * before the check for rx_high since servicing this condition + * may cause that condition to clear. + */ + if (sio_ir & hooks->intr_rx_timer) { + /* ACK the interrupt */ + writel(hooks->intr_rx_timer, + &port->ip_mem->sio_ir.raw); + + if ((port->ip_notify & N_DATA_READY) + && (port->ip_port)) { + /* ip_lock is set on call here */ + receive_chars(port->ip_port); + } + } + + /* rx high interrupt. Must be after rx_timer. */ + else if (sio_ir & hooks->intr_rx_high) { + /* Data available, notify upper layer */ + if ((port->ip_notify & N_DATA_READY) + && port->ip_port) { + /* ip_lock is set on call here */ + receive_chars(port->ip_port); + } + + /* We can't ACK this interrupt. If receive_chars didn't + * cause the condition to clear, we'll have to disable + * the interrupt until the data is drained. + * If the read was aborted, don't disable the interrupt + * as this may cause us to hang indefinitely. An + * aborted read generally means that this interrupt + * hasn't been delivered to the cpu yet anyway, even + * though we see it as asserted when we read the sio_ir. + */ + if ((sio_ir = PENDING(port)) & hooks->intr_rx_high) { + if ((port->ip_flags & READ_ABORTED) == 0) { + port->ip_ienb &= ~hooks->intr_rx_high; + port->ip_flags |= INPUT_HIGH; + } else { + rx_high_rd_aborted++; + } + } + } + + /* We got a low water interrupt: notify upper layer to + * send more data. Must come before tx_mt since servicing + * this condition may cause that condition to clear. + */ + if (sio_ir & hooks->intr_tx_explicit) { + port->ip_flags &= ~LOWAT_WRITTEN; + + /* ACK the interrupt */ + writel(hooks->intr_tx_explicit, + &port->ip_mem->sio_ir.raw); + + if (port->ip_notify & N_OUTPUT_LOWAT) + ioc4_cb_output_lowat(port->ip_port); + } + + /* Handle tx_mt. Must come after tx_explicit. */ + else if (sio_ir & hooks->intr_tx_mt) { + /* If we are expecting a lowat notification + * and we get to this point it probably means that for + * some reason the tx_explicit didn't work as expected + * (that can legitimately happen if the output buffer is + * filled up in just the right way). + * So send the notification now. + */ + if (port->ip_notify & N_OUTPUT_LOWAT) { + ioc4_cb_output_lowat(port->ip_port); + + /* We need to reload the sio_ir since the lowat + * call may have caused another write to occur, + * clearing the tx_mt condition. + */ + sio_ir = PENDING(port); + } + + /* If the tx_mt condition still persists even after the + * lowat call, we've got some work to do. + */ + if (sio_ir & hooks->intr_tx_mt) { + + /* If we are not currently expecting DMA input, + * and the transmitter has just gone idle, + * there is no longer any reason for DMA, so + * disable it. + */ + if (!(port->ip_notify + & (N_DATA_READY | N_DDCD))) { + BUG_ON(!(port->ip_sscr + & IOC4_SSCR_DMA_EN)); + port->ip_sscr &= ~IOC4_SSCR_DMA_EN; + writel(port->ip_sscr, + &port->ip_serial_regs->sscr); + } + + /* Prevent infinite tx_mt interrupt */ + port->ip_ienb &= ~hooks->intr_tx_mt; + } + } + sio_ir = PENDING(port); + + /* if the read was aborted and only hooks->intr_rx_high, + * clear hooks->intr_rx_high, so we do not loop forever. + */ + + if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) { + sio_ir &= ~hooks->intr_rx_high; + } + } while (sio_ir & hooks->intr_all); + + spin_unlock_irqrestore(&port->ip_lock, flags); + + /* Re-enable interrupts before returning from interrupt handler. + * Getting interrupted here is okay. It'll just v() our semaphore, and + * we'll come through the loop again. + */ + + write_ireg(port->ip_ioc4_soft, port->ip_ienb, IOC4_W_IES, + IOC4_SIO_INTR_TYPE); +} + +/* + * ioc4_cb_post_ncs - called for some basic errors + * @port: port to use + * @ncs: event + */ +static void ioc4_cb_post_ncs(struct uart_port *the_port, int ncs) +{ + struct uart_icount *icount; + + icount = &the_port->icount; + + if (ncs & NCS_BREAK) + icount->brk++; + if (ncs & NCS_FRAMING) + icount->frame++; + if (ncs & NCS_OVERRUN) + icount->overrun++; + if (ncs & NCS_PARITY) + icount->parity++; +} + +/** + * do_read - Read in bytes from the port. Return the number of bytes + * actually read. + * @the_port: port to use + * @buf: place to put the stuff we read + * @len: how big 'buf' is + */ + +static inline int do_read(struct uart_port *the_port, unsigned char *buf, + int len) +{ + int prod_ptr, cons_ptr, total; + struct ioc4_port *port = get_ioc4_port(the_port, 0); + struct ring *inring; + struct ring_entry *entry; + struct hooks *hooks = port->ip_hooks; + int byte_num; + char *sc; + int loop_counter; + + BUG_ON(!(len >= 0)); + BUG_ON(!port); + + /* There is a nasty timing issue in the IOC4. When the rx_timer + * expires or the rx_high condition arises, we take an interrupt. + * At some point while servicing the interrupt, we read bytes from + * the ring buffer and re-arm the rx_timer. However the rx_timer is + * not started until the first byte is received *after* it is armed, + * and any bytes pending in the rx construction buffers are not drained + * to memory until either there are 4 bytes available or the rx_timer + * expires. This leads to a potential situation where data is left + * in the construction buffers forever - 1 to 3 bytes were received + * after the interrupt was generated but before the rx_timer was + * re-armed. At that point as long as no subsequent bytes are received + * the timer will never be started and the bytes will remain in the + * construction buffer forever. The solution is to execute a DRAIN + * command after rearming the timer. This way any bytes received before + * the DRAIN will be drained to memory, and any bytes received after + * the DRAIN will start the TIMER and be drained when it expires. + * Luckily, this only needs to be done when the DMA buffer is empty + * since there is no requirement that this function return all + * available data as long as it returns some. + */ + /* Re-arm the timer */ + writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir); + + prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK; + cons_ptr = port->ip_rx_cons; + + if (prod_ptr == cons_ptr) { + int reset_dma = 0; + + /* Input buffer appears empty, do a flush. */ + + /* DMA must be enabled for this to work. */ + if (!(port->ip_sscr & IOC4_SSCR_DMA_EN)) { + port->ip_sscr |= IOC4_SSCR_DMA_EN; + reset_dma = 1; + } + + /* Potential race condition: we must reload the srpir after + * issuing the drain command, otherwise we could think the rx + * buffer is empty, then take a very long interrupt, and when + * we come back it's full and we wait forever for the drain to + * complete. + */ + writel(port->ip_sscr | IOC4_SSCR_RX_DRAIN, + &port->ip_serial_regs->sscr); + prod_ptr = readl(&port->ip_serial_regs->srpir) + & PROD_CONS_MASK; + + /* We must not wait for the DRAIN to complete unless there are + * at least 8 bytes (2 ring entries) available to receive the + * data otherwise the DRAIN will never complete and we'll + * deadlock here. + * In fact, to make things easier, I'll just ignore the flush if + * there is any data at all now available. + */ + if (prod_ptr == cons_ptr) { + loop_counter = 0; + while (readl(&port->ip_serial_regs->sscr) & + IOC4_SSCR_RX_DRAIN) { + loop_counter++; + if (loop_counter > MAXITER) + return -1; + } + + /* SIGH. We have to reload the prod_ptr *again* since + * the drain may have caused it to change + */ + prod_ptr = readl(&port->ip_serial_regs->srpir) + & PROD_CONS_MASK; + } + if (reset_dma) { + port->ip_sscr &= ~IOC4_SSCR_DMA_EN; + writel(port->ip_sscr, &port->ip_serial_regs->sscr); + } + } + inring = port->ip_inring; + port->ip_flags &= ~READ_ABORTED; + + total = 0; + loop_counter = 0xfffff; /* to avoid hangs */ + + /* Grab bytes from the hardware */ + while ((prod_ptr != cons_ptr) && (len > 0)) { + entry = (struct ring_entry *)((caddr_t)inring + cons_ptr); + + if ( loop_counter-- <= 0 ) { + printk(KERN_WARNING "IOC4 serial: " + "possible hang condition/" + "port stuck on read.\n"); + break; + } + + /* According to the producer pointer, this ring entry + * must contain some data. But if the PIO happened faster + * than the DMA, the data may not be available yet, so let's + * wait until it arrives. + */ + if ((entry->ring_allsc & RING_ANY_VALID) == 0) { + /* Indicate the read is aborted so we don't disable + * the interrupt thinking that the consumer is + * congested. + */ + port->ip_flags |= READ_ABORTED; + len = 0; + break; + } + + /* Load the bytes/status out of the ring entry */ + for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) { + sc = &(entry->ring_sc[byte_num]); + + /* Check for change in modem state or overrun */ + if ((*sc & IOC4_RXSB_MODEM_VALID) + && (port->ip_notify & N_DDCD)) { + /* Notify upper layer if DCD dropped */ + + if ((port->ip_flags & DCD_ON) + && !(*sc & IOC4_RXSB_DCD)) { + + /* If we have already copied some data, + * return it. We'll pick up the carrier + * drop on the next pass. That way we + * don't throw away the data that has + * already been copied back to + * the caller's buffer. + */ + if (total > 0) { + len = 0; + break; + } + port->ip_flags &= ~DCD_ON; + + /* Turn off this notification so the + * carrier drop protocol won't see it + * again when it does a read. + */ + *sc &= ~IOC4_RXSB_MODEM_VALID; + + /* To keep things consistent, we need + * to update the consumer pointer so + * the next reader won't come in and + * try to read the same ring entries + * again. This must be done here before + * the dcd change. + */ + + if ((entry->ring_allsc & RING_ANY_VALID) + == 0) { + cons_ptr += (int)sizeof + (struct ring_entry); + cons_ptr &= PROD_CONS_MASK; + } + writel(cons_ptr, + &port->ip_serial_regs->srcir); + port->ip_rx_cons = cons_ptr; + + /* Notify upper layer of carrier drop */ + if ((port->ip_notify & N_DDCD) + && port->ip_port) { + the_port->icount.dcd = 0; + wake_up_interruptible + (&the_port->state-> + port.delta_msr_wait); + } + + /* If we had any data to return, we + * would have returned it above. + */ + return 0; + } + } + if (*sc & IOC4_RXSB_MODEM_VALID) { + /* Notify that an input overrun occurred */ + if ((*sc & IOC4_RXSB_OVERRUN) + && (port->ip_notify & N_OVERRUN_ERROR)) { + ioc4_cb_post_ncs(the_port, NCS_OVERRUN); + } + /* Don't look at this byte again */ + *sc &= ~IOC4_RXSB_MODEM_VALID; + } + + /* Check for valid data or RX errors */ + if ((*sc & IOC4_RXSB_DATA_VALID) && + ((*sc & (IOC4_RXSB_PAR_ERR + | IOC4_RXSB_FRAME_ERR + | IOC4_RXSB_BREAK)) + && (port->ip_notify & (N_PARITY_ERROR + | N_FRAMING_ERROR + | N_BREAK)))) { + /* There is an error condition on the next byte. + * If we have already transferred some bytes, + * we'll stop here. Otherwise if this is the + * first byte to be read, we'll just transfer + * it alone after notifying the + * upper layer of its status. + */ + if (total > 0) { + len = 0; + break; + } else { + if ((*sc & IOC4_RXSB_PAR_ERR) && + (port->ip_notify & N_PARITY_ERROR)) { + ioc4_cb_post_ncs(the_port, + NCS_PARITY); + } + if ((*sc & IOC4_RXSB_FRAME_ERR) && + (port->ip_notify & N_FRAMING_ERROR)){ + ioc4_cb_post_ncs(the_port, + NCS_FRAMING); + } + if ((*sc & IOC4_RXSB_BREAK) + && (port->ip_notify & N_BREAK)) { + ioc4_cb_post_ncs + (the_port, + NCS_BREAK); + } + len = 1; + } + } + if (*sc & IOC4_RXSB_DATA_VALID) { + *sc &= ~IOC4_RXSB_DATA_VALID; + *buf = entry->ring_data[byte_num]; + buf++; + len--; + total++; + } + } + + /* If we used up this entry entirely, go on to the next one, + * otherwise we must have run out of buffer space, so + * leave the consumer pointer here for the next read in case + * there are still unread bytes in this entry. + */ + if ((entry->ring_allsc & RING_ANY_VALID) == 0) { + cons_ptr += (int)sizeof(struct ring_entry); + cons_ptr &= PROD_CONS_MASK; + } + } + + /* Update consumer pointer and re-arm rx timer interrupt */ + writel(cons_ptr, &port->ip_serial_regs->srcir); + port->ip_rx_cons = cons_ptr; + + /* If we have now dipped below the rx high water mark and we have + * rx_high interrupt turned off, we can now turn it back on again. + */ + if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr) + & PROD_CONS_MASK) < ((port->ip_sscr & + IOC4_SSCR_RX_THRESHOLD) + << IOC4_PROD_CONS_PTR_OFF))) { + port->ip_flags &= ~INPUT_HIGH; + enable_intrs(port, hooks->intr_rx_high); + } + return total; +} + +/** + * receive_chars - upper level read. Called with ip_lock. + * @the_port: port to read from + */ +static void receive_chars(struct uart_port *the_port) +{ + struct tty_struct *tty; + unsigned char ch[IOC4_MAX_CHARS]; + int read_count, request_count = IOC4_MAX_CHARS; + struct uart_icount *icount; + struct uart_state *state = the_port->state; + unsigned long pflags; + + /* Make sure all the pointers are "good" ones */ + if (!state) + return; + if (!state->port.tty) + return; + + spin_lock_irqsave(&the_port->lock, pflags); + tty = state->port.tty; + + request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS); + + if (request_count > 0) { + icount = &the_port->icount; + read_count = do_read(the_port, ch, request_count); + if (read_count > 0) { + tty_insert_flip_string(tty, ch, read_count); + icount->rx += read_count; + } + } + + spin_unlock_irqrestore(&the_port->lock, pflags); + + tty_flip_buffer_push(tty); +} + +/** + * ic4_type - What type of console are we? + * @port: Port to operate with (we ignore since we only have one port) + * + */ +static const char *ic4_type(struct uart_port *the_port) +{ + if (the_port->mapbase == PROTO_RS232) + return "SGI IOC4 Serial [rs232]"; + else + return "SGI IOC4 Serial [rs422]"; +} + +/** + * ic4_tx_empty - Is the transmitter empty? + * @port: Port to operate on + * + */ +static unsigned int ic4_tx_empty(struct uart_port *the_port) +{ + struct ioc4_port *port = get_ioc4_port(the_port, 0); + unsigned int ret = 0; + + if (port_is_active(port, the_port)) { + if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT) + ret = TIOCSER_TEMT; + } + return ret; +} + +/** + * ic4_stop_tx - stop the transmitter + * @port: Port to operate on + * + */ +static void ic4_stop_tx(struct uart_port *the_port) +{ + struct ioc4_port *port = get_ioc4_port(the_port, 0); + + if (port_is_active(port, the_port)) + set_notification(port, N_OUTPUT_LOWAT, 0); +} + +/** + * null_void_function - + * @port: Port to operate on + * + */ +static void null_void_function(struct uart_port *the_port) +{ +} + +/** + * ic4_shutdown - shut down the port - free irq and disable + * @port: Port to shut down + * + */ +static void ic4_shutdown(struct uart_port *the_port) +{ + unsigned long port_flags; + struct ioc4_port *port; + struct uart_state *state; + + port = get_ioc4_port(the_port, 0); + if (!port) + return; + + state = the_port->state; + port->ip_port = NULL; + + wake_up_interruptible(&state->port.delta_msr_wait); + + if (state->port.tty) + set_bit(TTY_IO_ERROR, &state->port.tty->flags); + + spin_lock_irqsave(&the_port->lock, port_flags); + set_notification(port, N_ALL, 0); + port->ip_flags = PORT_INACTIVE; + spin_unlock_irqrestore(&the_port->lock, port_flags); +} + +/** + * ic4_set_mctrl - set control lines (dtr, rts, etc) + * @port: Port to operate on + * @mctrl: Lines to set/unset + * + */ +static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl) +{ + unsigned char mcr = 0; + struct ioc4_port *port; + + port = get_ioc4_port(the_port, 0); + if (!port_is_active(port, the_port)) + return; + + 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; + + set_mcr(the_port, mcr, IOC4_SHADOW_DTR); +} + +/** + * ic4_get_mctrl - get control line info + * @port: port to operate on + * + */ +static unsigned int ic4_get_mctrl(struct uart_port *the_port) +{ + struct ioc4_port *port = get_ioc4_port(the_port, 0); + uint32_t shadow; + unsigned int ret = 0; + + if (!port_is_active(port, the_port)) + return 0; + + shadow = readl(&port->ip_serial_regs->shadow); + if (shadow & IOC4_SHADOW_DCD) + ret |= TIOCM_CAR; + if (shadow & IOC4_SHADOW_DR) + ret |= TIOCM_DSR; + if (shadow & IOC4_SHADOW_CTS) + ret |= TIOCM_CTS; + return ret; +} + +/** + * ic4_start_tx - Start transmitter, flush any output + * @port: Port to operate on + * + */ +static void ic4_start_tx(struct uart_port *the_port) +{ + struct ioc4_port *port = get_ioc4_port(the_port, 0); + + if (port_is_active(port, the_port)) { + set_notification(port, N_OUTPUT_LOWAT, 1); + enable_intrs(port, port->ip_hooks->intr_tx_mt); + } +} + +/** + * ic4_break_ctl - handle breaks + * @port: Port to operate on + * @break_state: Break state + * + */ +static void ic4_break_ctl(struct uart_port *the_port, int break_state) +{ +} + +/** + * ic4_startup - Start up the serial port + * @port: Port to operate on + * + */ +static int ic4_startup(struct uart_port *the_port) +{ + int retval; + struct ioc4_port *port; + struct ioc4_control *control; + struct uart_state *state; + unsigned long port_flags; + + if (!the_port) + return -ENODEV; + port = get_ioc4_port(the_port, 1); + if (!port) + return -ENODEV; + state = the_port->state; + + control = port->ip_control; + if (!control) { + port->ip_port = NULL; + return -ENODEV; + } + + /* Start up the serial port */ + spin_lock_irqsave(&the_port->lock, port_flags); + retval = ic4_startup_local(the_port); + spin_unlock_irqrestore(&the_port->lock, port_flags); + return retval; +} + +/** + * ic4_set_termios - set termios stuff + * @port: port to operate on + * @termios: New settings + * @termios: Old + * + */ +static void +ic4_set_termios(struct uart_port *the_port, + struct ktermios *termios, struct ktermios *old_termios) +{ + unsigned long port_flags; + + spin_lock_irqsave(&the_port->lock, port_flags); + ioc4_change_speed(the_port, termios, old_termios); + spin_unlock_irqrestore(&the_port->lock, port_flags); +} + +/** + * ic4_request_port - allocate resources for port - no op.... + * @port: port to operate on + * + */ +static int ic4_request_port(struct uart_port *port) +{ + return 0; +} + +/* Associate the uart functions above - given to serial core */ + +static struct uart_ops ioc4_ops = { + .tx_empty = ic4_tx_empty, + .set_mctrl = ic4_set_mctrl, + .get_mctrl = ic4_get_mctrl, + .stop_tx = ic4_stop_tx, + .start_tx = ic4_start_tx, + .stop_rx = null_void_function, + .enable_ms = null_void_function, + .break_ctl = ic4_break_ctl, + .startup = ic4_startup, + .shutdown = ic4_shutdown, + .set_termios = ic4_set_termios, + .type = ic4_type, + .release_port = null_void_function, + .request_port = ic4_request_port, +}; + +/* + * Boot-time initialization code + */ + +static struct uart_driver ioc4_uart_rs232 = { + .owner = THIS_MODULE, + .driver_name = "ioc4_serial_rs232", + .dev_name = DEVICE_NAME_RS232, + .major = DEVICE_MAJOR, + .minor = DEVICE_MINOR_RS232, + .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS, +}; + +static struct uart_driver ioc4_uart_rs422 = { + .owner = THIS_MODULE, + .driver_name = "ioc4_serial_rs422", + .dev_name = DEVICE_NAME_RS422, + .major = DEVICE_MAJOR, + .minor = DEVICE_MINOR_RS422, + .nr = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS, +}; + + +/** + * ioc4_serial_remove_one - detach function + * + * @idd: IOC4 master module data for this IOC4 + */ + +static int ioc4_serial_remove_one(struct ioc4_driver_data *idd) +{ + int port_num, port_type; + struct ioc4_control *control; + struct uart_port *the_port; + struct ioc4_port *port; + struct ioc4_soft *soft; + + /* If serial driver did not attach, don't try to detach */ + control = idd->idd_serial_data; + if (!control) + return 0; + + for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) { + for (port_type = UART_PORT_MIN; + port_type < UART_PORT_COUNT; + port_type++) { + the_port = &control->ic_port[port_num].icp_uart_port + [port_type]; + if (the_port) { + switch (port_type) { + case UART_PORT_RS422: + uart_remove_one_port(&ioc4_uart_rs422, + the_port); + break; + default: + case UART_PORT_RS232: + uart_remove_one_port(&ioc4_uart_rs232, + the_port); + break; + } + } + } + port = control->ic_port[port_num].icp_port; + /* we allocate in pairs */ + if (!(port_num & 1) && port) { + pci_free_consistent(port->ip_pdev, + TOTAL_RING_BUF_SIZE, + port->ip_cpu_ringbuf, + port->ip_dma_ringbuf); + kfree(port); + } + } + soft = control->ic_soft; + if (soft) { + free_irq(control->ic_irq, soft); + if (soft->is_ioc4_serial_addr) { + iounmap(soft->is_ioc4_serial_addr); + release_mem_region((unsigned long) + soft->is_ioc4_serial_addr, + sizeof(struct ioc4_serial)); + } + kfree(soft); + } + kfree(control); + idd->idd_serial_data = NULL; + + return 0; +} + + +/** + * ioc4_serial_core_attach_rs232 - register with serial core + * This is done during pci probing + * @pdev: handle for this card + */ +static inline int +ioc4_serial_core_attach(struct pci_dev *pdev, int port_type) +{ + struct ioc4_port *port; + struct uart_port *the_port; + struct ioc4_driver_data *idd = pci_get_drvdata(pdev); + struct ioc4_control *control = idd->idd_serial_data; + int port_num; + int port_type_idx; + struct uart_driver *u_driver; + + + DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n", + __func__, pdev, (void *)control)); + + if (!control) + return -ENODEV; + + port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232 + : UART_PORT_RS422; + + u_driver = (port_type == PROTO_RS232) ? &ioc4_uart_rs232 + : &ioc4_uart_rs422; + + /* once around for each port on this card */ + for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) { + the_port = &control->ic_port[port_num].icp_uart_port + [port_type_idx]; + port = control->ic_port[port_num].icp_port; + port->ip_all_ports[port_type_idx] = the_port; + + DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n", + __func__, (void *)the_port, + (void *)port, + port_type == PROTO_RS232 ? "rs232" : "rs422")); + + /* membase, iobase and mapbase just need to be non-0 */ + the_port->membase = (unsigned char __iomem *)1; + the_port->iobase = (pdev->bus->number << 16) | port_num; + the_port->line = (Num_of_ioc4_cards << 2) | port_num; + the_port->mapbase = port_type; + the_port->type = PORT_16550A; + the_port->fifosize = IOC4_FIFO_CHARS; + the_port->ops = &ioc4_ops; + the_port->irq = control->ic_irq; + the_port->dev = &pdev->dev; + spin_lock_init(&the_port->lock); + if (uart_add_one_port(u_driver, the_port) < 0) { + printk(KERN_WARNING + "%s: unable to add port %d bus %d\n", + __func__, the_port->line, pdev->bus->number); + } else { + DPRINT_CONFIG( + ("IOC4 serial port %d irq = %d, bus %d\n", + the_port->line, the_port->irq, pdev->bus->number)); + } + } + return 0; +} + +/** + * ioc4_serial_attach_one - register attach function + * called per card found from IOC4 master module. + * @idd: Master module data for this IOC4 + */ +int +ioc4_serial_attach_one(struct ioc4_driver_data *idd) +{ + unsigned long tmp_addr1; + struct ioc4_serial __iomem *serial; + struct ioc4_soft *soft; + struct ioc4_control *control; + int ret = 0; + + + DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev, + idd->idd_pci_id)); + + /* PCI-RT does not bring out serial connections. + * Do not attach to this particular IOC4. + */ + if (idd->idd_variant == IOC4_VARIANT_PCI_RT) + return 0; + + /* request serial registers */ + tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET; + + if (!request_mem_region(tmp_addr1, sizeof(struct ioc4_serial), + "sioc4_uart")) { + printk(KERN_WARNING + "ioc4 (%p): unable to get request region for " + "uart space\n", (void *)idd->idd_pdev); + ret = -ENODEV; + goto out1; + } + serial = ioremap(tmp_addr1, sizeof(struct ioc4_serial)); + if (!serial) { + printk(KERN_WARNING + "ioc4 (%p) : unable to remap ioc4 serial register\n", + (void *)idd->idd_pdev); + ret = -ENODEV; + goto out2; + } + DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n", + __func__, (void *)idd->idd_misc_regs, + (void *)serial)); + + /* Get memory for the new card */ + control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL); + + if (!control) { + printk(KERN_WARNING "ioc4_attach_one" + ": unable to get memory for the IOC4\n"); + ret = -ENOMEM; + goto out2; + } + idd->idd_serial_data = control; + + /* Allocate the soft structure */ + soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL); + if (!soft) { + printk(KERN_WARNING + "ioc4 (%p): unable to get memory for the soft struct\n", + (void *)idd->idd_pdev); + ret = -ENOMEM; + goto out3; + } + + spin_lock_init(&soft->is_ir_lock); + soft->is_ioc4_misc_addr = idd->idd_misc_regs; + soft->is_ioc4_serial_addr = serial; + + /* Init the IOC4 */ + writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT, + &idd->idd_misc_regs->sio_cr.raw); + + /* Enable serial port mode select generic PIO pins as outputs */ + writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL + | IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL, + &idd->idd_misc_regs->gpcr_s.raw); + + /* Clear and disable all serial interrupts */ + write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE); + writel(~0, &idd->idd_misc_regs->sio_ir.raw); + write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC, + IOC4_OTHER_INTR_TYPE); + writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw); + control->ic_soft = soft; + + /* Hook up interrupt handler */ + if (!request_irq(idd->idd_pdev->irq, ioc4_intr, IRQF_SHARED, + "sgi-ioc4serial", soft)) { + control->ic_irq = idd->idd_pdev->irq; + } else { + printk(KERN_WARNING + "%s : request_irq fails for IRQ 0x%x\n ", + __func__, idd->idd_pdev->irq); + } + ret = ioc4_attach_local(idd); + if (ret) + goto out4; + + /* register port with the serial core - 1 rs232, 1 rs422 */ + + if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232))) + goto out4; + + if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422))) + goto out5; + + Num_of_ioc4_cards++; + + return ret; + + /* error exits that give back resources */ +out5: + ioc4_serial_remove_one(idd); +out4: + kfree(soft); +out3: + kfree(control); +out2: + if (serial) + iounmap(serial); + release_mem_region(tmp_addr1, sizeof(struct ioc4_serial)); +out1: + + return ret; +} + + +static struct ioc4_submodule ioc4_serial_submodule = { + .is_name = "IOC4_serial", + .is_owner = THIS_MODULE, + .is_probe = ioc4_serial_attach_one, + .is_remove = ioc4_serial_remove_one, +}; + +/** + * ioc4_serial_init - module init + */ +static int __init ioc4_serial_init(void) +{ + int ret; + + /* register with serial core */ + if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) { + printk(KERN_WARNING + "%s: Couldn't register rs232 IOC4 serial driver\n", + __func__); + goto out; + } + if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) { + printk(KERN_WARNING + "%s: Couldn't register rs422 IOC4 serial driver\n", + __func__); + goto out_uart_rs232; + } + + /* register with IOC4 main module */ + ret = ioc4_register_submodule(&ioc4_serial_submodule); + if (ret) + goto out_uart_rs422; + return 0; + +out_uart_rs422: + uart_unregister_driver(&ioc4_uart_rs422); +out_uart_rs232: + uart_unregister_driver(&ioc4_uart_rs232); +out: + return ret; +} + +static void __exit ioc4_serial_exit(void) +{ + ioc4_unregister_submodule(&ioc4_serial_submodule); + uart_unregister_driver(&ioc4_uart_rs232); + uart_unregister_driver(&ioc4_uart_rs422); +} + +late_initcall(ioc4_serial_init); /* Call only after tty init is done */ +module_exit(ioc4_serial_exit); + +MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) "); +MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/ip22zilog.c b/drivers/tty/serial/ip22zilog.c new file mode 100644 index 000000000000..ebff4a1d4bcc --- /dev/null +++ b/drivers/tty/serial/ip22zilog.c @@ -0,0 +1,1221 @@ +/* + * Driver for Zilog serial chips found on SGI workstations and + * servers. This driver could actually be made more generic. + * + * This is based on the drivers/serial/sunzilog.c code as of 2.6.0-test7 and the + * old drivers/sgi/char/sgiserial.c code which itself is based of the original + * drivers/sbus/char/zs.c code. A lot of code has been simply moved over + * directly from there but much has been rewritten. Credits therefore go out + * to David S. Miller, Eddie C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell + * for their work there. + * + * Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org) + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SERIAL_IP22_ZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include "ip22zilog.h" + +/* + * On IP22 we need to delay after register accesses but we do not need to + * flush writes. + */ +#define ZSDELAY() udelay(5) +#define ZSDELAY_LONG() udelay(20) +#define ZS_WSYNC(channel) do { } while (0) + +#define NUM_IP22ZILOG 1 +#define NUM_CHANNELS (NUM_IP22ZILOG * 2) + +#define ZS_CLOCK 3672000 /* Zilog input clock rate. */ +#define ZS_CLOCK_DIVISOR 16 /* Divisor this driver uses. */ + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_ip22zilog_port { + struct uart_port port; + + /* IRQ servicing chain. */ + struct uart_ip22zilog_port *next; + + /* Current values of Zilog write registers. */ + unsigned char curregs[NUM_ZSREGS]; + + unsigned int flags; +#define IP22ZILOG_FLAG_IS_CONS 0x00000004 +#define IP22ZILOG_FLAG_IS_KGDB 0x00000008 +#define IP22ZILOG_FLAG_MODEM_STATUS 0x00000010 +#define IP22ZILOG_FLAG_IS_CHANNEL_A 0x00000020 +#define IP22ZILOG_FLAG_REGS_HELD 0x00000040 +#define IP22ZILOG_FLAG_TX_STOPPED 0x00000080 +#define IP22ZILOG_FLAG_TX_ACTIVE 0x00000100 +#define IP22ZILOG_FLAG_RESET_DONE 0x00000200 + + unsigned int tty_break; + + unsigned char parity_mask; + unsigned char prev_status; +}; + +#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel *)((PORT)->membase)) +#define UART_ZILOG(PORT) ((struct uart_ip22zilog_port *)(PORT)) +#define IP22ZILOG_GET_CURR_REG(PORT, REGNUM) \ + (UART_ZILOG(PORT)->curregs[REGNUM]) +#define IP22ZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL) \ + ((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL)) +#define ZS_IS_CONS(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_CONS) +#define ZS_IS_KGDB(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_KGDB) +#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & IP22ZILOG_FLAG_MODEM_STATUS) +#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & IP22ZILOG_FLAG_IS_CHANNEL_A) +#define ZS_REGS_HELD(UP) ((UP)->flags & IP22ZILOG_FLAG_REGS_HELD) +#define ZS_TX_STOPPED(UP) ((UP)->flags & IP22ZILOG_FLAG_TX_STOPPED) +#define ZS_TX_ACTIVE(UP) ((UP)->flags & IP22ZILOG_FLAG_TX_ACTIVE) + +/* Reading and writing Zilog8530 registers. The delays are to make this + * driver work on the IP22 which needs a settling delay after each chip + * register access, other machines handle this in hardware via auxiliary + * flip-flops which implement the settle time we do in software. + * + * The port lock must be held and local IRQs must be disabled + * when {read,write}_zsreg is invoked. + */ +static unsigned char read_zsreg(struct zilog_channel *channel, + unsigned char reg) +{ + unsigned char retval; + + writeb(reg, &channel->control); + ZSDELAY(); + retval = readb(&channel->control); + ZSDELAY(); + + return retval; +} + +static void write_zsreg(struct zilog_channel *channel, + unsigned char reg, unsigned char value) +{ + writeb(reg, &channel->control); + ZSDELAY(); + writeb(value, &channel->control); + ZSDELAY(); +} + +static void ip22zilog_clear_fifo(struct zilog_channel *channel) +{ + int i; + + for (i = 0; i < 32; i++) { + unsigned char regval; + + regval = readb(&channel->control); + ZSDELAY(); + if (regval & Rx_CH_AV) + break; + + regval = read_zsreg(channel, R1); + readb(&channel->data); + ZSDELAY(); + + if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + } +} + +/* This function must only be called when the TX is not busy. The UART + * port lock must be held and local interrupts disabled. + */ +static void __load_zsregs(struct zilog_channel *channel, unsigned char *regs) +{ + int i; + + /* Let pending transmits finish. */ + for (i = 0; i < 1000; i++) { + unsigned char stat = read_zsreg(channel, R1); + if (stat & ALL_SNT) + break; + udelay(100); + } + + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + ip22zilog_clear_fifo(channel); + + /* Disable all interrupts. */ + write_zsreg(channel, R1, + regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); + + /* Set parity, sync config, stop bits, and clock divisor. */ + write_zsreg(channel, R4, regs[R4]); + + /* Set misc. TX/RX control bits. */ + write_zsreg(channel, R10, regs[R10]); + + /* Set TX/RX controls sans the enable bits. */ + write_zsreg(channel, R3, regs[R3] & ~RxENAB); + write_zsreg(channel, R5, regs[R5] & ~TxENAB); + + /* Synchronous mode config. */ + write_zsreg(channel, R6, regs[R6]); + write_zsreg(channel, R7, regs[R7]); + + /* Don't mess with the interrupt vector (R2, unused by us) and + * master interrupt control (R9). We make sure this is setup + * properly at probe time then never touch it again. + */ + + /* Disable baud generator. */ + write_zsreg(channel, R14, regs[R14] & ~BRENAB); + + /* Clock mode control. */ + write_zsreg(channel, R11, regs[R11]); + + /* Lower and upper byte of baud rate generator divisor. */ + write_zsreg(channel, R12, regs[R12]); + write_zsreg(channel, R13, regs[R13]); + + /* Now rewrite R14, with BRENAB (if set). */ + write_zsreg(channel, R14, regs[R14]); + + /* External status interrupt control. */ + write_zsreg(channel, R15, regs[R15]); + + /* Reset external status interrupts. */ + write_zsreg(channel, R0, RES_EXT_INT); + write_zsreg(channel, R0, RES_EXT_INT); + + /* Rewrite R3/R5, this time without enables masked. */ + write_zsreg(channel, R3, regs[R3]); + write_zsreg(channel, R5, regs[R5]); + + /* Rewrite R1, this time without IRQ enabled masked. */ + write_zsreg(channel, R1, regs[R1]); +} + +/* Reprogram the Zilog channel HW registers with the copies found in the + * software state struct. If the transmitter is busy, we defer this update + * until the next TX complete interrupt. Else, we do it right now. + * + * The UART port lock must be held and local interrupts disabled. + */ +static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up, + struct zilog_channel *channel) +{ + if (!ZS_REGS_HELD(up)) { + if (ZS_TX_ACTIVE(up)) { + up->flags |= IP22ZILOG_FLAG_REGS_HELD; + } else { + __load_zsregs(channel, up->curregs); + } + } +} + +#define Rx_BRK 0x0100 /* BREAK event software flag. */ +#define Rx_SYS 0x0200 /* SysRq event software flag. */ + +static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up, + struct zilog_channel *channel) +{ + struct tty_struct *tty; + unsigned char ch, flag; + unsigned int r1; + + tty = NULL; + if (up->port.state != NULL && + up->port.state->port.tty != NULL) + tty = up->port.state->port.tty; + + for (;;) { + ch = readb(&channel->control); + ZSDELAY(); + if (!(ch & Rx_CH_AV)) + break; + + r1 = read_zsreg(channel, R1); + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + + ch = readb(&channel->data); + ZSDELAY(); + + ch &= up->parity_mask; + + /* Handle the null char got when BREAK is removed. */ + if (!ch) + r1 |= up->tty_break; + + /* A real serial line, record the character and status. */ + flag = TTY_NORMAL; + up->port.icount.rx++; + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) { + up->tty_break = 0; + + if (r1 & (Rx_SYS | Rx_BRK)) { + up->port.icount.brk++; + if (r1 & Rx_SYS) + continue; + r1 &= ~(PAR_ERR | CRC_ERR); + } + else if (r1 & PAR_ERR) + up->port.icount.parity++; + else if (r1 & CRC_ERR) + up->port.icount.frame++; + if (r1 & Rx_OVR) + up->port.icount.overrun++; + r1 &= up->port.read_status_mask; + if (r1 & Rx_BRK) + flag = TTY_BREAK; + else if (r1 & PAR_ERR) + flag = TTY_PARITY; + else if (r1 & CRC_ERR) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + continue; + + if (tty) + uart_insert_char(&up->port, r1, Rx_OVR, ch, flag); + } + return tty; +} + +static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, + struct zilog_channel *channel) +{ + unsigned char status; + + status = readb(&channel->control); + ZSDELAY(); + + writeb(RES_EXT_INT, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + if (up->curregs[R15] & BRKIE) { + if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) { + if (uart_handle_break(&up->port)) + up->tty_break = Rx_SYS; + else + up->tty_break = Rx_BRK; + } + } + + if (ZS_WANTS_MODEM_STATUS(up)) { + if (status & SYNC) + up->port.icount.dsr++; + + /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. + * But it does not tell us which bit has changed, we have to keep + * track of this ourselves. + */ + if ((status ^ up->prev_status) ^ DCD) + uart_handle_dcd_change(&up->port, + (status & DCD)); + if ((status ^ up->prev_status) ^ CTS) + uart_handle_cts_change(&up->port, + (status & CTS)); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + up->prev_status = status; +} + +static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, + struct zilog_channel *channel) +{ + struct circ_buf *xmit; + + if (ZS_IS_CONS(up)) { + unsigned char status = readb(&channel->control); + ZSDELAY(); + + /* TX still busy? Just wait for the next TX done interrupt. + * + * It can occur because of how we do serial console writes. It would + * be nice to transmit console writes just like we normally would for + * a TTY line. (ie. buffered and TX interrupt driven). That is not + * easy because console writes cannot sleep. One solution might be + * to poll on enough port->xmit space becomming free. -DaveM + */ + if (!(status & Tx_BUF_EMP)) + return; + } + + up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE; + + if (ZS_REGS_HELD(up)) { + __load_zsregs(channel, up->curregs); + up->flags &= ~IP22ZILOG_FLAG_REGS_HELD; + } + + if (ZS_TX_STOPPED(up)) { + up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED; + goto ack_tx_int; + } + + if (up->port.x_char) { + up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; + writeb(up->port.x_char, &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + + if (up->port.state == NULL) + goto ack_tx_int; + xmit = &up->port.state->xmit; + if (uart_circ_empty(xmit)) + goto ack_tx_int; + if (uart_tx_stopped(&up->port)) + goto ack_tx_int; + + up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; + writeb(xmit->buf[xmit->tail], &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + return; + +ack_tx_int: + writeb(RES_Tx_P, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); +} + +static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id) +{ + struct uart_ip22zilog_port *up = dev_id; + + while (up) { + struct zilog_channel *channel + = ZILOG_CHANNEL_FROM_PORT(&up->port); + struct tty_struct *tty; + unsigned char r3; + + spin_lock(&up->port.lock); + r3 = read_zsreg(channel, R3); + + /* Channel A */ + tty = NULL; + if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { + writeb(RES_H_IUS, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + if (r3 & CHARxIP) + tty = ip22zilog_receive_chars(up, channel); + if (r3 & CHAEXT) + ip22zilog_status_handle(up, channel); + if (r3 & CHATxIP) + ip22zilog_transmit_chars(up, channel); + } + spin_unlock(&up->port.lock); + + if (tty) + tty_flip_buffer_push(tty); + + /* Channel B */ + up = up->next; + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + + spin_lock(&up->port.lock); + tty = NULL; + if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { + writeb(RES_H_IUS, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + if (r3 & CHBRxIP) + tty = ip22zilog_receive_chars(up, channel); + if (r3 & CHBEXT) + ip22zilog_status_handle(up, channel); + if (r3 & CHBTxIP) + ip22zilog_transmit_chars(up, channel); + } + spin_unlock(&up->port.lock); + + if (tty) + tty_flip_buffer_push(tty); + + up = up->next; + } + + return IRQ_HANDLED; +} + +/* A convenient way to quickly get R0 status. The caller must _not_ hold the + * port lock, it is acquired here. + */ +static __inline__ unsigned char ip22zilog_read_channel_status(struct uart_port *port) +{ + struct zilog_channel *channel; + unsigned char status; + + channel = ZILOG_CHANNEL_FROM_PORT(port); + status = readb(&channel->control); + ZSDELAY(); + + return status; +} + +/* The port lock is not held. */ +static unsigned int ip22zilog_tx_empty(struct uart_port *port) +{ + unsigned long flags; + unsigned char status; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + + status = ip22zilog_read_channel_status(port); + + spin_unlock_irqrestore(&port->lock, flags); + + if (status & Tx_BUF_EMP) + ret = TIOCSER_TEMT; + else + ret = 0; + + return ret; +} + +/* The port lock is held and interrupts are disabled. */ +static unsigned int ip22zilog_get_mctrl(struct uart_port *port) +{ + unsigned char status; + unsigned int ret; + + status = ip22zilog_read_channel_status(port); + + ret = 0; + if (status & DCD) + ret |= TIOCM_CAR; + if (status & SYNC) + ret |= TIOCM_DSR; + if (status & CTS) + ret |= TIOCM_CTS; + + return ret; +} + +/* The port lock is held and interrupts are disabled. */ +static void ip22zilog_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char set_bits, clear_bits; + + set_bits = clear_bits = 0; + + if (mctrl & TIOCM_RTS) + set_bits |= RTS; + else + clear_bits |= RTS; + if (mctrl & TIOCM_DTR) + set_bits |= DTR; + else + clear_bits |= DTR; + + /* NOTE: Not subject to 'transmitter active' rule. */ + up->curregs[R5] |= set_bits; + up->curregs[R5] &= ~clear_bits; + write_zsreg(channel, R5, up->curregs[R5]); +} + +/* The port lock is held and interrupts are disabled. */ +static void ip22zilog_stop_tx(struct uart_port *port) +{ + struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + + up->flags |= IP22ZILOG_FLAG_TX_STOPPED; +} + +/* The port lock is held and interrupts are disabled. */ +static void ip22zilog_start_tx(struct uart_port *port) +{ + struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char status; + + up->flags |= IP22ZILOG_FLAG_TX_ACTIVE; + up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED; + + status = readb(&channel->control); + ZSDELAY(); + + /* TX busy? Just wait for the TX done interrupt. */ + if (!(status & Tx_BUF_EMP)) + return; + + /* Send the first character to jump-start the TX done + * IRQ sending engine. + */ + if (port->x_char) { + writeb(port->x_char, &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + port->icount.tx++; + port->x_char = 0; + } else { + struct circ_buf *xmit = &port->state->xmit; + + writeb(xmit->buf[xmit->tail], &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + } +} + +/* The port lock is held and interrupts are disabled. */ +static void ip22zilog_stop_rx(struct uart_port *port) +{ + struct uart_ip22zilog_port *up = UART_ZILOG(port); + struct zilog_channel *channel; + + if (ZS_IS_CONS(up)) + return; + + channel = ZILOG_CHANNEL_FROM_PORT(port); + + /* Disable all RX interrupts. */ + up->curregs[R1] &= ~RxINT_MASK; + ip22zilog_maybe_update_regs(up, channel); +} + +/* The port lock is held. */ +static void ip22zilog_enable_ms(struct uart_port *port) +{ + struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char new_reg; + + new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE); + if (new_reg != up->curregs[R15]) { + up->curregs[R15] = new_reg; + + /* NOTE: Not subject to 'transmitter active' rule. */ + write_zsreg(channel, R15, up->curregs[R15]); + } +} + +/* The port lock is not held. */ +static void ip22zilog_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char set_bits, clear_bits, new_reg; + unsigned long flags; + + set_bits = clear_bits = 0; + + if (break_state) + set_bits |= SND_BRK; + else + clear_bits |= SND_BRK; + + spin_lock_irqsave(&port->lock, flags); + + new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; + if (new_reg != up->curregs[R5]) { + up->curregs[R5] = new_reg; + + /* NOTE: Not subject to 'transmitter active' rule. */ + write_zsreg(channel, R5, up->curregs[R5]); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __ip22zilog_reset(struct uart_ip22zilog_port *up) +{ + struct zilog_channel *channel; + int i; + + if (up->flags & IP22ZILOG_FLAG_RESET_DONE) + return; + + /* Let pending transmits finish. */ + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + for (i = 0; i < 1000; i++) { + unsigned char stat = read_zsreg(channel, R1); + if (stat & ALL_SNT) + break; + udelay(100); + } + + if (!ZS_IS_CHANNEL_A(up)) { + up++; + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + } + write_zsreg(channel, R9, FHWRES); + ZSDELAY_LONG(); + (void) read_zsreg(channel, R0); + + up->flags |= IP22ZILOG_FLAG_RESET_DONE; + up->next->flags |= IP22ZILOG_FLAG_RESET_DONE; +} + +static void __ip22zilog_startup(struct uart_ip22zilog_port *up) +{ + struct zilog_channel *channel; + + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + + __ip22zilog_reset(up); + + __load_zsregs(channel, up->curregs); + /* set master interrupt enable */ + write_zsreg(channel, R9, up->curregs[R9]); + up->prev_status = readb(&channel->control); + + /* Enable receiver and transmitter. */ + up->curregs[R3] |= RxENAB; + up->curregs[R5] |= TxENAB; + + up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; + ip22zilog_maybe_update_regs(up, channel); +} + +static int ip22zilog_startup(struct uart_port *port) +{ + struct uart_ip22zilog_port *up = UART_ZILOG(port); + unsigned long flags; + + if (ZS_IS_CONS(up)) + return 0; + + spin_lock_irqsave(&port->lock, flags); + __ip22zilog_startup(up); + spin_unlock_irqrestore(&port->lock, flags); + return 0; +} + +/* + * The test for ZS_IS_CONS is explained by the following e-mail: + ***** + * From: Russell King + * Date: Sun, 8 Dec 2002 10:18:38 +0000 + * + * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: + * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, + * > and I noticed that something is not right with reference + * > counting in this case. It seems that when the console + * > is open by kernel initially, this is not accounted + * > as an open, and uart_startup is not called. + * + * That is correct. We are unable to call uart_startup when the serial + * console is initialised because it may need to allocate memory (as + * request_irq does) and the memory allocators may not have been + * initialised. + * + * 1. initialise the port into a state where it can send characters in the + * console write method. + * + * 2. don't do the actual hardware shutdown in your shutdown() method (but + * do the normal software shutdown - ie, free irqs etc) + ***** + */ +static void ip22zilog_shutdown(struct uart_port *port) +{ + struct uart_ip22zilog_port *up = UART_ZILOG(port); + struct zilog_channel *channel; + unsigned long flags; + + if (ZS_IS_CONS(up)) + return; + + spin_lock_irqsave(&port->lock, flags); + + channel = ZILOG_CHANNEL_FROM_PORT(port); + + /* Disable receiver and transmitter. */ + up->curregs[R3] &= ~RxENAB; + up->curregs[R5] &= ~TxENAB; + + /* Disable all interrupts and BRK assertion. */ + up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); + up->curregs[R5] &= ~SND_BRK; + ip22zilog_maybe_update_regs(up, channel); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Shared by TTY driver and serial console setup. The port lock is held + * and local interrupts are disabled. + */ +static void +ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag, + unsigned int iflag, int brg) +{ + + up->curregs[R10] = NRZ; + up->curregs[R11] = TCBR | RCBR; + + /* Program BAUD and clock source. */ + up->curregs[R4] &= ~XCLK_MASK; + up->curregs[R4] |= X16CLK; + up->curregs[R12] = brg & 0xff; + up->curregs[R13] = (brg >> 8) & 0xff; + up->curregs[R14] = BRENAB; + + /* Character size, stop bits, and parity. */ + up->curregs[3] &= ~RxN_MASK; + up->curregs[5] &= ~TxN_MASK; + switch (cflag & CSIZE) { + case CS5: + up->curregs[3] |= Rx5; + up->curregs[5] |= Tx5; + up->parity_mask = 0x1f; + break; + case CS6: + up->curregs[3] |= Rx6; + up->curregs[5] |= Tx6; + up->parity_mask = 0x3f; + break; + case CS7: + up->curregs[3] |= Rx7; + up->curregs[5] |= Tx7; + up->parity_mask = 0x7f; + break; + case CS8: + default: + up->curregs[3] |= Rx8; + up->curregs[5] |= Tx8; + up->parity_mask = 0xff; + break; + }; + up->curregs[4] &= ~0x0c; + if (cflag & CSTOPB) + up->curregs[4] |= SB2; + else + up->curregs[4] |= SB1; + if (cflag & PARENB) + up->curregs[4] |= PAR_ENAB; + else + up->curregs[4] &= ~PAR_ENAB; + if (!(cflag & PARODD)) + up->curregs[4] |= PAR_EVEN; + else + up->curregs[4] &= ~PAR_EVEN; + + up->port.read_status_mask = Rx_OVR; + if (iflag & INPCK) + up->port.read_status_mask |= CRC_ERR | PAR_ERR; + if (iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= BRK_ABRT; + + up->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= CRC_ERR | PAR_ERR; + if (iflag & IGNBRK) { + up->port.ignore_status_mask |= BRK_ABRT; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= Rx_OVR; + } + + if ((cflag & CREAD) == 0) + up->port.ignore_status_mask = 0xff; +} + +/* The port lock is not held. */ +static void +ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port; + unsigned long flags; + int baud, brg; + + baud = uart_get_baud_rate(port, termios, old, 1200, 76800); + + spin_lock_irqsave(&up->port.lock, flags); + + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + + ip22zilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg); + + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->flags |= IP22ZILOG_FLAG_MODEM_STATUS; + else + up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS; + + ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static const char *ip22zilog_type(struct uart_port *port) +{ + return "IP22-Zilog"; +} + +/* We do not request/release mappings of the registers here, this + * happens at early serial probe time. + */ +static void ip22zilog_release_port(struct uart_port *port) +{ +} + +static int ip22zilog_request_port(struct uart_port *port) +{ + return 0; +} + +/* These do not need to do anything interesting either. */ +static void ip22zilog_config_port(struct uart_port *port, int flags) +{ +} + +/* We do not support letting the user mess with the divisor, IRQ, etc. */ +static int ip22zilog_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static struct uart_ops ip22zilog_pops = { + .tx_empty = ip22zilog_tx_empty, + .set_mctrl = ip22zilog_set_mctrl, + .get_mctrl = ip22zilog_get_mctrl, + .stop_tx = ip22zilog_stop_tx, + .start_tx = ip22zilog_start_tx, + .stop_rx = ip22zilog_stop_rx, + .enable_ms = ip22zilog_enable_ms, + .break_ctl = ip22zilog_break_ctl, + .startup = ip22zilog_startup, + .shutdown = ip22zilog_shutdown, + .set_termios = ip22zilog_set_termios, + .type = ip22zilog_type, + .release_port = ip22zilog_release_port, + .request_port = ip22zilog_request_port, + .config_port = ip22zilog_config_port, + .verify_port = ip22zilog_verify_port, +}; + +static struct uart_ip22zilog_port *ip22zilog_port_table; +static struct zilog_layout **ip22zilog_chip_regs; + +static struct uart_ip22zilog_port *ip22zilog_irq_chain; +static int zilog_irq = -1; + +static void * __init alloc_one_table(unsigned long size) +{ + return kzalloc(size, GFP_KERNEL); +} + +static void __init ip22zilog_alloc_tables(void) +{ + ip22zilog_port_table = (struct uart_ip22zilog_port *) + alloc_one_table(NUM_CHANNELS * sizeof(struct uart_ip22zilog_port)); + ip22zilog_chip_regs = (struct zilog_layout **) + alloc_one_table(NUM_IP22ZILOG * sizeof(struct zilog_layout *)); + + if (ip22zilog_port_table == NULL || ip22zilog_chip_regs == NULL) { + panic("IP22-Zilog: Cannot allocate IP22-Zilog tables."); + } +} + +/* Get the address of the registers for IP22-Zilog instance CHIP. */ +static struct zilog_layout * __init get_zs(int chip) +{ + unsigned long base; + + if (chip < 0 || chip >= NUM_IP22ZILOG) { + panic("IP22-Zilog: Illegal chip number %d in get_zs.", chip); + } + + /* Not probe-able, hard code it. */ + base = (unsigned long) &sgioc->uart; + + zilog_irq = SGI_SERIAL_IRQ; + request_mem_region(base, 8, "IP22-Zilog"); + + return (struct zilog_layout *) base; +} + +#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */ + +#ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE +static void ip22zilog_put_char(struct uart_port *port, int ch) +{ + struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port); + int loops = ZS_PUT_CHAR_MAX_DELAY; + + /* This is a timed polling loop so do not switch the explicit + * udelay with ZSDELAY as that is a NOP on some platforms. -DaveM + */ + do { + unsigned char val = readb(&channel->control); + if (val & Tx_BUF_EMP) { + ZSDELAY(); + break; + } + udelay(5); + } while (--loops); + + writeb(ch, &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); +} + +static void +ip22zilog_console_write(struct console *con, const char *s, unsigned int count) +{ + struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + uart_console_write(&up->port, s, count, ip22zilog_put_char); + udelay(2); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int __init ip22zilog_console_setup(struct console *con, char *options) +{ + struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; + unsigned long flags; + int baud = 9600, bits = 8; + int parity = 'n'; + int flow = 'n'; + + up->flags |= IP22ZILOG_FLAG_IS_CONS; + + printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index); + + spin_lock_irqsave(&up->port.lock, flags); + + up->curregs[R15] |= BRKIE; + + __ip22zilog_startup(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(&up->port, con, baud, parity, bits, flow); +} + +static struct uart_driver ip22zilog_reg; + +static struct console ip22zilog_console = { + .name = "ttyS", + .write = ip22zilog_console_write, + .device = uart_console_device, + .setup = ip22zilog_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &ip22zilog_reg, +}; +#endif /* CONFIG_SERIAL_IP22_ZILOG_CONSOLE */ + +static struct uart_driver ip22zilog_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = NUM_CHANNELS, +#ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE + .cons = &ip22zilog_console, +#endif +}; + +static void __init ip22zilog_prepare(void) +{ + struct uart_ip22zilog_port *up; + struct zilog_layout *rp; + int channel, chip; + + /* + * Temporary fix. + */ + for (channel = 0; channel < NUM_CHANNELS; channel++) + spin_lock_init(&ip22zilog_port_table[channel].port.lock); + + ip22zilog_irq_chain = &ip22zilog_port_table[NUM_CHANNELS - 1]; + up = &ip22zilog_port_table[0]; + for (channel = NUM_CHANNELS - 1 ; channel > 0; channel--) + up[channel].next = &up[channel - 1]; + up[channel].next = NULL; + + for (chip = 0; chip < NUM_IP22ZILOG; chip++) { + if (!ip22zilog_chip_regs[chip]) { + ip22zilog_chip_regs[chip] = rp = get_zs(chip); + + up[(chip * 2) + 0].port.membase = (char *) &rp->channelB; + up[(chip * 2) + 1].port.membase = (char *) &rp->channelA; + + /* In theory mapbase is the physical address ... */ + up[(chip * 2) + 0].port.mapbase = + (unsigned long) ioremap((unsigned long) &rp->channelB, 8); + up[(chip * 2) + 1].port.mapbase = + (unsigned long) ioremap((unsigned long) &rp->channelA, 8); + } + + /* Channel A */ + up[(chip * 2) + 0].port.iotype = UPIO_MEM; + up[(chip * 2) + 0].port.irq = zilog_irq; + up[(chip * 2) + 0].port.uartclk = ZS_CLOCK; + up[(chip * 2) + 0].port.fifosize = 1; + up[(chip * 2) + 0].port.ops = &ip22zilog_pops; + up[(chip * 2) + 0].port.type = PORT_IP22ZILOG; + up[(chip * 2) + 0].port.flags = 0; + up[(chip * 2) + 0].port.line = (chip * 2) + 0; + up[(chip * 2) + 0].flags = 0; + + /* Channel B */ + up[(chip * 2) + 1].port.iotype = UPIO_MEM; + up[(chip * 2) + 1].port.irq = zilog_irq; + up[(chip * 2) + 1].port.uartclk = ZS_CLOCK; + up[(chip * 2) + 1].port.fifosize = 1; + up[(chip * 2) + 1].port.ops = &ip22zilog_pops; + up[(chip * 2) + 1].port.type = PORT_IP22ZILOG; + up[(chip * 2) + 1].port.line = (chip * 2) + 1; + up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A; + } + + for (channel = 0; channel < NUM_CHANNELS; channel++) { + struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel]; + int brg; + + /* Normal serial TTY. */ + up->parity_mask = 0xff; + up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; + up->curregs[R4] = PAR_EVEN | X16CLK | SB1; + up->curregs[R3] = RxENAB | Rx8; + up->curregs[R5] = TxENAB | Tx8; + up->curregs[R9] = NV | MIE; + up->curregs[R10] = NRZ; + up->curregs[R11] = TCBR | RCBR; + brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR); + up->curregs[R12] = (brg & 0xff); + up->curregs[R13] = (brg >> 8) & 0xff; + up->curregs[R14] = BRENAB; + } +} + +static int __init ip22zilog_ports_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: IP22 Zilog driver (%d chips).\n", NUM_IP22ZILOG); + + ip22zilog_prepare(); + + if (request_irq(zilog_irq, ip22zilog_interrupt, 0, + "IP22-Zilog", ip22zilog_irq_chain)) { + panic("IP22-Zilog: Unable to register zs interrupt handler.\n"); + } + + ret = uart_register_driver(&ip22zilog_reg); + if (ret == 0) { + int i; + + for (i = 0; i < NUM_CHANNELS; i++) { + struct uart_ip22zilog_port *up = &ip22zilog_port_table[i]; + + uart_add_one_port(&ip22zilog_reg, &up->port); + } + } + + return ret; +} + +static int __init ip22zilog_init(void) +{ + /* IP22 Zilog setup is hard coded, no probing to do. */ + ip22zilog_alloc_tables(); + ip22zilog_ports_init(); + + return 0; +} + +static void __exit ip22zilog_exit(void) +{ + int i; + struct uart_ip22zilog_port *up; + + for (i = 0; i < NUM_CHANNELS; i++) { + up = &ip22zilog_port_table[i]; + + uart_remove_one_port(&ip22zilog_reg, &up->port); + } + + /* Free IO mem */ + up = &ip22zilog_port_table[0]; + for (i = 0; i < NUM_IP22ZILOG; i++) { + if (up[(i * 2) + 0].port.mapbase) { + iounmap((void*)up[(i * 2) + 0].port.mapbase); + up[(i * 2) + 0].port.mapbase = 0; + } + if (up[(i * 2) + 1].port.mapbase) { + iounmap((void*)up[(i * 2) + 1].port.mapbase); + up[(i * 2) + 1].port.mapbase = 0; + } + } + + uart_unregister_driver(&ip22zilog_reg); +} + +module_init(ip22zilog_init); +module_exit(ip22zilog_exit); + +/* David wrote it but I'm to blame for the bugs ... */ +MODULE_AUTHOR("Ralf Baechle "); +MODULE_DESCRIPTION("SGI Zilog serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/ip22zilog.h b/drivers/tty/serial/ip22zilog.h new file mode 100644 index 000000000000..a59a9a8341d2 --- /dev/null +++ b/drivers/tty/serial/ip22zilog.h @@ -0,0 +1,281 @@ +#ifndef _IP22_ZILOG_H +#define _IP22_ZILOG_H + +#include + +struct zilog_channel { +#ifdef __BIG_ENDIAN + volatile unsigned char unused0[3]; + volatile unsigned char control; + volatile unsigned char unused1[3]; + volatile unsigned char data; +#else /* __LITTLE_ENDIAN */ + volatile unsigned char control; + volatile unsigned char unused0[3]; + volatile unsigned char data; + volatile unsigned char unused1[3]; +#endif +}; + +struct zilog_layout { + struct zilog_channel channelB; + struct zilog_channel channelA; +}; + +#define NUM_ZSREGS 16 + +/* Conversion routines to/from brg time constants from/to bits + * per second. + */ +#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) +#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) + +/* The Zilog register set */ + +#define FLAG 0x7e + +/* Write Register 0 */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 */ + +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ +#define INT_ERR_Rx 0x18 /* Int on error only */ +#define RxINT_MASK 0x18 + +#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ +#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ +#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ + +/* Write Register #2 (Interrupt Vector) */ + +/* Write Register 3 */ + +#define RxENAB 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxN_MASK 0xc0 + +/* Write Register 4 */ + +#define PAR_ENAB 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xC0 /* x64 clock mode */ +#define XCLK_MASK 0xC0 + +/* Write Register 5 */ + +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENAB 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define TxN_MASK 0x60 +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 8 (transmit buffer) */ + +/* Write Register 9 (Master interrupt control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (misc control bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (lower byte of baud rate generator time constant) */ + +/* Write Register 13 (upper byte of baud rate generator time constant) */ + +/* Write Register 14 (Misc control bits) */ +#define BRENAB 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (external/status interrupt control) */ +#define ZCIE 2 /* Zero count IE */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define CRC_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x0e + +/* Read Register 3 (interrupt pending register) ch a only */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 8 (receive data register) */ + +/* Read Register 10 (misc status bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (lower byte of baud rate generator constant) */ + +/* Read Register 13 (upper byte of baud rate generator constant) */ + +/* Read Register 15 (value of WR 15) */ + +/* Misc macros */ +#define ZS_CLEARERR(channel) do { writeb(ERR_RES, &channel->control); \ + udelay(5); } while(0) + +#define ZS_CLEARSTAT(channel) do { writeb(RES_EXT_INT, &channel->control); \ + udelay(5); } while(0) + +#define ZS_CLEARFIFO(channel) do { readb(&channel->data); \ + udelay(2); \ + readb(&channel->data); \ + udelay(2); \ + readb(&channel->data); \ + udelay(2); } while(0) + +#endif /* _IP22_ZILOG_H */ diff --git a/drivers/tty/serial/jsm/Makefile b/drivers/tty/serial/jsm/Makefile new file mode 100644 index 000000000000..e46b6e0f8b18 --- /dev/null +++ b/drivers/tty/serial/jsm/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for Jasmine adapter +# + +obj-$(CONFIG_SERIAL_JSM) += jsm.o + +jsm-objs := jsm_driver.o jsm_neo.o jsm_tty.o + diff --git a/drivers/tty/serial/jsm/jsm.h b/drivers/tty/serial/jsm/jsm.h new file mode 100644 index 000000000000..38a509c684cd --- /dev/null +++ b/drivers/tty/serial/jsm/jsm.h @@ -0,0 +1,388 @@ +/************************************************************************ + * Copyright 2003 Digi International (www.digi.com) + * + * Copyright (C) 2004 IBM Corporation. All rights reserved. + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * Contact Information: + * Scott H Kilau + * Wendy Xiong + * + ***********************************************************************/ + +#ifndef __JSM_DRIVER_H +#define __JSM_DRIVER_H + +#include +#include /* To pick up the varions Linux types */ +#include +#include +#include + +/* + * Debugging levels can be set using debug insmod variable + * They can also be compiled out completely. + */ +enum { + DBG_INIT = 0x01, + DBG_BASIC = 0x02, + DBG_CORE = 0x04, + DBG_OPEN = 0x08, + DBG_CLOSE = 0x10, + DBG_READ = 0x20, + DBG_WRITE = 0x40, + DBG_IOCTL = 0x80, + DBG_PROC = 0x100, + DBG_PARAM = 0x200, + DBG_PSCAN = 0x400, + DBG_EVENT = 0x800, + DBG_DRAIN = 0x1000, + DBG_MSIGS = 0x2000, + DBG_MGMT = 0x4000, + DBG_INTR = 0x8000, + 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 MAXLINES 256 +#define MAXPORTS 8 +#define MAX_STOPS_SENT 5 + +/* Board type definitions */ + +#define T_NEO 0000 +#define T_CLASSIC 0001 +#define T_PCIBUS 0400 + +/* Board State Definitions */ + +#define BD_RUNNING 0x0 +#define BD_REASON 0x7f +#define BD_NOTFOUND 0x1 +#define BD_NOIOPORT 0x2 +#define BD_NOMEM 0x3 +#define BD_NOBIOS 0x4 +#define BD_NOFEP 0x5 +#define BD_FAILED 0x6 +#define BD_ALLOCATED 0x7 +#define BD_TRIBOOT 0x8 +#define BD_BADKME 0x80 + + +/* 4 extra for alignment play space */ +#define WRITEBUFLEN ((4096) + 4) +#define MYFLIPLEN N_TTY_BUF_SIZE + +#define JSM_VERSION "jsm: 1.2-1-INKERNEL" +#define JSM_PARTNUM "40002438_A-INKERNEL" + +struct jsm_board; +struct jsm_channel; + +/************************************************************************ + * Per board operations structure * + ************************************************************************/ +struct board_ops { + irq_handler_t intr; + void (*uart_init) (struct jsm_channel *ch); + void (*uart_off) (struct jsm_channel *ch); + void (*param) (struct jsm_channel *ch); + void (*assert_modem_signals) (struct jsm_channel *ch); + void (*flush_uart_write) (struct jsm_channel *ch); + void (*flush_uart_read) (struct jsm_channel *ch); + void (*disable_receiver) (struct jsm_channel *ch); + void (*enable_receiver) (struct jsm_channel *ch); + void (*send_break) (struct jsm_channel *ch); + void (*clear_break) (struct jsm_channel *ch, int); + void (*send_start_character) (struct jsm_channel *ch); + void (*send_stop_character) (struct jsm_channel *ch); + void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch); + u32 (*get_uart_bytes_left) (struct jsm_channel *ch); + void (*send_immediate_char) (struct jsm_channel *ch, unsigned char); +}; + + +/* + * Per-board information + */ +struct jsm_board +{ + int boardnum; /* Board number: 0-32 */ + + int type; /* Type of board */ + u8 rev; /* PCI revision ID */ + struct pci_dev *pci_dev; + u32 maxports; /* MAX ports this board can handle */ + + spinlock_t bd_intr_lock; /* Used to protect the poller tasklet and + * the interrupt routine from each other. + */ + + u32 nasync; /* Number of ports on card */ + + u32 irq; /* Interrupt request number */ + + u64 membase; /* Start of base memory of the card */ + u64 membase_end; /* End of base memory of the card */ + + u8 __iomem *re_map_membase;/* Remapped memory of the card */ + + u64 iobase; /* Start of io base of the card */ + u64 iobase_end; /* End of io base of the card */ + + u32 bd_uart_offset; /* Space between each UART */ + + struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */ + char *flipbuf; /* Our flip buffer, alloced if board is found */ + + u32 bd_dividend; /* Board/UARTs specific dividend */ + + struct board_ops *bd_ops; + + struct list_head jsm_board_entry; +}; + +/************************************************************************ + * Device flag definitions for ch_flags. + ************************************************************************/ +#define CH_PRON 0x0001 /* Printer on string */ +#define CH_STOP 0x0002 /* Output is stopped */ +#define CH_STOPI 0x0004 /* Input is stopped */ +#define CH_CD 0x0008 /* Carrier is present */ +#define CH_FCAR 0x0010 /* Carrier forced on */ +#define CH_HANGUP 0x0020 /* Hangup received */ + +#define CH_RECEIVER_OFF 0x0040 /* Receiver is off */ +#define CH_OPENING 0x0080 /* Port in fragile open state */ +#define CH_CLOSING 0x0100 /* Port in fragile close state */ +#define CH_FIFO_ENABLED 0x0200 /* Port has FIFOs enabled */ +#define CH_TX_FIFO_EMPTY 0x0400 /* TX Fifo is completely empty */ +#define CH_TX_FIFO_LWM 0x0800 /* TX Fifo is below Low Water */ +#define CH_BREAK_SENDING 0x1000 /* Break is being sent */ +#define CH_LOOPBACK 0x2000 /* Channel is in lookback mode */ +#define CH_FLIPBUF_IN_USE 0x4000 /* Channel's flipbuf is in use */ +#define CH_BAUD0 0x08000 /* Used for checking B0 transitions */ + +/* Our Read/Error/Write queue sizes */ +#define RQUEUEMASK 0x1FFF /* 8 K - 1 */ +#define EQUEUEMASK 0x1FFF /* 8 K - 1 */ +#define WQUEUEMASK 0x0FFF /* 4 K - 1 */ +#define RQUEUESIZE (RQUEUEMASK + 1) +#define EQUEUESIZE RQUEUESIZE +#define WQUEUESIZE (WQUEUEMASK + 1) + + +/************************************************************************ + * Channel information structure. + ************************************************************************/ +struct jsm_channel { + struct uart_port uart_port; + struct jsm_board *ch_bd; /* Board structure pointer */ + + spinlock_t ch_lock; /* provide for serialization */ + wait_queue_head_t ch_flags_wait; + + u32 ch_portnum; /* Port number, 0 offset. */ + u32 ch_open_count; /* open count */ + u32 ch_flags; /* Channel flags */ + + u64 ch_close_delay; /* How long we should drop RTS/DTR for */ + + tcflag_t ch_c_iflag; /* channel iflags */ + tcflag_t ch_c_cflag; /* channel cflags */ + tcflag_t ch_c_oflag; /* channel oflags */ + tcflag_t ch_c_lflag; /* channel lflags */ + u8 ch_stopc; /* Stop character */ + u8 ch_startc; /* Start character */ + + u8 ch_mostat; /* FEP output modem status */ + u8 ch_mistat; /* FEP input modem status */ + + struct neo_uart_struct __iomem *ch_neo_uart; /* Pointer to the "mapped" UART struct */ + u8 ch_cached_lsr; /* Cached value of the LSR register */ + + u8 *ch_rqueue; /* Our read queue buffer - malloc'ed */ + u16 ch_r_head; /* Head location of the read queue */ + u16 ch_r_tail; /* Tail location of the read queue */ + + u8 *ch_equeue; /* Our error queue buffer - malloc'ed */ + u16 ch_e_head; /* Head location of the error queue */ + u16 ch_e_tail; /* Tail location of the error queue */ + + u8 *ch_wqueue; /* Our write queue buffer - malloc'ed */ + u16 ch_w_head; /* Head location of the write queue */ + u16 ch_w_tail; /* Tail location of the write queue */ + + u64 ch_rxcount; /* total of data received so far */ + u64 ch_txcount; /* total of data transmitted so far */ + + u8 ch_r_tlevel; /* Receive Trigger level */ + u8 ch_t_tlevel; /* Transmit Trigger level */ + + u8 ch_r_watermark; /* Receive Watermark */ + + + u32 ch_stops_sent; /* How many times I have sent a stop character + * to try to stop the other guy sending. + */ + u64 ch_err_parity; /* Count of parity errors on channel */ + u64 ch_err_frame; /* Count of framing errors on channel */ + u64 ch_err_break; /* Count of breaks on channel */ + u64 ch_err_overrun; /* Count of overruns on channel */ + + u64 ch_xon_sends; /* Count of xons transmitted */ + u64 ch_xoff_sends; /* Count of xoffs transmitted */ +}; + + +/************************************************************************ + * Per channel/port NEO UART structure * + ************************************************************************ + * Base Structure Entries Usage Meanings to Host * + * * + * W = read write R = read only * + * U = Unused. * + ************************************************************************/ + +struct neo_uart_struct { + u8 txrx; /* WR RHR/THR - Holding Reg */ + u8 ier; /* WR IER - Interrupt Enable Reg */ + u8 isr_fcr; /* WR ISR/FCR - Interrupt Status Reg/Fifo Control Reg */ + u8 lcr; /* WR LCR - Line Control Reg */ + u8 mcr; /* WR MCR - Modem Control Reg */ + u8 lsr; /* WR LSR - Line Status Reg */ + u8 msr; /* WR MSR - Modem Status Reg */ + u8 spr; /* WR SPR - Scratch Pad Reg */ + u8 fctr; /* WR FCTR - Feature Control Reg */ + u8 efr; /* WR EFR - Enhanced Function Reg */ + u8 tfifo; /* WR TXCNT/TXTRG - Transmit FIFO Reg */ + u8 rfifo; /* WR RXCNT/RXTRG - Recieve FIFO Reg */ + u8 xoffchar1; /* WR XOFF 1 - XOff Character 1 Reg */ + u8 xoffchar2; /* WR XOFF 2 - XOff Character 2 Reg */ + u8 xonchar1; /* WR XON 1 - Xon Character 1 Reg */ + u8 xonchar2; /* WR XON 2 - XOn Character 2 Reg */ + + u8 reserved1[0x2ff - 0x200]; /* U Reserved by Exar */ + u8 txrxburst[64]; /* RW 64 bytes of RX/TX FIFO Data */ + u8 reserved2[0x37f - 0x340]; /* U Reserved by Exar */ + u8 rxburst_with_errors[64]; /* R 64 bytes of RX FIFO Data + LSR */ +}; + +/* Where to read the extended interrupt register (32bits instead of 8bits) */ +#define UART_17158_POLL_ADDR_OFFSET 0x80 + +/* + * These are the redefinitions for the FCTR on the XR17C158, since + * Exar made them different than their earlier design. (XR16C854) + */ + +/* These are only applicable when table D is selected */ +#define UART_17158_FCTR_RTS_NODELAY 0x00 +#define UART_17158_FCTR_RTS_4DELAY 0x01 +#define UART_17158_FCTR_RTS_6DELAY 0x02 +#define UART_17158_FCTR_RTS_8DELAY 0x03 +#define UART_17158_FCTR_RTS_12DELAY 0x12 +#define UART_17158_FCTR_RTS_16DELAY 0x05 +#define UART_17158_FCTR_RTS_20DELAY 0x13 +#define UART_17158_FCTR_RTS_24DELAY 0x06 +#define UART_17158_FCTR_RTS_28DELAY 0x14 +#define UART_17158_FCTR_RTS_32DELAY 0x07 +#define UART_17158_FCTR_RTS_36DELAY 0x16 +#define UART_17158_FCTR_RTS_40DELAY 0x08 +#define UART_17158_FCTR_RTS_44DELAY 0x09 +#define UART_17158_FCTR_RTS_48DELAY 0x10 +#define UART_17158_FCTR_RTS_52DELAY 0x11 + +#define UART_17158_FCTR_RTS_IRDA 0x10 +#define UART_17158_FCTR_RS485 0x20 +#define UART_17158_FCTR_TRGA 0x00 +#define UART_17158_FCTR_TRGB 0x40 +#define UART_17158_FCTR_TRGC 0x80 +#define UART_17158_FCTR_TRGD 0xC0 + +/* 17158 trigger table selects.. */ +#define UART_17158_FCTR_BIT6 0x40 +#define UART_17158_FCTR_BIT7 0x80 + +/* 17158 TX/RX memmapped buffer offsets */ +#define UART_17158_RX_FIFOSIZE 64 +#define UART_17158_TX_FIFOSIZE 64 + +/* 17158 Extended IIR's */ +#define UART_17158_IIR_RDI_TIMEOUT 0x0C /* Receiver data TIMEOUT */ +#define UART_17158_IIR_XONXOFF 0x10 /* Received an XON/XOFF char */ +#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20 /* CTS/DSR or RTS/DTR state change */ +#define UART_17158_IIR_FIFO_ENABLED 0xC0 /* 16550 FIFOs are Enabled */ + +/* + * These are the extended interrupts that get sent + * back to us from the UART's 32bit interrupt register + */ +#define UART_17158_RX_LINE_STATUS 0x1 /* RX Ready */ +#define UART_17158_RXRDY_TIMEOUT 0x2 /* RX Ready Timeout */ +#define UART_17158_TXRDY 0x3 /* TX Ready */ +#define UART_17158_MSR 0x4 /* Modem State Change */ +#define UART_17158_TX_AND_FIFO_CLR 0x40 /* Transmitter Holding Reg Empty */ +#define UART_17158_RX_FIFO_DATA_ERROR 0x80 /* UART detected an RX FIFO Data error */ + +/* + * These are the EXTENDED definitions for the 17C158's Interrupt + * Enable Register. + */ +#define UART_17158_EFR_ECB 0x10 /* Enhanced control bit */ +#define UART_17158_EFR_IXON 0x2 /* Receiver compares Xon1/Xoff1 */ +#define UART_17158_EFR_IXOFF 0x8 /* Transmit Xon1/Xoff1 */ +#define UART_17158_EFR_RTSDTR 0x40 /* Auto RTS/DTR Flow Control Enable */ +#define UART_17158_EFR_CTSDSR 0x80 /* Auto CTS/DSR Flow COntrol Enable */ + +#define UART_17158_XOFF_DETECT 0x1 /* Indicates whether chip saw an incoming XOFF char */ +#define UART_17158_XON_DETECT 0x2 /* Indicates whether chip saw an incoming XON char */ + +#define UART_17158_IER_RSVD1 0x10 /* Reserved by Exar */ +#define UART_17158_IER_XOFF 0x20 /* Xoff Interrupt Enable */ +#define UART_17158_IER_RTSDTR 0x40 /* Output Interrupt Enable */ +#define UART_17158_IER_CTSDSR 0x80 /* Input Interrupt Enable */ + +#define PCI_DEVICE_NEO_2DB9_PCI_NAME "Neo 2 - DB9 Universal PCI" +#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME "Neo 2 - DB9 Universal PCI - Powered Ring Indicator" +#define PCI_DEVICE_NEO_2RJ45_PCI_NAME "Neo 2 - RJ45 Universal PCI" +#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME "Neo 2 - RJ45 Universal PCI - Powered Ring Indicator" +#define PCIE_DEVICE_NEO_IBM_PCI_NAME "Neo 4 - PCI Express - IBM" + +/* + * Our Global Variables. + */ +extern struct uart_driver jsm_uart_driver; +extern struct board_ops jsm_neo_ops; +extern int jsm_debug; + +/************************************************************************* + * + * Prototypes for non-static functions used in more than one module + * + *************************************************************************/ +int jsm_tty_write(struct uart_port *port); +int jsm_tty_init(struct jsm_board *); +int jsm_uart_port_init(struct jsm_board *); +int jsm_remove_uart_port(struct jsm_board *); +void jsm_input(struct jsm_channel *ch); +void jsm_check_queue_flow_control(struct jsm_channel *ch); + +#endif diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c new file mode 100644 index 000000000000..18f548449c63 --- /dev/null +++ b/drivers/tty/serial/jsm/jsm_driver.c @@ -0,0 +1,297 @@ +/************************************************************************ + * Copyright 2003 Digi International (www.digi.com) + * + * Copyright (C) 2004 IBM Corporation. All rights reserved. + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * Contact Information: + * Scott H Kilau + * Wendy Xiong + * + * + ***********************************************************************/ +#include +#include +#include + +#include "jsm.h" + +MODULE_AUTHOR("Digi International, http://www.digi.com"); +MODULE_DESCRIPTION("Driver for the Digi International " + "Neo PCI based product line"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("jsm"); + +#define JSM_DRIVER_NAME "jsm" +#define NR_PORTS 32 +#define JSM_MINOR_START 0 + +struct uart_driver jsm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = JSM_DRIVER_NAME, + .dev_name = "ttyn", + .major = 0, + .minor = JSM_MINOR_START, + .nr = NR_PORTS, +}; + +static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state); +static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev); +static void jsm_io_resume(struct pci_dev *pdev); + +static struct pci_error_handlers jsm_err_handler = { + .error_detected = jsm_io_error_detected, + .slot_reset = jsm_io_slot_reset, + .resume = jsm_io_resume, +}; + +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) +{ + int rc = 0; + struct jsm_board *brd; + static int adapter_count = 0; + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "Device enable FAILED\n"); + goto out; + } + + rc = pci_request_regions(pdev, "jsm"); + if (rc) { + dev_err(&pdev->dev, "pci_request_region FAILED\n"); + goto out_disable_device; + } + + brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL); + if (!brd) { + dev_err(&pdev->dev, + "memory allocation for board structure failed\n"); + rc = -ENOMEM; + goto out_release_regions; + } + + /* store the info for the board we've found */ + brd->boardnum = adapter_count++; + brd->pci_dev = pdev; + if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM) + brd->maxports = 4; + else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8) + brd->maxports = 8; + else + brd->maxports = 2; + + spin_lock_init(&brd->bd_intr_lock); + + /* store which revision we have */ + brd->rev = pdev->revision; + + brd->irq = pdev->irq; + + jsm_printk(INIT, INFO, &brd->pci_dev, + "jsm_found_board - NEO adapter\n"); + + /* get the PCI Base Address Registers */ + brd->membase = pci_resource_start(pdev, 0); + brd->membase_end = pci_resource_end(pdev, 0); + + if (brd->membase & 1) + brd->membase &= ~3; + else + brd->membase &= ~15; + + /* Assign the board_ops struct */ + brd->bd_ops = &jsm_neo_ops; + + brd->bd_uart_offset = 0x200; + brd->bd_dividend = 921600; + + brd->re_map_membase = ioremap(brd->membase, 0x1000); + if (!brd->re_map_membase) { + dev_err(&pdev->dev, + "card has no PCI Memory resources, " + "failing board.\n"); + rc = -ENOMEM; + goto out_kfree_brd; + } + + rc = request_irq(brd->irq, brd->bd_ops->intr, + IRQF_SHARED, "JSM", brd); + if (rc) { + printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq); + goto out_iounmap; + } + + rc = jsm_tty_init(brd); + if (rc < 0) { + dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc); + rc = -ENXIO; + goto out_free_irq; + } + + rc = jsm_uart_port_init(brd); + if (rc < 0) { + /* XXX: leaking all resources from jsm_tty_init here! */ + dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc); + rc = -ENXIO; + goto out_free_irq; + } + + /* Log the information about the board */ + dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n", + adapter_count, brd->rev, brd->irq); + + /* + * allocate flip buffer for board. + * + * Okay to malloc with GFP_KERNEL, we are not at interrupt + * context, and there are no locks held. + */ + brd->flipbuf = kzalloc(MYFLIPLEN, GFP_KERNEL); + if (!brd->flipbuf) { + /* XXX: leaking all resources from jsm_tty_init and + jsm_uart_port_init here! */ + dev_err(&pdev->dev, "memory allocation for flipbuf failed\n"); + rc = -ENOMEM; + goto out_free_uart; + } + + pci_set_drvdata(pdev, brd); + pci_save_state(pdev); + + return 0; + out_free_uart: + jsm_remove_uart_port(brd); + out_free_irq: + jsm_remove_uart_port(brd); + free_irq(brd->irq, brd); + out_iounmap: + iounmap(brd->re_map_membase); + out_kfree_brd: + kfree(brd); + out_release_regions: + pci_release_regions(pdev); + out_disable_device: + pci_disable_device(pdev); + out: + return rc; +} + +static void __devexit jsm_remove_one(struct pci_dev *pdev) +{ + struct jsm_board *brd = pci_get_drvdata(pdev); + int i = 0; + + jsm_remove_uart_port(brd); + + free_irq(brd->irq, brd); + iounmap(brd->re_map_membase); + + /* Free all allocated channels structs */ + for (i = 0; i < brd->maxports; i++) { + if (brd->channels[i]) { + kfree(brd->channels[i]->ch_rqueue); + kfree(brd->channels[i]->ch_equeue); + kfree(brd->channels[i]->ch_wqueue); + kfree(brd->channels[i]); + } + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(brd->flipbuf); + kfree(brd); +} + +static struct pci_device_id jsm_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 }, + { PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, jsm_pci_tbl); + +static struct pci_driver jsm_driver = { + .name = "jsm", + .id_table = jsm_pci_tbl, + .probe = jsm_probe_one, + .remove = __devexit_p(jsm_remove_one), + .err_handler = &jsm_err_handler, +}; + +static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct jsm_board *brd = pci_get_drvdata(pdev); + + jsm_remove_uart_port(brd); + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev) +{ + int rc; + + rc = pci_enable_device(pdev); + + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + pci_set_master(pdev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void jsm_io_resume(struct pci_dev *pdev) +{ + struct jsm_board *brd = pci_get_drvdata(pdev); + + pci_restore_state(pdev); + + jsm_uart_port_init(brd); +} + +static int __init jsm_init_module(void) +{ + int rc; + + rc = uart_register_driver(&jsm_uart_driver); + if (!rc) { + rc = pci_register_driver(&jsm_driver); + if (rc) + uart_unregister_driver(&jsm_uart_driver); + } + return rc; +} + +static void __exit jsm_exit_module(void) +{ + pci_unregister_driver(&jsm_driver); + uart_unregister_driver(&jsm_uart_driver); +} + +module_init(jsm_init_module); +module_exit(jsm_exit_module); diff --git a/drivers/tty/serial/jsm/jsm_neo.c b/drivers/tty/serial/jsm/jsm_neo.c new file mode 100644 index 000000000000..7960d9633c15 --- /dev/null +++ b/drivers/tty/serial/jsm/jsm_neo.c @@ -0,0 +1,1412 @@ +/************************************************************************ + * Copyright 2003 Digi International (www.digi.com) + * + * Copyright (C) 2004 IBM Corporation. All rights reserved. + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * Contact Information: + * Scott H Kilau + * Wendy Xiong + * + ***********************************************************************/ +#include /* For udelay */ +#include /* For the various UART offsets */ +#include +#include +#include + +#include "jsm.h" /* Driver main header file */ + +static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; + +/* + * This function allows calls to ensure that all outstanding + * PCI writes have been completed, by doing a PCI read against + * a non-destructive, read-only location on the Neo card. + * + * In this case, we are reading the DVID (Read-only Device Identification) + * value of the Neo card. + */ +static inline void neo_pci_posting_flush(struct jsm_board *bd) +{ + readb(bd->re_map_membase + 0x8D); +} + +static void neo_set_cts_flow_control(struct jsm_channel *ch) +{ + u8 ier, efr; + 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"); + + /* Turn on auto CTS flow control */ + ier |= (UART_17158_IER_CTSDSR); + efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR); + + /* Turn off auto Xon flow control */ + efr &= ~(UART_17158_EFR_IXON); + + /* Why? Becuz Exar's spec says we have to zero it out before setting it */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Turn on UART enhanced bits */ + writeb(efr, &ch->ch_neo_uart->efr); + + /* Turn on table D, with 8 char hi/low watermarks */ + writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr); + + /* Feed the UART our trigger levels */ + writeb(8, &ch->ch_neo_uart->tfifo); + ch->ch_t_tlevel = 8; + + writeb(ier, &ch->ch_neo_uart->ier); +} + +static void neo_set_rts_flow_control(struct jsm_channel *ch) +{ + u8 ier, efr; + 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"); + + /* Turn on auto RTS flow control */ + ier |= (UART_17158_IER_RTSDTR); + efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR); + + /* Turn off auto Xoff flow control */ + ier &= ~(UART_17158_IER_XOFF); + efr &= ~(UART_17158_EFR_IXOFF); + + /* Why? Becuz Exar's spec says we have to zero it out before setting it */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Turn on UART enhanced bits */ + writeb(efr, &ch->ch_neo_uart->efr); + + writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr); + ch->ch_r_watermark = 4; + + writeb(56, &ch->ch_neo_uart->rfifo); + ch->ch_r_tlevel = 56; + + writeb(ier, &ch->ch_neo_uart->ier); + + /* + * From the Neo UART spec sheet: + * The auto RTS/DTR function must be started by asserting + * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after + * it is enabled. + */ + ch->ch_mostat |= (UART_MCR_RTS); +} + + +static void neo_set_ixon_flow_control(struct jsm_channel *ch) +{ + u8 ier, efr; + 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"); + + /* Turn off auto CTS flow control */ + ier &= ~(UART_17158_IER_CTSDSR); + efr &= ~(UART_17158_EFR_CTSDSR); + + /* Turn on auto Xon flow control */ + efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON); + + /* Why? Becuz Exar's spec says we have to zero it out before setting it */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Turn on UART enhanced bits */ + writeb(efr, &ch->ch_neo_uart->efr); + + writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); + ch->ch_r_watermark = 4; + + writeb(32, &ch->ch_neo_uart->rfifo); + ch->ch_r_tlevel = 32; + + /* Tell UART what start/stop chars it should be looking for */ + writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1); + writeb(0, &ch->ch_neo_uart->xonchar2); + + writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1); + writeb(0, &ch->ch_neo_uart->xoffchar2); + + writeb(ier, &ch->ch_neo_uart->ier); +} + +static void neo_set_ixoff_flow_control(struct jsm_channel *ch) +{ + u8 ier, efr; + 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"); + + /* Turn off auto RTS flow control */ + ier &= ~(UART_17158_IER_RTSDTR); + efr &= ~(UART_17158_EFR_RTSDTR); + + /* Turn on auto Xoff flow control */ + ier |= (UART_17158_IER_XOFF); + efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF); + + /* Why? Becuz Exar's spec says we have to zero it out before setting it */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Turn on UART enhanced bits */ + writeb(efr, &ch->ch_neo_uart->efr); + + /* Turn on table D, with 8 char hi/low watermarks */ + writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); + + writeb(8, &ch->ch_neo_uart->tfifo); + ch->ch_t_tlevel = 8; + + /* Tell UART what start/stop chars it should be looking for */ + writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1); + writeb(0, &ch->ch_neo_uart->xonchar2); + + writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1); + writeb(0, &ch->ch_neo_uart->xoffchar2); + + writeb(ier, &ch->ch_neo_uart->ier); +} + +static void neo_set_no_input_flow_control(struct jsm_channel *ch) +{ + u8 ier, efr; + 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"); + + /* Turn off auto RTS flow control */ + ier &= ~(UART_17158_IER_RTSDTR); + efr &= ~(UART_17158_EFR_RTSDTR); + + /* Turn off auto Xoff flow control */ + ier &= ~(UART_17158_IER_XOFF); + if (ch->ch_c_iflag & IXON) + efr &= ~(UART_17158_EFR_IXOFF); + else + efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF); + + /* Why? Becuz Exar's spec says we have to zero it out before setting it */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Turn on UART enhanced bits */ + writeb(efr, &ch->ch_neo_uart->efr); + + /* Turn on table D, with 8 char hi/low watermarks */ + writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); + + ch->ch_r_watermark = 0; + + writeb(16, &ch->ch_neo_uart->tfifo); + ch->ch_t_tlevel = 16; + + writeb(16, &ch->ch_neo_uart->rfifo); + ch->ch_r_tlevel = 16; + + writeb(ier, &ch->ch_neo_uart->ier); +} + +static void neo_set_no_output_flow_control(struct jsm_channel *ch) +{ + u8 ier, efr; + 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"); + + /* Turn off auto CTS flow control */ + ier &= ~(UART_17158_IER_CTSDSR); + efr &= ~(UART_17158_EFR_CTSDSR); + + /* Turn off auto Xon flow control */ + if (ch->ch_c_iflag & IXOFF) + efr &= ~(UART_17158_EFR_IXON); + else + efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON); + + /* Why? Becuz Exar's spec says we have to zero it out before setting it */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Turn on UART enhanced bits */ + writeb(efr, &ch->ch_neo_uart->efr); + + /* Turn on table D, with 8 char hi/low watermarks */ + writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr); + + ch->ch_r_watermark = 0; + + writeb(16, &ch->ch_neo_uart->tfifo); + ch->ch_t_tlevel = 16; + + writeb(16, &ch->ch_neo_uart->rfifo); + ch->ch_r_tlevel = 16; + + writeb(ier, &ch->ch_neo_uart->ier); +} + +static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch) +{ + + /* if hardware flow control is set, then skip this whole thing */ + if (ch->ch_c_cflag & CRTSCTS) + return; + + jsm_printk(PARAM, INFO, &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); + writeb(0, &ch->ch_neo_uart->xonchar2); + + writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1); + writeb(0, &ch->ch_neo_uart->xoffchar2); +} + +static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch) +{ + int qleft = 0; + u8 linestatus = 0; + u8 error_mask = 0; + int n = 0; + int total = 0; + u16 head; + u16 tail; + + if (!ch) + return; + + /* cache head and tail of queue */ + head = ch->ch_r_head & RQUEUEMASK; + tail = ch->ch_r_tail & RQUEUEMASK; + + /* Get our cached LSR */ + linestatus = ch->ch_cached_lsr; + ch->ch_cached_lsr = 0; + + /* Store how much space we have left in the queue */ + if ((qleft = tail - head - 1) < 0) + qleft += RQUEUEMASK + 1; + + /* + * If the UART is not in FIFO mode, force the FIFO copy to + * NOT be run, by setting total to 0. + * + * On the other hand, if the UART IS in FIFO mode, then ask + * the UART to give us an approximation of data it has RX'ed. + */ + if (!(ch->ch_flags & CH_FIFO_ENABLED)) + total = 0; + else { + total = readb(&ch->ch_neo_uart->rfifo); + + /* + * EXAR chip bug - RX FIFO COUNT - Fudge factor. + * + * This resolves a problem/bug with the Exar chip that sometimes + * returns a bogus value in the rfifo register. + * The count can be any where from 0-3 bytes "off". + * Bizarre, but true. + */ + total -= 3; + } + + /* + * Finally, bound the copy to make sure we don't overflow + * our own queue... + * The byte by byte copy loop below this loop this will + * deal with the queue overflow possibility. + */ + total = min(total, qleft); + + while (total > 0) { + /* + * Grab the linestatus register, we need to check + * to see if there are any errors in the FIFO. + */ + linestatus = readb(&ch->ch_neo_uart->lsr); + + /* + * Break out if there is a FIFO error somewhere. + * This will allow us to go byte by byte down below, + * finding the exact location of the error. + */ + if (linestatus & UART_17158_RX_FIFO_DATA_ERROR) + break; + + /* Make sure we don't go over the end of our queue */ + n = min(((u32) total), (RQUEUESIZE - (u32) head)); + + /* + * Cut down n even further if needed, this is to fix + * a problem with memcpy_fromio() with the Neo on the + * IBM pSeries platform. + * 15 bytes max appears to be the magic number. + */ + n = min((u32) n, (u32) 12); + + /* + * Since we are grabbing the linestatus register, which + * will reset some bits after our read, we need to ensure + * we don't miss our TX FIFO emptys. + */ + if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + + linestatus = 0; + + /* Copy data from uart to the queue */ + memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n); + /* + * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed + * that all the data currently in the FIFO is free of + * breaks and parity/frame/orun errors. + */ + memset(ch->ch_equeue + head, 0, n); + + /* Add to and flip head if needed */ + head = (head + n) & RQUEUEMASK; + total -= n; + qleft -= n; + ch->ch_rxcount += n; + } + + /* + * Create a mask to determine whether we should + * insert the character (if any) into our queue. + */ + if (ch->ch_c_iflag & IGNBRK) + error_mask |= UART_LSR_BI; + + /* + * Now cleanup any leftover bytes still in the UART. + * Also deal with any possible queue overflow here as well. + */ + while (1) { + + /* + * Its possible we have a linestatus from the loop above + * this, so we "OR" on any extra bits. + */ + linestatus |= readb(&ch->ch_neo_uart->lsr); + + /* + * If the chip tells us there is no more data pending to + * be read, we can then leave. + * But before we do, cache the linestatus, just in case. + */ + if (!(linestatus & UART_LSR_DR)) { + ch->ch_cached_lsr = linestatus; + break; + } + + /* No need to store this bit */ + linestatus &= ~UART_LSR_DR; + + /* + * Since we are grabbing the linestatus register, which + * will reset some bits after our read, we need to ensure + * we don't miss our TX FIFO emptys. + */ + if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) { + linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR); + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + } + + /* + * Discard character if we are ignoring the error mask. + */ + if (linestatus & error_mask) { + u8 discard; + linestatus = 0; + memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1); + continue; + } + + /* + * If our queue is full, we have no choice but to drop some data. + * The assumption is that HWFLOW or SWFLOW should have stopped + * things way way before we got to this point. + * + * I decided that I wanted to ditch the oldest data first, + * I hope thats okay with everyone? Yes? Good. + */ + while (qleft < 1) { + jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, + "Queue full, dropping DATA:%x LSR:%x\n", + ch->ch_rqueue[tail], ch->ch_equeue[tail]); + + ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK; + ch->ch_err_overrun++; + qleft++; + } + + 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]); + + /* Ditch any remaining linestatus value. */ + linestatus = 0; + + /* Add to and flip head if needed */ + head = (head + 1) & RQUEUEMASK; + + qleft--; + ch->ch_rxcount++; + } + + /* + * Write new final heads to channel structure. + */ + ch->ch_r_head = head & RQUEUEMASK; + ch->ch_e_head = head & EQUEUEMASK; + jsm_input(ch); +} + +static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch) +{ + u16 head; + u16 tail; + int n; + int s; + int qlen; + u32 len_written = 0; + + if (!ch) + return; + + /* No data to write to the UART */ + if (ch->ch_w_tail == ch->ch_w_head) + return; + + /* If port is "stopped", don't send any data to the UART */ + if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING)) + return; + /* + * If FIFOs are disabled. Send data directly to txrx register + */ + if (!(ch->ch_flags & CH_FIFO_ENABLED)) { + u8 lsrbits = readb(&ch->ch_neo_uart->lsr); + + ch->ch_cached_lsr |= lsrbits; + if (ch->ch_cached_lsr & UART_LSR_THRE) { + ch->ch_cached_lsr &= ~(UART_LSR_THRE); + + writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx); + jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, + "Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]); + ch->ch_w_tail++; + ch->ch_w_tail &= WQUEUEMASK; + ch->ch_txcount++; + } + return; + } + + /* + * We have to do it this way, because of the EXAR TXFIFO count bug. + */ + if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM))) + return; + + n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel; + + /* cache head and tail of queue */ + head = ch->ch_w_head & WQUEUEMASK; + tail = ch->ch_w_tail & WQUEUEMASK; + qlen = (head - tail) & WQUEUEMASK; + + /* Find minimum of the FIFO space, versus queue length */ + n = min(n, qlen); + + while (n > 0) { + + s = ((head >= tail) ? head : WQUEUESIZE) - tail; + s = min(s, n); + + if (s <= 0) + break; + + memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s); + /* Add and flip queue if needed */ + tail = (tail + s) & WQUEUEMASK; + n -= s; + ch->ch_txcount += s; + len_written += s; + } + + /* Update the final tail */ + ch->ch_w_tail = tail & WQUEUEMASK; + + if (len_written >= ch->ch_t_tlevel) + ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + + if (!jsm_tty_write(&ch->uart_port)) + uart_write_wakeup(&ch->uart_port); +} + +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); + + /* Scrub off lower bits. They signify delta's, which I don't care about */ + /* Keep DDCD and DDSR though */ + msignals &= 0xf8; + + if (msignals & UART_MSR_DDCD) + uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_DCD); + if (msignals & UART_MSR_DDSR) + uart_handle_cts_change(&ch->uart_port, msignals & UART_MSR_CTS); + if (msignals & UART_MSR_DCD) + ch->ch_mistat |= UART_MSR_DCD; + else + ch->ch_mistat &= ~UART_MSR_DCD; + + if (msignals & UART_MSR_DSR) + ch->ch_mistat |= UART_MSR_DSR; + else + ch->ch_mistat &= ~UART_MSR_DSR; + + if (msignals & UART_MSR_RI) + ch->ch_mistat |= UART_MSR_RI; + else + ch->ch_mistat &= ~UART_MSR_RI; + + if (msignals & UART_MSR_CTS) + ch->ch_mistat |= UART_MSR_CTS; + 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", + ch->ch_portnum, + !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI), + !!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD)); +} + +/* Make the UART raise any of the output signals we want up */ +static void neo_assert_modem_signals(struct jsm_channel *ch) +{ + if (!ch) + return; + + writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); +} + +/* + * Flush the WRITE FIFO on the Neo. + * + * NOTE: Channel lock MUST be held before calling this function! + */ +static void neo_flush_uart_write(struct jsm_channel *ch) +{ + u8 tmp = 0; + int i = 0; + + if (!ch) + return; + + writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr); + + for (i = 0; i < 10; i++) { + + /* 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); + udelay(10); + } + else + break; + } + + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); +} + + +/* + * Flush the READ FIFO on the Neo. + * + * NOTE: Channel lock MUST be held before calling this function! + */ +static void neo_flush_uart_read(struct jsm_channel *ch) +{ + u8 tmp = 0; + int i = 0; + + if (!ch) + return; + + writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr); + + for (i = 0; i < 10; i++) { + + /* 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); + udelay(10); + } + else + break; + } +} + +/* + * No locks are assumed to be held when calling this function. + */ +static void neo_clear_break(struct jsm_channel *ch, int force) +{ + unsigned long lock_flags; + + spin_lock_irqsave(&ch->ch_lock, lock_flags); + + /* Turn break off, and unset some variables */ + if (ch->ch_flags & CH_BREAK_SENDING) { + u8 temp = readb(&ch->ch_neo_uart->lcr); + 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); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); + } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); +} + +/* + * Parse the ISR register. + */ +static inline void neo_parse_isr(struct jsm_board *brd, u32 port) +{ + struct jsm_channel *ch; + u8 isr; + u8 cause; + unsigned long lock_flags; + + if (!brd) + return; + + if (port > brd->maxports) + return; + + ch = brd->channels[port]; + if (!ch) + return; + + /* Here we try to figure out what caused the interrupt to happen */ + while (1) { + + isr = readb(&ch->ch_neo_uart->isr_fcr); + + /* Bail if no pending interrupt */ + if (isr & UART_IIR_NO_INT) + break; + + /* + * Yank off the upper 2 bits, which just show that the FIFO's are enabled. + */ + isr &= ~(UART_17158_IIR_FIFO_ENABLED); + + jsm_printk(INTR, INFO, &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 */ + neo_copy_data_from_uart_to_queue(ch); + + /* Call our tty layer to enforce queue flow control if needed. */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + jsm_check_queue_flow_control(ch); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + } + + if (isr & UART_IIR_THRI) { + /* Transfer data (if any) from Write Queue -> UART. */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + neo_copy_data_from_queue_to_uart(ch); + } + + 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); + + /* + * Since the UART detected either an XON or + * XOFF match, we need to figure out which + * one it was, so we can suspend or resume data flow. + */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + if (cause == UART_17158_XON_DETECT) { + /* Is output stopped right now, if so, resume it */ + 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); + } + 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_printk(INTR, INFO, &ch->ch_bd->pci_dev, + "Port: %d. XOFF detected in incoming data\n", port); + } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + } + + if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) { + /* + * If we get here, this means the hardware is doing auto flow control. + * Check to see whether RTS/DTR or CTS/DSR caused this interrupt. + */ + cause = readb(&ch->ch_neo_uart->mcr); + + /* Which pin is doing auto flow? RTS or DTR? */ + spin_lock_irqsave(&ch->ch_lock, lock_flags); + if ((cause & 0x4) == 0) { + if (cause & UART_MCR_RTS) + ch->ch_mostat |= UART_MCR_RTS; + else + ch->ch_mostat &= ~(UART_MCR_RTS); + } else { + if (cause & UART_MCR_DTR) + ch->ch_mostat |= UART_MCR_DTR; + else + ch->ch_mostat &= ~(UART_MCR_DTR); + } + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + } + + /* Parse any modem signal changes */ + jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, + "MOD_STAT: sending to parse_modem_sigs\n"); + neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); + } +} + +static inline void neo_parse_lsr(struct jsm_board *brd, u32 port) +{ + struct jsm_channel *ch; + int linestatus; + unsigned long lock_flags; + + if (!brd) + return; + + if (port > brd->maxports) + return; + + ch = brd->channels[port]; + if (!ch) + return; + + 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); + + ch->ch_cached_lsr |= linestatus; + + if (ch->ch_cached_lsr & UART_LSR_DR) { + /* Read data from uart -> queue */ + neo_copy_data_from_uart_to_queue(ch); + spin_lock_irqsave(&ch->ch_lock, lock_flags); + jsm_check_queue_flow_control(ch); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + } + + /* + * This is a special flag. It indicates that at least 1 + * RX error (parity, framing, or break) has happened. + * Mark this in our struct, which will tell me that I have + *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, + "%s:%d Port: %d Got an RX error, need to parse LSR\n", + __FILE__, __LINE__, port); + + /* + * The next 3 tests should *NOT* happen, as the above test + * should encapsulate all 3... At least, thats what Exar says. + */ + + 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); + } + + 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); + } + + 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); + } + + if (linestatus & UART_LSR_OE) { + /* + * Rx Oruns. Exar says that an orun will NOT corrupt + * the FIFO. It will just replace the holding register + * with this new data byte. So basically just ignore this. + * 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); + } + + if (linestatus & UART_LSR_THRE) { + spin_lock_irqsave(&ch->ch_lock, lock_flags); + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + + /* Transfer data (if any) from Write Queue -> UART. */ + neo_copy_data_from_queue_to_uart(ch); + } + else if (linestatus & UART_17158_TX_AND_FIFO_CLR) { + spin_lock_irqsave(&ch->ch_lock, lock_flags); + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + + /* Transfer data (if any) from Write Queue -> UART. */ + neo_copy_data_from_queue_to_uart(ch); + } +} + +/* + * neo_param() + * Send any/all changes to the line to the UART. + */ +static void neo_param(struct jsm_channel *ch) +{ + u8 lcr = 0; + u8 uart_lcr, ier; + u32 baud; + int quot; + struct jsm_board *bd; + + bd = ch->ch_bd; + if (!bd) + return; + + /* + * If baud rate is zero, flush queues, and set mval to drop DTR. + */ + if ((ch->ch_c_cflag & (CBAUD)) == 0) { + ch->ch_r_head = ch->ch_r_tail = 0; + ch->ch_e_head = ch->ch_e_tail = 0; + ch->ch_w_head = ch->ch_w_tail = 0; + + neo_flush_uart_write(ch); + neo_flush_uart_read(ch); + + ch->ch_flags |= (CH_BAUD0); + ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR); + neo_assert_modem_signals(ch); + return; + + } else { + int i; + unsigned int cflag; + static struct { + unsigned int rate; + unsigned int cflag; + } baud_rates[] = { + { 921600, B921600 }, + { 460800, B460800 }, + { 230400, B230400 }, + { 115200, B115200 }, + { 57600, B57600 }, + { 38400, B38400 }, + { 19200, B19200 }, + { 9600, B9600 }, + { 4800, B4800 }, + { 2400, B2400 }, + { 1200, B1200 }, + { 600, B600 }, + { 300, B300 }, + { 200, B200 }, + { 150, B150 }, + { 134, B134 }, + { 110, B110 }, + { 75, B75 }, + { 50, B50 }, + }; + + cflag = C_BAUD(ch->uart_port.state->port.tty); + baud = 9600; + for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { + if (baud_rates[i].cflag == cflag) { + baud = baud_rates[i].rate; + break; + } + } + + if (ch->ch_flags & CH_BAUD0) + ch->ch_flags &= ~(CH_BAUD0); + } + + if (ch->ch_c_cflag & PARENB) + lcr |= UART_LCR_PARITY; + + if (!(ch->ch_c_cflag & PARODD)) + lcr |= UART_LCR_EPAR; + + /* + * Not all platforms support mark/space parity, + * so this will hide behind an ifdef. + */ +#ifdef CMSPAR + if (ch->ch_c_cflag & CMSPAR) + lcr |= UART_LCR_SPAR; +#endif + + if (ch->ch_c_cflag & CSTOPB) + lcr |= UART_LCR_STOP; + + switch (ch->ch_c_cflag & CSIZE) { + case CS5: + lcr |= UART_LCR_WLEN5; + break; + case CS6: + lcr |= UART_LCR_WLEN6; + break; + case CS7: + lcr |= UART_LCR_WLEN7; + break; + case CS8: + default: + lcr |= UART_LCR_WLEN8; + break; + } + + ier = readb(&ch->ch_neo_uart->ier); + uart_lcr = readb(&ch->ch_neo_uart->lcr); + + if (baud == 0) + baud = 9600; + + quot = ch->ch_bd->bd_dividend / baud; + + if (quot != 0) { + writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr); + writeb((quot & 0xff), &ch->ch_neo_uart->txrx); + writeb((quot >> 8), &ch->ch_neo_uart->ier); + writeb(lcr, &ch->ch_neo_uart->lcr); + } + + if (uart_lcr != lcr) + writeb(lcr, &ch->ch_neo_uart->lcr); + + if (ch->ch_c_cflag & CREAD) + ier |= (UART_IER_RDI | UART_IER_RLSI); + + ier |= (UART_IER_THRI | UART_IER_MSI); + + writeb(ier, &ch->ch_neo_uart->ier); + + /* Set new start/stop chars */ + neo_set_new_start_stop_chars(ch); + + if (ch->ch_c_cflag & CRTSCTS) + neo_set_cts_flow_control(ch); + else if (ch->ch_c_iflag & IXON) { + /* If start/stop is set to disable, then we should disable flow control */ + if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR)) + neo_set_no_output_flow_control(ch); + else + neo_set_ixon_flow_control(ch); + } + else + neo_set_no_output_flow_control(ch); + + if (ch->ch_c_cflag & CRTSCTS) + neo_set_rts_flow_control(ch); + else if (ch->ch_c_iflag & IXOFF) { + /* If start/stop is set to disable, then we should disable flow control */ + if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR)) + neo_set_no_input_flow_control(ch); + else + neo_set_ixoff_flow_control(ch); + } + else + neo_set_no_input_flow_control(ch); + /* + * Adjust the RX FIFO Trigger level if baud is less than 9600. + * Not exactly elegant, but this is needed because of the Exar chip's + * delay on firing off the RX FIFO interrupt on slower baud rates. + */ + if (baud < 9600) { + writeb(1, &ch->ch_neo_uart->rfifo); + ch->ch_r_tlevel = 1; + } + + neo_assert_modem_signals(ch); + + /* Get current status of the modem signals now */ + neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr)); + return; +} + +/* + * jsm_neo_intr() + * + * Neo specific interrupt handler. + */ +static irqreturn_t neo_intr(int irq, void *voidbrd) +{ + struct jsm_board *brd = voidbrd; + struct jsm_channel *ch; + int port = 0; + int type = 0; + int current_port; + u32 tmp; + u32 uart_poll; + unsigned long lock_flags; + unsigned long lock_flags2; + int outofloop_count = 0; + + /* Lock out the slow poller from running on this board. */ + spin_lock_irqsave(&brd->bd_intr_lock, lock_flags); + + /* + * Read in "extended" IRQ information from the 32bit Neo register. + * Bits 0-7: What port triggered the interrupt. + * Bits 8-31: Each 3bits indicate what type of interrupt occurred. + */ + 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); + + if (!uart_poll) { + jsm_printk(INTR, INFO, &brd->pci_dev, + "Kernel interrupted to me, but no pending interrupts...\n"); + spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); + return IRQ_NONE; + } + + /* At this point, we have at least SOMETHING to service, dig further... */ + + current_port = 0; + + /* Loop on each port */ + while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){ + + tmp = uart_poll; + outofloop_count++; + + /* Check current port to see if it has interrupt pending */ + if ((tmp & jsm_offset_table[current_port]) != 0) { + port = current_port; + type = tmp >> (8 + (port * 3)); + type &= 0x7; + } else { + current_port++; + continue; + } + + jsm_printk(INTR, INFO, &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, + "Interrupt with no type! port: %d\n", port); + continue; + } + + /* Switch on type of interrupt we have */ + switch (type) { + + case UART_17158_RXRDY_TIMEOUT: + /* + * RXRDY Time-out is cleared by reading data in the + * RX FIFO until it falls below the trigger level. + */ + + /* Verify the port is in range. */ + if (port > brd->nasync) + continue; + + ch = brd->channels[port]; + neo_copy_data_from_uart_to_queue(ch); + + /* Call our tty layer to enforce queue flow control if needed. */ + spin_lock_irqsave(&ch->ch_lock, lock_flags2); + jsm_check_queue_flow_control(ch); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags2); + + continue; + + case UART_17158_RX_LINE_STATUS: + /* + * RXRDY and RX LINE Status (logic OR of LSR[4:1]) + */ + neo_parse_lsr(brd, port); + continue; + + case UART_17158_TXRDY: + /* + * TXRDY interrupt clears after reading ISR register for the UART channel. + */ + + /* + * Yes, this is odd... + * Why would I check EVERY possibility of type of + * interrupt, when we know its TXRDY??? + * Becuz for some reason, even tho we got triggered for TXRDY, + * it seems to be occassionally wrong. Instead of TX, which + * it should be, I was getting things like RXDY too. Weird. + */ + neo_parse_isr(brd, port); + continue; + + case UART_17158_MSR: + /* + * MSR or flow control was seen. + */ + neo_parse_isr(brd, port); + continue; + + default: + /* + * The UART triggered us with a bogus interrupt type. + * It appears the Exar chip, when REALLY bogged down, will throw + * 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); + continue; + } + } + + spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags); + + jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n"); + return IRQ_HANDLED; +} + +/* + * Neo specific way of turning off the receiver. + * Used as a way to enforce queue flow control when in + * hardware flow control mode. + */ +static void neo_disable_receiver(struct jsm_channel *ch) +{ + u8 tmp = readb(&ch->ch_neo_uart->ier); + tmp &= ~(UART_IER_RDI); + writeb(tmp, &ch->ch_neo_uart->ier); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); +} + + +/* + * Neo specific way of turning on the receiver. + * Used as a way to un-enforce queue flow control when in + * hardware flow control mode. + */ +static void neo_enable_receiver(struct jsm_channel *ch) +{ + u8 tmp = readb(&ch->ch_neo_uart->ier); + tmp |= (UART_IER_RDI); + writeb(tmp, &ch->ch_neo_uart->ier); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); +} + +static void neo_send_start_character(struct jsm_channel *ch) +{ + if (!ch) + return; + + if (ch->ch_startc != __DISABLED_CHAR) { + ch->ch_xon_sends++; + writeb(ch->ch_startc, &ch->ch_neo_uart->txrx); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); + } +} + +static void neo_send_stop_character(struct jsm_channel *ch) +{ + if (!ch) + return; + + if (ch->ch_stopc != __DISABLED_CHAR) { + ch->ch_xoff_sends++; + writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); + } +} + +/* + * neo_uart_init + */ +static void neo_uart_init(struct jsm_channel *ch) +{ + writeb(0, &ch->ch_neo_uart->ier); + writeb(0, &ch->ch_neo_uart->efr); + writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr); + + /* Clear out UART and FIFO */ + readb(&ch->ch_neo_uart->txrx); + writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr); + readb(&ch->ch_neo_uart->lsr); + readb(&ch->ch_neo_uart->msr); + + ch->ch_flags |= CH_FIFO_ENABLED; + + /* Assert any signals we want up */ + writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr); +} + +/* + * Make the UART completely turn off. + */ +static void neo_uart_off(struct jsm_channel *ch) +{ + /* Turn off UART enhanced bits */ + writeb(0, &ch->ch_neo_uart->efr); + + /* Stop all interrupts from occurring. */ + writeb(0, &ch->ch_neo_uart->ier); +} + +static u32 neo_get_uart_bytes_left(struct jsm_channel *ch) +{ + u8 left = 0; + u8 lsr = readb(&ch->ch_neo_uart->lsr); + + /* We must cache the LSR as some of the bits get reset once read... */ + ch->ch_cached_lsr |= lsr; + + /* Determine whether the Transmitter is empty or not */ + if (!(lsr & UART_LSR_TEMT)) + left = 1; + else { + ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + left = 0; + } + + return left; +} + +/* Channel lock MUST be held by the calling function! */ +static void neo_send_break(struct jsm_channel *ch) +{ + /* + * Set the time we should stop sending the break. + * If we are already sending a break, toss away the existing + * time to stop, and use this new value instead. + */ + + /* Tell the UART to start sending the break */ + if (!(ch->ch_flags & CH_BREAK_SENDING)) { + u8 temp = readb(&ch->ch_neo_uart->lcr); + writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr); + ch->ch_flags |= (CH_BREAK_SENDING); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); + } +} + +/* + * neo_send_immediate_char. + * + * Sends a specific character as soon as possible to the UART, + * jumping over any bytes that might be in the write queue. + * + * The channel lock MUST be held by the calling function. + */ +static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c) +{ + if (!ch) + return; + + writeb(c, &ch->ch_neo_uart->txrx); + + /* flush write operation */ + neo_pci_posting_flush(ch->ch_bd); +} + +struct board_ops jsm_neo_ops = { + .intr = neo_intr, + .uart_init = neo_uart_init, + .uart_off = neo_uart_off, + .param = neo_param, + .assert_modem_signals = neo_assert_modem_signals, + .flush_uart_write = neo_flush_uart_write, + .flush_uart_read = neo_flush_uart_read, + .disable_receiver = neo_disable_receiver, + .enable_receiver = neo_enable_receiver, + .send_break = neo_send_break, + .clear_break = neo_clear_break, + .send_start_character = neo_send_start_character, + .send_stop_character = neo_send_stop_character, + .copy_data_from_queue_to_uart = neo_copy_data_from_queue_to_uart, + .get_uart_bytes_left = neo_get_uart_bytes_left, + .send_immediate_char = neo_send_immediate_char +}; diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c new file mode 100644 index 000000000000..7a4a914ecff0 --- /dev/null +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -0,0 +1,910 @@ +/************************************************************************ + * Copyright 2003 Digi International (www.digi.com) + * + * Copyright (C) 2004 IBM Corporation. All rights reserved. + * + * 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, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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. + * + * Contact Information: + * Scott H Kilau + * Ananda Venkatarman + * Modifications: + * 01/19/06: changed jsm_input routine to use the dynamically allocated + * tty_buffer changes. Contributors: Scott Kilau and Ananda V. + ***********************************************************************/ +#include +#include +#include +#include /* For udelay */ +#include +#include + +#include "jsm.h" + +static DECLARE_BITMAP(linemap, MAXLINES); + +static void jsm_carrier(struct jsm_channel *ch); + +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"); + + mstat = (ch->ch_mostat | ch->ch_mistat); + + result = 0; + + if (mstat & UART_MCR_DTR) + result |= TIOCM_DTR; + if (mstat & UART_MCR_RTS) + result |= TIOCM_RTS; + if (mstat & UART_MSR_CTS) + result |= TIOCM_CTS; + if (mstat & UART_MSR_DSR) + result |= TIOCM_DSR; + if (mstat & UART_MSR_RI) + result |= TIOCM_RI; + if (mstat & UART_MSR_DCD) + result |= TIOCM_CD; + + jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n"); + return result; +} + +static unsigned int jsm_tty_tx_empty(struct uart_port *port) +{ + return TIOCSER_TEMT; +} + +/* + * Return modem signals to ld. + */ +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"); + + result = jsm_get_mstat(channel); + + if (result < 0) + return -ENXIO; + + jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + + return result; +} + +/* + * jsm_set_modem_info() + * + * Set modem signals, called by ld. + */ +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"); + + if (mctrl & TIOCM_RTS) + channel->ch_mostat |= UART_MCR_RTS; + else + channel->ch_mostat &= ~UART_MCR_RTS; + + if (mctrl & TIOCM_DTR) + channel->ch_mostat |= UART_MCR_DTR; + else + channel->ch_mostat &= ~UART_MCR_DTR; + + channel->ch_bd->bd_ops->assert_modem_signals(channel); + + jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); + udelay(10); +} + +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"); + + channel->ch_flags &= ~(CH_STOP); + jsm_tty_write(port); + + jsm_printk(IOCTL, INFO, &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"); + + channel->ch_flags |= (CH_STOP); + + jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n"); +} + +static void jsm_tty_send_xchar(struct uart_port *port, char ch) +{ + unsigned long lock_flags; + struct jsm_channel *channel = (struct jsm_channel *)port; + struct ktermios *termios; + + spin_lock_irqsave(&port->lock, lock_flags); + termios = port->state->port.tty->termios; + if (ch == termios->c_cc[VSTART]) + channel->ch_bd->bd_ops->send_start_character(channel); + + if (ch == termios->c_cc[VSTOP]) + channel->ch_bd->bd_ops->send_stop_character(channel); + spin_unlock_irqrestore(&port->lock, lock_flags); +} + +static void jsm_tty_stop_rx(struct uart_port *port) +{ + struct jsm_channel *channel = (struct jsm_channel *)port; + + channel->ch_bd->bd_ops->disable_receiver(channel); +} + +static void jsm_tty_enable_ms(struct uart_port *port) +{ + /* Nothing needed */ +} + +static void jsm_tty_break(struct uart_port *port, int break_state) +{ + unsigned long lock_flags; + struct jsm_channel *channel = (struct jsm_channel *)port; + + spin_lock_irqsave(&port->lock, lock_flags); + if (break_state == -1) + channel->ch_bd->bd_ops->send_break(channel); + else + channel->ch_bd->bd_ops->clear_break(channel, 0); + + spin_unlock_irqrestore(&port->lock, lock_flags); +} + +static int jsm_tty_open(struct uart_port *port) +{ + struct jsm_board *brd; + struct jsm_channel *channel = (struct jsm_channel *)port; + struct ktermios *termios; + + /* Get board pointer from our array of majors we have allocated */ + brd = channel->ch_bd; + + /* + * Allocate channel buffers for read/write/error. + * Set flag, so we don't get trounced on. + */ + channel->ch_flags |= (CH_OPENING); + + /* Drop locks, as malloc with GFP_KERNEL can sleep */ + + 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"); + 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"); + return -ENOMEM; + } + } + if (!channel->ch_wqueue) { + channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); + if (!channel->ch_wqueue) { + jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, + "unable to allocate write queue buf"); + return -ENOMEM; + } + } + + channel->ch_flags &= ~(CH_OPENING); + /* + * Initialize if neither terminal is open. + */ + jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, + "jsm_open: initializing channel in open...\n"); + + /* + * Flush input queues. + */ + channel->ch_r_head = channel->ch_r_tail = 0; + channel->ch_e_head = channel->ch_e_tail = 0; + channel->ch_w_head = channel->ch_w_tail = 0; + + brd->bd_ops->flush_uart_write(channel); + brd->bd_ops->flush_uart_read(channel); + + channel->ch_flags = 0; + channel->ch_cached_lsr = 0; + channel->ch_stops_sent = 0; + + termios = port->state->port.tty->termios; + channel->ch_c_cflag = termios->c_cflag; + channel->ch_c_iflag = termios->c_iflag; + channel->ch_c_oflag = termios->c_oflag; + channel->ch_c_lflag = termios->c_lflag; + channel->ch_startc = termios->c_cc[VSTART]; + channel->ch_stopc = termios->c_cc[VSTOP]; + + /* Tell UART to init itself */ + brd->bd_ops->uart_init(channel); + + /* + * Run param in case we changed anything + */ + brd->bd_ops->param(channel); + + jsm_carrier(channel); + + channel->ch_open_count++; + + jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n"); + return 0; +} + +static void jsm_tty_close(struct uart_port *port) +{ + struct jsm_board *bd; + struct ktermios *ts; + struct jsm_channel *channel = (struct jsm_channel *)port; + + jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); + + bd = channel->ch_bd; + ts = port->state->port.tty->termios; + + channel->ch_flags &= ~(CH_STOPI); + + channel->ch_open_count--; + + /* + * If we have HUPCL set, lower DTR and RTS + */ + if (channel->ch_c_cflag & HUPCL) { + jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, + "Close. HUPCL set, dropping DTR/RTS\n"); + + /* Drop RTS/DTR */ + channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS); + bd->bd_ops->assert_modem_signals(channel); + } + + /* 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"); +} + +static void jsm_tty_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old_termios) +{ + unsigned long lock_flags; + struct jsm_channel *channel = (struct jsm_channel *)port; + + spin_lock_irqsave(&port->lock, lock_flags); + channel->ch_c_cflag = termios->c_cflag; + channel->ch_c_iflag = termios->c_iflag; + channel->ch_c_oflag = termios->c_oflag; + channel->ch_c_lflag = termios->c_lflag; + channel->ch_startc = termios->c_cc[VSTART]; + channel->ch_stopc = termios->c_cc[VSTOP]; + + channel->ch_bd->bd_ops->param(channel); + jsm_carrier(channel); + spin_unlock_irqrestore(&port->lock, lock_flags); +} + +static const char *jsm_tty_type(struct uart_port *port) +{ + return "jsm"; +} + +static void jsm_tty_release_port(struct uart_port *port) +{ +} + +static int jsm_tty_request_port(struct uart_port *port) +{ + return 0; +} + +static void jsm_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_JSM; +} + +static struct uart_ops jsm_ops = { + .tx_empty = jsm_tty_tx_empty, + .set_mctrl = jsm_tty_set_mctrl, + .get_mctrl = jsm_tty_get_mctrl, + .stop_tx = jsm_tty_stop_tx, + .start_tx = jsm_tty_start_tx, + .send_xchar = jsm_tty_send_xchar, + .stop_rx = jsm_tty_stop_rx, + .enable_ms = jsm_tty_enable_ms, + .break_ctl = jsm_tty_break, + .startup = jsm_tty_open, + .shutdown = jsm_tty_close, + .set_termios = jsm_tty_set_termios, + .type = jsm_tty_type, + .release_port = jsm_tty_release_port, + .request_port = jsm_tty_request_port, + .config_port = jsm_config_port, +}; + +/* + * jsm_tty_init() + * + * 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 i; + void __iomem *vaddr; + struct jsm_channel *ch; + + if (!brd) + return -ENXIO; + + jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + + /* + * Initialize board structure elements. + */ + + brd->nasync = brd->maxports; + + /* + * Allocate channel memory that might not have been allocated + * when the driver was first loaded. + */ + for (i = 0; i < brd->nasync; i++) { + if (!brd->channels[i]) { + + /* + * Okay to malloc with GFP_KERNEL, we are not at + * interrupt context, and there are no locks held. + */ + brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL); + if (!brd->channels[i]) { + jsm_printk(CORE, ERR, &brd->pci_dev, + "%s:%d Unable to allocate memory for channel struct\n", + __FILE__, __LINE__); + } + } + } + + ch = brd->channels[0]; + vaddr = brd->re_map_membase; + + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { + + if (!brd->channels[i]) + continue; + + spin_lock_init(&ch->ch_lock); + + if (brd->bd_uart_offset == 0x200) + ch->ch_neo_uart = vaddr + (brd->bd_uart_offset * i); + + ch->ch_bd = brd; + ch->ch_portnum = i; + + /* .25 second delay */ + ch->ch_close_delay = 250; + + init_waitqueue_head(&ch->ch_flags_wait); + } + + jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + return 0; +} + +int jsm_uart_port_init(struct jsm_board *brd) +{ + int i, rc; + unsigned int line; + struct jsm_channel *ch; + + if (!brd) + return -ENXIO; + + jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + + /* + * Initialize board structure elements. + */ + + brd->nasync = brd->maxports; + + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { + + if (!brd->channels[i]) + continue; + + brd->channels[i]->uart_port.irq = brd->irq; + brd->channels[i]->uart_port.uartclk = 14745600; + brd->channels[i]->uart_port.type = PORT_JSM; + brd->channels[i]->uart_port.iotype = UPIO_MEM; + brd->channels[i]->uart_port.membase = brd->re_map_membase; + brd->channels[i]->uart_port.fifosize = 16; + brd->channels[i]->uart_port.ops = &jsm_ops; + line = find_first_zero_bit(linemap, MAXLINES); + if (line >= MAXLINES) { + printk(KERN_INFO "jsm: linemap is full, added device failed\n"); + continue; + } else + set_bit(line, linemap); + brd->channels[i]->uart_port.line = line; + rc = uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port); + if (rc){ + printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i); + return rc; + } + else + printk(KERN_INFO "jsm: Port %d added\n", i); + } + + jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + return 0; +} + +int jsm_remove_uart_port(struct jsm_board *brd) +{ + int i; + struct jsm_channel *ch; + + if (!brd) + return -ENXIO; + + jsm_printk(INIT, INFO, &brd->pci_dev, "start\n"); + + /* + * Initialize board structure elements. + */ + + brd->nasync = brd->maxports; + + /* Set up channel variables */ + for (i = 0; i < brd->nasync; i++) { + + if (!brd->channels[i]) + continue; + + ch = brd->channels[i]; + + clear_bit(ch->uart_port.line, linemap); + uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); + } + + jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); + return 0; +} + +void jsm_input(struct jsm_channel *ch) +{ + struct jsm_board *bd; + struct tty_struct *tp; + u32 rmask; + u16 head; + u16 tail; + int data_len; + unsigned long lock_flags; + int len = 0; + int n = 0; + int s = 0; + int i = 0; + + jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); + + if (!ch) + return; + + tp = ch->uart_port.state->port.tty; + + bd = ch->ch_bd; + if(!bd) + return; + + spin_lock_irqsave(&ch->ch_lock, lock_flags); + + /* + *Figure the number of characters in the buffer. + *Exit immediately if none. + */ + + rmask = RQUEUEMASK; + + head = ch->ch_r_head & rmask; + tail = ch->ch_r_tail & rmask; + + data_len = (head - tail) & rmask; + if (data_len == 0) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + return; + } + + jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n"); + + /* + *If the device is not open, or CREAD is off, flush + *input data and return immediately. + */ + 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); + ch->ch_r_head = tail; + + /* Force queue flow control to be released, if needed */ + jsm_check_queue_flow_control(ch); + + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + return; + } + + /* + * If we are throttled, simply don't read any data. + */ + if (ch->ch_flags & CH_STOPI) { + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + jsm_printk(READ, INFO, &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"); + + 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"); + return; + } + + len = tty_buffer_request_room(tp, data_len); + n = len; + + /* + * n now contains the most amount of data we can copy, + * bounded either by the flip buffer size or the amount + * of data the card actually has pending... + */ + while (n) { + s = ((head >= tail) ? head : RQUEUESIZE) - tail; + s = min(s, n); + + if (s <= 0) + break; + + /* + * If conditions are such that ld needs to see all + * UART errors, we will have to walk each character + * and error byte and send them to the buffer one at + * a time. + */ + + if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) { + for (i = 0; i < s; i++) { + /* + * Give the Linux ld the flags in the + * format it likes. + */ + if (*(ch->ch_equeue +tail +i) & UART_LSR_BI) + tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_BREAK); + else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE) + tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY); + else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE) + tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME); + else + tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL); + } + } else { + tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ; + } + tail += s; + n -= s; + /* Flip queue if needed */ + tail &= rmask; + } + + ch->ch_r_tail = tail & rmask; + ch->ch_e_tail = tail & rmask; + jsm_check_queue_flow_control(ch); + spin_unlock_irqrestore(&ch->ch_lock, lock_flags); + + /* 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"); +} + +static void jsm_carrier(struct jsm_channel *ch) +{ + struct jsm_board *bd; + + int virt_carrier = 0; + int phys_carrier = 0; + + jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n"); + if (!ch) + return; + + bd = ch->ch_bd; + + if (!bd) + 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); + 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); + + /* + * Test for a VIRTUAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) { + + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ + + jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, + "carrier: virt DCD rose\n"); + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL carrier transition to HIGH. + */ + if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) { + + /* + * When carrier rises, wake any threads waiting + * for carrier in the open routine. + */ + + jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, + "carrier: physical DCD rose\n"); + + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Test for a PHYSICAL transition to low, so long as we aren't + * currently ignoring physical transitions (which is what "virtual + * carrier" indicates). + * + * The transition of the virtual carrier to low really doesn't + * matter... it really only means "ignore carrier state", not + * "make pretend that carrier is there". + */ + if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) + && (phys_carrier == 0)) { + /* + * When carrier drops: + * + * Drop carrier on all open units. + * + * Flush queues, waking up any task waiting in the + * line discipline. + * + * Send a hangup to the control terminal. + * + * Enable all select calls. + */ + if (waitqueue_active(&(ch->ch_flags_wait))) + wake_up_interruptible(&ch->ch_flags_wait); + } + + /* + * Make sure that our cached values reflect the current reality. + */ + if (virt_carrier == 1) + ch->ch_flags |= CH_FCAR; + else + ch->ch_flags &= ~CH_FCAR; + + if (phys_carrier == 1) + ch->ch_flags |= CH_CD; + else + ch->ch_flags &= ~CH_CD; +} + + +void jsm_check_queue_flow_control(struct jsm_channel *ch) +{ + struct board_ops *bd_ops = ch->ch_bd->bd_ops; + int qleft; + + /* Store how much space we have left in the queue */ + if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0) + qleft += RQUEUEMASK + 1; + + /* + * Check to see if we should enforce flow control on our queue because + * the ld (or user) isn't reading data out of our queue fast enuf. + * + * NOTE: This is done based on what the current flow control of the + * port is set for. + * + * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt. + * This will cause the UART's FIFO to back up, and force + * the RTS signal to be dropped. + * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to + * the other side, in hopes it will stop sending data to us. + * 3) NONE - Nothing we can do. We will simply drop any extra data + * that gets sent into us when the queue fills up. + */ + if (qleft < 256) { + /* HWFLOW */ + if (ch->ch_c_cflag & CRTSCTS) { + 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", + qleft); + } + } + /* SWFLOW */ + else if (ch->ch_c_iflag & IXOFF) { + 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); + } + } + } + + /* + * Check to see if we should unenforce flow control because + * ld (or user) finally read enuf data out of our queue. + * + * NOTE: This is done based on what the current flow control of the + * port is set for. + * + * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt. + * This will cause the UART's FIFO to raise RTS back up, + * which will allow the other side to start sending data again. + * 2) SWFLOW (IXOFF) - Send a start character to + * the other side, so it will start sending data to us again. + * 3) NONE - Do nothing. Since we didn't do anything to turn off the + * other side, we don't need to do anything now. + */ + if (qleft > (RQUEUESIZE / 2)) { + /* HWFLOW */ + if (ch->ch_c_cflag & CRTSCTS) { + 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", + qleft); + } + } + /* SWFLOW */ + 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_tty_write() + * + * Take data from the user or kernel and send it out to the FEP. + * In here exists all the Transparent Print magic as well. + */ +int jsm_tty_write(struct uart_port *port) +{ + int bufcount; + int data_count = 0,data_count1 =0; + u16 head; + u16 tail; + u16 tmask; + u32 remain; + int temp_tail = port->state->xmit.tail; + struct jsm_channel *channel = (struct jsm_channel *)port; + + tmask = WQUEUEMASK; + head = (channel->ch_w_head) & tmask; + tail = (channel->ch_w_tail) & tmask; + + if ((bufcount = tail - head - 1) < 0) + bufcount += WQUEUESIZE; + + bufcount = min(bufcount, 56); + remain = WQUEUESIZE - head; + + data_count = 0; + if (bufcount >= remain) { + bufcount -= remain; + while ((port->state->xmit.head != temp_tail) && + (data_count < remain)) { + channel->ch_wqueue[head++] = + port->state->xmit.buf[temp_tail]; + + temp_tail++; + temp_tail &= (UART_XMIT_SIZE - 1); + data_count++; + } + if (data_count == remain) head = 0; + } + + data_count1 = 0; + if (bufcount > 0) { + remain = bufcount; + while ((port->state->xmit.head != temp_tail) && + (data_count1 < remain)) { + channel->ch_wqueue[head++] = + port->state->xmit.buf[temp_tail]; + + temp_tail++; + temp_tail &= (UART_XMIT_SIZE - 1); + data_count1++; + + } + } + + port->state->xmit.tail = temp_tail; + + data_count += data_count1; + if (data_count) { + head &= tmask; + channel->ch_w_head = head; + } + + if (data_count) { + channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); + } + + return data_count; +} diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c new file mode 100644 index 000000000000..25a8bc565f40 --- /dev/null +++ b/drivers/tty/serial/kgdboc.c @@ -0,0 +1,328 @@ +/* + * Based on the same principle as kgdboe using the NETPOLL api, this + * driver uses a console polling api to implement a gdb serial inteface + * which is multiplexed on a console port. + * + * Maintainer: Jason Wessel + * + * 2007-2008 (c) Jason Wessel - Wind River Systems, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CONFIG_LEN 40 + +static struct kgdb_io kgdboc_io_ops; + +/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */ +static int configured = -1; + +static char config[MAX_CONFIG_LEN]; +static struct kparam_string kps = { + .string = config, + .maxlen = MAX_CONFIG_LEN, +}; + +static int kgdboc_use_kms; /* 1 if we use kernel mode switching */ +static struct tty_driver *kgdb_tty_driver; +static int kgdb_tty_line; + +#ifdef CONFIG_KDB_KEYBOARD +static int kgdboc_reset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + input_reset_device(dev); + + /* Retrun an error - we do not want to bind, just to reset */ + return -ENODEV; +} + +static void kgdboc_reset_disconnect(struct input_handle *handle) +{ + /* We do not expect anyone to actually bind to us */ + BUG(); +} + +static const struct input_device_id kgdboc_reset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { } +}; + +static struct input_handler kgdboc_reset_handler = { + .connect = kgdboc_reset_connect, + .disconnect = kgdboc_reset_disconnect, + .name = "kgdboc_reset", + .id_table = kgdboc_reset_ids, +}; + +static DEFINE_MUTEX(kgdboc_reset_mutex); + +static void kgdboc_restore_input_helper(struct work_struct *dummy) +{ + /* + * We need to take a mutex to prevent several instances of + * this work running on different CPUs so they don't try + * to register again already registered handler. + */ + mutex_lock(&kgdboc_reset_mutex); + + if (input_register_handler(&kgdboc_reset_handler) == 0) + input_unregister_handler(&kgdboc_reset_handler); + + mutex_unlock(&kgdboc_reset_mutex); +} + +static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); + +static void kgdboc_restore_input(void) +{ + if (likely(system_state == SYSTEM_RUNNING)) + schedule_work(&kgdboc_restore_input_work); +} + +static int kgdboc_register_kbd(char **cptr) +{ + if (strncmp(*cptr, "kbd", 3) == 0) { + if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { + kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; + kdb_poll_idx++; + if (cptr[0][3] == ',') + *cptr += 4; + else + return 1; + } + } + return 0; +} + +static void kgdboc_unregister_kbd(void) +{ + int i; + + for (i = 0; i < kdb_poll_idx; i++) { + if (kdb_poll_funcs[i] == kdb_get_kbd_char) { + kdb_poll_idx--; + kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; + kdb_poll_funcs[kdb_poll_idx] = NULL; + i--; + } + } + flush_work_sync(&kgdboc_restore_input_work); +} +#else /* ! CONFIG_KDB_KEYBOARD */ +#define kgdboc_register_kbd(x) 0 +#define kgdboc_unregister_kbd() +#define kgdboc_restore_input() +#endif /* ! CONFIG_KDB_KEYBOARD */ + +static int kgdboc_option_setup(char *opt) +{ + if (strlen(opt) > MAX_CONFIG_LEN) { + printk(KERN_ERR "kgdboc: config string too long\n"); + return -ENOSPC; + } + strcpy(config, opt); + + return 0; +} + +__setup("kgdboc=", kgdboc_option_setup); + +static void cleanup_kgdboc(void) +{ + kgdboc_unregister_kbd(); + if (configured == 1) + kgdb_unregister_io_module(&kgdboc_io_ops); +} + +static int configure_kgdboc(void) +{ + struct tty_driver *p; + int tty_line = 0; + int err; + char *cptr = config; + struct console *cons; + + err = kgdboc_option_setup(config); + if (err || !strlen(config) || isspace(config[0])) + goto noconfig; + + err = -ENODEV; + kgdboc_io_ops.is_console = 0; + kgdb_tty_driver = NULL; + + kgdboc_use_kms = 0; + if (strncmp(cptr, "kms,", 4) == 0) { + cptr += 4; + kgdboc_use_kms = 1; + } + + if (kgdboc_register_kbd(&cptr)) + goto do_register; + + p = tty_find_polling_driver(cptr, &tty_line); + if (!p) + goto noconfig; + + cons = console_drivers; + while (cons) { + int idx; + if (cons->device && cons->device(cons, &idx) == p && + idx == tty_line) { + kgdboc_io_ops.is_console = 1; + break; + } + cons = cons->next; + } + + kgdb_tty_driver = p; + kgdb_tty_line = tty_line; + +do_register: + err = kgdb_register_io_module(&kgdboc_io_ops); + if (err) + goto noconfig; + + configured = 1; + + return 0; + +noconfig: + config[0] = 0; + configured = 0; + cleanup_kgdboc(); + + return err; +} + +static int __init init_kgdboc(void) +{ + /* Already configured? */ + if (configured == 1) + return 0; + + return configure_kgdboc(); +} + +static int kgdboc_get_char(void) +{ + if (!kgdb_tty_driver) + return -1; + return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, + kgdb_tty_line); +} + +static void kgdboc_put_char(u8 chr) +{ + if (!kgdb_tty_driver) + return; + kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, + kgdb_tty_line, chr); +} + +static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) +{ + int len = strlen(kmessage); + + if (len >= MAX_CONFIG_LEN) { + printk(KERN_ERR "kgdboc: config string too long\n"); + return -ENOSPC; + } + + /* Only copy in the string if the init function has not run yet */ + if (configured < 0) { + strcpy(config, kmessage); + return 0; + } + + if (kgdb_connected) { + printk(KERN_ERR + "kgdboc: Cannot reconfigure while KGDB is connected.\n"); + + return -EBUSY; + } + + strcpy(config, kmessage); + /* Chop out \n char as a result of echo */ + if (config[len - 1] == '\n') + config[len - 1] = '\0'; + + if (configured == 1) + cleanup_kgdboc(); + + /* Go and configure with the new params. */ + return configure_kgdboc(); +} + +static int dbg_restore_graphics; + +static void kgdboc_pre_exp_handler(void) +{ + if (!dbg_restore_graphics && kgdboc_use_kms) { + dbg_restore_graphics = 1; + con_debug_enter(vc_cons[fg_console].d); + } + /* Increment the module count when the debugger is active */ + if (!kgdb_connected) + try_module_get(THIS_MODULE); +} + +static void kgdboc_post_exp_handler(void) +{ + /* decrement the module count when the debugger detaches */ + if (!kgdb_connected) + module_put(THIS_MODULE); + if (kgdboc_use_kms && dbg_restore_graphics) { + dbg_restore_graphics = 0; + con_debug_leave(); + } + kgdboc_restore_input(); +} + +static struct kgdb_io kgdboc_io_ops = { + .name = "kgdboc", + .read_char = kgdboc_get_char, + .write_char = kgdboc_put_char, + .pre_exception = kgdboc_pre_exp_handler, + .post_exception = kgdboc_post_exp_handler, +}; + +#ifdef CONFIG_KGDB_SERIAL_CONSOLE +/* This is only available if kgdboc is a built in for early debugging */ +static int __init kgdboc_early_init(char *opt) +{ + /* save the first character of the config string because the + * init routine can destroy it. + */ + char save_ch; + + kgdboc_option_setup(opt); + save_ch = config[0]; + init_kgdboc(); + config[0] = save_ch; + return 0; +} + +early_param("ekgdboc", kgdboc_early_init); +#endif /* CONFIG_KGDB_SERIAL_CONSOLE */ + +module_init(init_kgdboc); +module_exit(cleanup_kgdboc); +module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); +MODULE_PARM_DESC(kgdboc, "[,baud]"); +MODULE_DESCRIPTION("KGDB Console TTY Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/m32r_sio.c b/drivers/tty/serial/m32r_sio.c new file mode 100644 index 000000000000..bea5c215460c --- /dev/null +++ b/drivers/tty/serial/m32r_sio.c @@ -0,0 +1,1192 @@ +/* + * m32r_sio.c + * + * Driver for M32R serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.c. + * + * Copyright (C) 2001 Russell King. + * Copyright (C) 2004 Hirokazu Takata + * + * 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. + */ + +/* + * A note about mapbase / membase + * + * mapbase is the physical address of the IO port. Currently, we don't + * support this very well, and it may well be dropped from this driver + * in future. As such, mapbase should be NULL. + * + * membase is an 'ioremapped' cookie. This is compatible with the old + * serial.c driver, and is currently the preferred form. + */ + +#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PORT_M32R_BASE PORT_M32R_SIO +#define PORT_INDEX(x) (x - PORT_M32R_BASE + 1) +#define BAUD_RATE 115200 + +#include +#include "m32r_sio.h" +#include "m32r_sio_reg.h" + +/* + * Debugging. + */ +#if 0 +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#if 0 +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#define PASS_LIMIT 256 + +/* + * We default to IRQ0 for the "no irq" hack. Some + * machine types want others as well - they're free + * to redefine this in their header file. + */ +#define is_real_interrupt(irq) ((irq) != 0) + +#define BASE_BAUD 115200 + +/* Standard COM flags */ +#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST) + +/* + * SERIAL_PORT_DFNS tells us about built-in ports that have no + * standard enumeration mechanism. Platforms that can find all + * serial ports via mechanisms like ACPI or PCI need not supply it. + */ +#if defined(CONFIG_PLAT_USRV) + +#define SERIAL_PORT_DFNS \ + /* UART CLK PORT IRQ FLAGS */ \ + { 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \ + { 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */ + +#else /* !CONFIG_PLAT_USRV */ + +#if defined(CONFIG_SERIAL_M32R_PLDSIO) +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV, \ + STD_COM_FLAGS }, /* ttyS0 */ +#else +#define SERIAL_PORT_DFNS \ + { 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R, \ + STD_COM_FLAGS }, /* ttyS0 */ +#endif + +#endif /* !CONFIG_PLAT_USRV */ + +static struct old_serial_port old_serial_port[] = { + SERIAL_PORT_DFNS +}; + +#define UART_NR ARRAY_SIZE(old_serial_port) + +struct uart_sio_port { + struct uart_port port; + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned short rev; + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char lsr_break_flag; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +static struct irq_info irq_lists[NR_IRQS]; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uart_config uart_config[] = { + [PORT_UNKNOWN] = { + .name = "unknown", + .dfl_xmit_fifo_size = 1, + .flags = 0, + }, + [PORT_INDEX(PORT_M32R_SIO)] = { + .name = "M32RSIO", + .dfl_xmit_fifo_size = 1, + .flags = 0, + }, +}; + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + +#define __sio_in(x) inw((unsigned long)(x)) +#define __sio_out(v,x) outw((v),(unsigned long)(x)) + +static inline void sio_set_baud_rate(unsigned long baud) +{ + unsigned short sbaud; + sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1; + __sio_out(sbaud, PLD_ESIO0BAUR); +} + +static void sio_reset(void) +{ + unsigned short tmp; + + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0CR); + sio_set_baud_rate(BAUD_RATE); + __sio_out(0x0300, PLD_ESIO0CR); + __sio_out(0x0003, PLD_ESIO0CR); +} + +static void sio_init(void) +{ + unsigned short tmp; + + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0RXB); + tmp = __sio_in(PLD_ESIO0CR); + __sio_out(0x0300, PLD_ESIO0CR); + __sio_out(0x0003, PLD_ESIO0CR); +} + +static void sio_error(int *status) +{ + printk("SIO0 error[%04x]\n", *status); + do { + sio_init(); + } while ((*status = __sio_in(PLD_ESIO0CR)) != 3); +} + +#else /* not CONFIG_SERIAL_M32R_PLDSIO */ + +#define __sio_in(x) inl(x) +#define __sio_out(v,x) outl((v),(x)) + +static inline void sio_set_baud_rate(unsigned long baud) +{ + unsigned long i, j; + + i = boot_cpu_data.bus_clock / (baud * 16); + j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud; + i -= 1; + j = (j + 1) >> 1; + + __sio_out(i, M32R_SIO0_BAUR_PORTL); + __sio_out(j, M32R_SIO0_RBAUR_PORTL); +} + +static void sio_reset(void) +{ + __sio_out(0x00000300, M32R_SIO0_CR_PORTL); /* init status */ + __sio_out(0x00000800, M32R_SIO0_MOD1_PORTL); /* 8bit */ + __sio_out(0x00000080, M32R_SIO0_MOD0_PORTL); /* 1stop non */ + sio_set_baud_rate(BAUD_RATE); + __sio_out(0x00000000, M32R_SIO0_TRCR_PORTL); + __sio_out(0x00000003, M32R_SIO0_CR_PORTL); /* RXCEN */ +} + +static void sio_init(void) +{ + unsigned int tmp; + + tmp = __sio_in(M32R_SIO0_RXB_PORTL); + tmp = __sio_in(M32R_SIO0_RXB_PORTL); + tmp = __sio_in(M32R_SIO0_STS_PORTL); + __sio_out(0x00000003, M32R_SIO0_CR_PORTL); +} + +static void sio_error(int *status) +{ + printk("SIO0 error[%04x]\n", *status); + do { + sio_init(); + } while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3); +} + +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +static unsigned int sio_in(struct uart_sio_port *up, int offset) +{ + return __sio_in(up->port.iobase + offset); +} + +static void sio_out(struct uart_sio_port *up, int offset, int value) +{ + __sio_out(value, up->port.iobase + offset); +} + +static unsigned int serial_in(struct uart_sio_port *up, int offset) +{ + if (!offset) + return 0; + + return __sio_in(offset); +} + +static void serial_out(struct uart_sio_port *up, int offset, int value) +{ + if (!offset) + return; + + __sio_out(value, offset); +} + +static void m32r_sio_stop_tx(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void m32r_sio_start_tx(struct uart_port *port) +{ +#ifdef CONFIG_SERIAL_M32R_PLDSIO + struct uart_sio_port *up = (struct uart_sio_port *)port; + struct circ_buf *xmit = &up->port.state->xmit; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + } + while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY); +#else + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +#endif +} + +static void m32r_sio_stop_rx(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void m32r_sio_enable_ms(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void receive_chars(struct uart_sio_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch; + unsigned char flag; + int max_count = 256; + + do { + ch = sio_in(up, SIORXB); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= up->port.read_status_mask; + + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } + + if (*status & UART_LSR_BI) { + DEBUG_INTR("handling break...."); + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) + tty_insert_flip_char(tty, ch, flag); + + if (*status & UART_LSR_OE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_sio_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { +#ifndef CONFIG_SERIAL_M32R_PLDSIO /* XXX */ + serial_out(up, UART_TX, up->port.x_char); +#endif + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + m32r_sio_stop_tx(&up->port); + return; + } + + count = up->port.fifosize; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + while (!(serial_in(up, UART_LSR) & UART_LSR_THRE)); + + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + DEBUG_INTR("THRE..."); + + if (uart_circ_empty(xmit)) + m32r_sio_stop_tx(&up->port); +} + +/* + * This handles the interrupt from one port. + */ +static inline void m32r_sio_handle_port(struct uart_sio_port *up, + unsigned int status) +{ + DEBUG_INTR("status = %x...", status); + + if (status & 0x04) + receive_chars(up, &status); + if (status & 0x01) + transmit_chars(up); +} + +/* + * This is the serial driver's interrupt routine. + * + * Arjan thinks the old way was overly complex, so it got simplified. + * Alan disagrees, saying that need the complexity to handle the weird + * nature of ISA shared interrupts. (This is a special exception.) + * + * In order to handle ISA shared interrupts properly, we need to check + * that all ports have been serviced, and therefore the ISA interrupt + * line has been de-asserted. + * + * This means we need to loop through all ports. checking that they + * don't have an interrupt pending. + */ +static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id) +{ + struct irq_info *i = dev_id; + struct list_head *l, *end = NULL; + int pass_counter = 0; + + DEBUG_INTR("m32r_sio_interrupt(%d)...", irq); + +#ifdef CONFIG_SERIAL_M32R_PLDSIO +// if (irq == PLD_IRQ_SIO0_SND) +// irq = PLD_IRQ_SIO0_RCV; +#else + if (irq == M32R_IRQ_SIO0_S) + irq = M32R_IRQ_SIO0_R; +#endif + + spin_lock(&i->lock); + + l = i->head; + do { + struct uart_sio_port *up; + unsigned int sts; + + up = list_entry(l, struct uart_sio_port, list); + + sts = sio_in(up, SIOSTS); + if (sts & 0x5) { + spin_lock(&up->port.lock); + m32r_sio_handle_port(up, sts); + spin_unlock(&up->port.lock); + + end = NULL; + } else if (end == NULL) + end = l; + + l = l->next; + + if (l == i->head && pass_counter++ > PASS_LIMIT) { + if (sts & 0xe0) + sio_error(&sts); + break; + } + } while (l != end); + + spin_unlock(&i->lock); + + DEBUG_INTR("end.\n"); + + return IRQ_HANDLED; +} + +/* + * To support ISA shared interrupts, we need to have one interrupt + * handler that ensures that the IRQ line has been deasserted + * before returning. Failing to do this will result in the IRQ + * line being stuck active, and, since ISA irqs are edge triggered, + * no more IRQs will be seen. + */ +static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &up->list) + i->head = i->head->next; + list_del(&up->list); + } else { + BUG_ON(i->head != &up->list); + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct uart_sio_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + int ret, irq_flags = 0; + + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&up->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&up->list); + i->head = &up->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(up->port.irq, m32r_sio_interrupt, + irq_flags, "SIO0-RX", i); + ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt, + irq_flags, "SIO0-TX", i); + if (ret < 0) + serial_do_unlink(i, up); + } + + return ret; +} + +static void serial_unlink_irq_chain(struct uart_sio_port *up) +{ + struct irq_info *i = irq_lists + up->port.irq; + + BUG_ON(i->head == NULL); + + if (list_empty(i->head)) { + free_irq(up->port.irq, i); + free_irq(up->port.irq + 1, i); + } + + serial_do_unlink(i, up); +} + +/* + * This function is used to handle ports that do not have an interrupt. + */ +static void m32r_sio_timeout(unsigned long data) +{ + struct uart_sio_port *up = (struct uart_sio_port *)data; + unsigned int timeout; + unsigned int sts; + + sts = sio_in(up, SIOSTS); + if (sts & 0x5) { + spin_lock(&up->port.lock); + m32r_sio_handle_port(up, sts); + spin_unlock(&up->port.lock); + } + + timeout = up->port.timeout; + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + mod_timer(&up->timer, jiffies + timeout); +} + +static unsigned int m32r_sio_tx_empty(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int m32r_sio_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + +} + +static void m32r_sio_break_ctl(struct uart_port *port, int break_state) +{ + +} + +static int m32r_sio_startup(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + int retval; + + sio_init(); + + /* + * If the "interrupt" for this port doesn't correspond with any + * hardware interrupt, we use a timer-based system. The original + * driver used to do this with IRQ0. + */ + if (!is_real_interrupt(up->port.irq)) { + unsigned int timeout = up->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + up->timer.data = (unsigned long)up; + mod_timer(&up->timer, jiffies + timeout); + } else { + retval = serial_link_irq_chain(up); + if (retval) + return retval; + } + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + * - M32R_SIO: 0x0c + * - M32R_PLDSIO: 0x04 + */ + up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + sio_out(up, SIOTRCR, up->ier); + + /* + * And clear the interrupt registers again for luck. + */ + sio_reset(); + + return 0; +} + +static void m32r_sio_shutdown(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + sio_out(up, SIOTRCR, 0); + + /* + * Disable break condition and FIFOs + */ + + sio_init(); + + if (!is_real_interrupt(up->port.irq)) + del_timer_sync(&up->timer); + else + serial_unlink_irq_chain(up); +} + +static unsigned int m32r_sio_get_divisor(struct uart_port *port, + unsigned int baud) +{ + return uart_get_divisor(port, baud); +} + +static void m32r_sio_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned char cval = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + 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 + + /* + * Ask the core to calculate the divisor for us. + */ +#ifdef CONFIG_SERIAL_M32R_PLDSIO + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); +#else + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); +#endif + quot = m32r_sio_get_divisor(port, baud); + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + sio_set_baud_rate(baud); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + up->lcr = cval; /* Save LCR */ + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void m32r_sio_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + if (up->pm) + up->pm(port, state, oldstate); +} + +/* + * Resource handling. This is complicated by the fact that resources + * depend on the port type. Maybe we should be claiming the standard + * 8250 ports, and then trying to get other resources as necessary? + */ +static int +m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res) +{ + unsigned int size = 8 << up->port.regshift; +#ifndef CONFIG_SERIAL_M32R_PLDSIO + unsigned long start; +#endif + int ret = 0; + + switch (up->port.iotype) { + case UPIO_MEM: + if (up->port.mapbase) { +#ifdef CONFIG_SERIAL_M32R_PLDSIO + *res = request_mem_region(up->port.mapbase, size, "serial"); +#else + start = up->port.mapbase; + *res = request_mem_region(start, size, "serial"); +#endif + if (!*res) + ret = -EBUSY; + } + break; + + case UPIO_PORT: + *res = request_region(up->port.iobase, size, "serial"); + if (!*res) + ret = -EBUSY; + break; + } + return ret; +} + +static void m32r_sio_release_port(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + unsigned long start, offset = 0, size = 0; + + size <<= up->port.regshift; + + switch (up->port.iotype) { + case UPIO_MEM: + if (up->port.mapbase) { + /* + * Unmap the area. + */ + iounmap(up->port.membase); + up->port.membase = NULL; + + start = up->port.mapbase; + + if (size) + release_mem_region(start + offset, size); + release_mem_region(start, 8 << up->port.regshift); + } + break; + + case UPIO_PORT: + start = up->port.iobase; + + if (size) + release_region(start + offset, size); + release_region(start + offset, 8 << up->port.regshift); + break; + + default: + break; + } +} + +static int m32r_sio_request_port(struct uart_port *port) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + struct resource *res = NULL; + int ret = 0; + + ret = m32r_sio_request_std_resource(up, &res); + + /* + * If we have a mapbase, then request that as well. + */ + if (ret == 0 && up->port.flags & UPF_IOREMAP) { + int size = res->end - res->start + 1; + + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) + ret = -ENOMEM; + } + + if (ret < 0) { + if (res) + release_resource(res); + } + + return ret; +} + +static void m32r_sio_config_port(struct uart_port *port, int flags) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1); + up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int +m32r_sio_verify_port(struct 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 >= ARRAY_SIZE(uart_config)) + return -EINVAL; + return 0; +} + +static const char * +m32r_sio_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops m32r_sio_pops = { + .tx_empty = m32r_sio_tx_empty, + .set_mctrl = m32r_sio_set_mctrl, + .get_mctrl = m32r_sio_get_mctrl, + .stop_tx = m32r_sio_stop_tx, + .start_tx = m32r_sio_start_tx, + .stop_rx = m32r_sio_stop_rx, + .enable_ms = m32r_sio_enable_ms, + .break_ctl = m32r_sio_break_ctl, + .startup = m32r_sio_startup, + .shutdown = m32r_sio_shutdown, + .set_termios = m32r_sio_set_termios, + .pm = m32r_sio_pm, + .type = m32r_sio_type, + .release_port = m32r_sio_release_port, + .request_port = m32r_sio_request_port, + .config_port = m32r_sio_config_port, + .verify_port = m32r_sio_verify_port, +}; + +static struct uart_sio_port m32r_sio_ports[UART_NR]; + +static void __init m32r_sio_init_ports(void) +{ + struct uart_sio_port *up; + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port); + i++, up++) { + up->port.iobase = old_serial_port[i].port; + up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.uartclk = old_serial_port[i].baud_base * 16; + up->port.flags = old_serial_port[i].flags; + up->port.membase = old_serial_port[i].iomem_base; + up->port.iotype = old_serial_port[i].io_type; + up->port.regshift = old_serial_port[i].iomem_reg_shift; + up->port.ops = &m32r_sio_pops; + } +} + +static void __init m32r_sio_register_ports(struct uart_driver *drv) +{ + int i; + + m32r_sio_init_ports(); + + for (i = 0; i < UART_NR; i++) { + struct uart_sio_port *up = &m32r_sio_ports[i]; + + up->port.line = i; + up->port.ops = &m32r_sio_pops; + init_timer(&up->timer); + up->timer.function = m32r_sio_timeout; + + /* + * ALPHA_KLUDGE_MCR needs to be killed. + */ + up->mcr_mask = ~ALPHA_KLUDGE_MCR; + up->mcr_force = ALPHA_KLUDGE_MCR; + + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_sio_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = sio_in(up, SIOSTS); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & UART_EMPTY) != UART_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) + udelay(1); + } +} + +static void m32r_sio_console_putchar(struct uart_port *port, int ch) +{ + struct uart_sio_port *up = (struct uart_sio_port *)port; + + wait_for_xmitr(up); + sio_out(up, SIOTXB, ch); +} + +/* + * 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. + */ +static void m32r_sio_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_sio_port *up = &m32r_sio_ports[co->index]; + unsigned int ier; + + /* + * First save the UER then disable the interrupts + */ + ier = sio_in(up, SIOTRCR); + sio_out(up, SIOTRCR, 0); + + uart_console_write(&up->port, s, count, m32r_sio_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, SIOTRCR, ier); +} + +static int __init m32r_sio_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + 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. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &m32r_sio_ports[co->index].port; + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver m32r_sio_reg; +static struct console m32r_sio_console = { + .name = "ttyS", + .write = m32r_sio_console_write, + .device = uart_console_device, + .setup = m32r_sio_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &m32r_sio_reg, +}; + +static int __init m32r_sio_console_init(void) +{ + sio_reset(); + sio_init(); + m32r_sio_init_ports(); + register_console(&m32r_sio_console); + return 0; +} +console_initcall(m32r_sio_console_init); + +#define M32R_SIO_CONSOLE &m32r_sio_console +#else +#define M32R_SIO_CONSOLE NULL +#endif + +static struct uart_driver m32r_sio_reg = { + .owner = THIS_MODULE, + .driver_name = "sio", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = UART_NR, + .cons = M32R_SIO_CONSOLE, +}; + +/** + * m32r_sio_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void m32r_sio_suspend_port(int line) +{ + uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port); +} + +/** + * m32r_sio_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void m32r_sio_resume_port(int line) +{ + uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port); +} + +static int __init m32r_sio_init(void) +{ + int ret, i; + + printk(KERN_INFO "Serial: M32R SIO driver\n"); + + for (i = 0; i < nr_irqs; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = uart_register_driver(&m32r_sio_reg); + if (ret >= 0) + m32r_sio_register_ports(&m32r_sio_reg); + + return ret; +} + +static void __exit m32r_sio_exit(void) +{ + int i; + + for (i = 0; i < UART_NR; i++) + uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port); + + uart_unregister_driver(&m32r_sio_reg); +} + +module_init(m32r_sio_init); +module_exit(m32r_sio_exit); + +EXPORT_SYMBOL(m32r_sio_suspend_port); +EXPORT_SYMBOL(m32r_sio_resume_port); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Generic M32R SIO serial driver"); diff --git a/drivers/tty/serial/m32r_sio.h b/drivers/tty/serial/m32r_sio.h new file mode 100644 index 000000000000..e9b7e11793b1 --- /dev/null +++ b/drivers/tty/serial/m32r_sio.h @@ -0,0 +1,48 @@ +/* + * m32r_sio.h + * + * Driver for M32R serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/8250.h. + * + * Copyright (C) 2001 Russell King. + * Copyright (C) 2004 Hirokazu Takata + * + * 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. + */ + + +struct m32r_sio_probe { + struct module *owner; + int (*pci_init_one)(struct pci_dev *dev); + void (*pci_remove_one)(struct pci_dev *dev); + void (*pnp_init)(void); +}; + +int m32r_sio_register_probe(struct m32r_sio_probe *probe); +void m32r_sio_unregister_probe(struct m32r_sio_probe *probe); +void m32r_sio_get_irq_map(unsigned int *map); +void m32r_sio_suspend_port(int line); +void m32r_sio_resume_port(int line); + +struct old_serial_port { + unsigned int uart; + unsigned int baud_base; + unsigned int port; + unsigned int irq; + unsigned int flags; + unsigned char io_type; + unsigned char __iomem *iomem_base; + unsigned short iomem_reg_shift; +}; + +#define _INLINE_ inline + +#define PROBE_RSA (1 << 0) +#define PROBE_ANY (~0) + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) diff --git a/drivers/tty/serial/m32r_sio_reg.h b/drivers/tty/serial/m32r_sio_reg.h new file mode 100644 index 000000000000..4671473793e3 --- /dev/null +++ b/drivers/tty/serial/m32r_sio_reg.h @@ -0,0 +1,152 @@ +/* + * m32r_sio_reg.h + * + * Copyright (C) 1992, 1994 by Theodore Ts'o. + * Copyright (C) 2004 Hirokazu Takata + * + * Redistribution of this file is permitted under the terms of the GNU + * Public License (GPL) + * + * These are the UART port assignments, expressed as offsets from the base + * register. These assignments should hold for any serial port based on + * a 8250, 16450, or 16550(A). + */ + +#ifndef _M32R_SIO_REG_H +#define _M32R_SIO_REG_H + + +#ifdef CONFIG_SERIAL_M32R_PLDSIO + +#define SIOCR 0x000 +#define SIOMOD0 0x002 +#define SIOMOD1 0x004 +#define SIOSTS 0x006 +#define SIOTRCR 0x008 +#define SIOBAUR 0x00a +// #define SIORBAUR 0x018 +#define SIOTXB 0x00c +#define SIORXB 0x00e + +#define UART_RX ((unsigned long) PLD_ESIO0RXB) + /* In: Receive buffer (DLAB=0) */ +#define UART_TX ((unsigned long) PLD_ESIO0TXB) + /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER ((unsigned long) PLD_ESIO0INTCR) + /* Out: Interrupt Enable Register */ +#define UART_FCTR 0 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 0 /* In: Interrupt ID Register */ +#define UART_FCR 0 /* Out: FIFO Control Register */ +#define UART_EFR 0 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 0 /* Out: Line Control Register */ +#define UART_MCR 0 /* Out: Modem Control Register */ +#define UART_LSR ((unsigned long) PLD_ESIO0STS) + /* In: Line Status Register */ +#define UART_MSR 0 /* In: Modem Status Register */ +#define UART_SCR 0 /* I/O: Scratch Register */ +#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +#else /* not CONFIG_SERIAL_M32R_PLDSIO */ + +#define SIOCR 0x000 +#define SIOMOD0 0x004 +#define SIOMOD1 0x008 +#define SIOSTS 0x00c +#define SIOTRCR 0x010 +#define SIOBAUR 0x014 +#define SIORBAUR 0x018 +#define SIOTXB 0x01c +#define SIORXB 0x020 + +#define UART_RX M32R_SIO0_RXB_PORTL /* In: Receive buffer (DLAB=0) */ +#define UART_TX M32R_SIO0_TXB_PORTL /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_TRG 0 /* (LCR=BF) FCTR bit 7 selects Rx or Tx + * In: Fifo count + * Out: Fifo custom trigger levels + * XR16C85x only */ + +#define UART_DLM 0 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER M32R_SIO0_TRCR_PORTL /* Out: Interrupt Enable Register */ +#define UART_FCTR 0 /* (LCR=BF) Feature Control Register + * XR16C85x only */ + +#define UART_IIR 0 /* In: Interrupt ID Register */ +#define UART_FCR 0 /* Out: FIFO Control Register */ +#define UART_EFR 0 /* I/O: Extended Features Register */ + /* (DLAB=1, 16C660 only) */ + +#define UART_LCR 0 /* Out: Line Control Register */ +#define UART_MCR 0 /* Out: Modem Control Register */ +#define UART_LSR M32R_SIO0_STS_PORTL /* In: Line Status Register */ +#define UART_MSR 0 /* In: Modem Status Register */ +#define UART_SCR 0 /* I/O: Scratch Register */ +#define UART_EMSR 0 /* (LCR=BF) Extended Mode Select Register + * FCTR bit 6 selects SCR or EMSR + * XR16c85x only */ + +#endif /* CONFIG_SERIAL_M32R_PLDSIO */ + +#define UART_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * These are the definitions for the Line Control Register + * + * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting + * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +/* + * These are the definitions for the Line Status Register + */ +#define UART_LSR_TEMT 0x02 /* Transmitter empty */ +#define UART_LSR_THRE 0x01 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x00 /* Break interrupt indicator */ +#define UART_LSR_FE 0x80 /* Frame error indicator */ +#define UART_LSR_PE 0x40 /* Parity error indicator */ +#define UART_LSR_OE 0x20 /* Overrun error indicator */ +#define UART_LSR_DR 0x04 /* Receiver data ready */ + +/* + * These are the definitions for the Interrupt Identification Register + */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ + +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ + +/* + * These are the definitions for the Interrupt Enable Register + */ +#define UART_IER_MSI 0x00 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x08 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x03 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x04 /* Enable receiver data interrupt */ + +#endif /* _M32R_SIO_REG_H */ diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c new file mode 100644 index 000000000000..beb1afa27d8d --- /dev/null +++ b/drivers/tty/serial/max3100.c @@ -0,0 +1,926 @@ +/* + * + * Copyright (C) 2008 Christian Pellegrin + * + * 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. + * + * + * Notes: the MAX3100 doesn't provide an interrupt on CTS so we have + * to use polling for flow control. TX empty IRQ is unusable, since + * writing conf clears FIFO buffer and we cannot have this interrupt + * always asking us for attention. + * + * Example platform data: + + static struct plat_max3100 max3100_plat_data = { + .loopback = 0, + .crystal = 0, + .poll_time = 100, + }; + + static struct spi_board_info spi_board_info[] = { + { + .modalias = "max3100", + .platform_data = &max3100_plat_data, + .irq = IRQ_EINT12, + .max_speed_hz = 5*1000*1000, + .chip_select = 0, + }, + }; + + * The initial minor number is 209 in the low-density serial port: + * mknod /dev/ttyMAX0 c 204 209 + */ + +#define MAX3100_MAJOR 204 +#define MAX3100_MINOR 209 +/* 4 MAX3100s should be enough for everyone */ +#define MAX_MAX3100 4 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX3100_C (1<<14) +#define MAX3100_D (0<<14) +#define MAX3100_W (1<<15) +#define MAX3100_RX (0<<15) + +#define MAX3100_WC (MAX3100_W | MAX3100_C) +#define MAX3100_RC (MAX3100_RX | MAX3100_C) +#define MAX3100_WD (MAX3100_W | MAX3100_D) +#define MAX3100_RD (MAX3100_RX | MAX3100_D) +#define MAX3100_CMD (3 << 14) + +#define MAX3100_T (1<<14) +#define MAX3100_R (1<<15) + +#define MAX3100_FEN (1<<13) +#define MAX3100_SHDN (1<<12) +#define MAX3100_TM (1<<11) +#define MAX3100_RM (1<<10) +#define MAX3100_PM (1<<9) +#define MAX3100_RAM (1<<8) +#define MAX3100_IR (1<<7) +#define MAX3100_ST (1<<6) +#define MAX3100_PE (1<<5) +#define MAX3100_L (1<<4) +#define MAX3100_BAUD (0xf) + +#define MAX3100_TE (1<<10) +#define MAX3100_RAFE (1<<10) +#define MAX3100_RTS (1<<9) +#define MAX3100_CTS (1<<9) +#define MAX3100_PT (1<<8) +#define MAX3100_DATA (0xff) + +#define MAX3100_RT (MAX3100_R | MAX3100_T) +#define MAX3100_RTC (MAX3100_RT | MAX3100_CTS | MAX3100_RAFE) + +/* the following simulate a status reg for ignore_status_mask */ +#define MAX3100_STATUS_PE 1 +#define MAX3100_STATUS_FE 2 +#define MAX3100_STATUS_OE 4 + +struct max3100_port { + struct uart_port port; + struct spi_device *spi; + + int cts; /* last CTS received for flow ctrl */ + int tx_empty; /* last TX empty bit */ + + spinlock_t conf_lock; /* shared data */ + int conf_commit; /* need to make changes */ + int conf; /* configuration for the MAX31000 + * (bits 0-7, bits 8-11 are irqs) */ + int rts_commit; /* need to change rts */ + int rts; /* rts status */ + int baud; /* current baud rate */ + + int parity; /* keeps track if we should send parity */ +#define MAX3100_PARITY_ON 1 +#define MAX3100_PARITY_ODD 2 +#define MAX3100_7BIT 4 + int rx_enabled; /* if we should rx chars */ + + int irq; /* irq assigned to the max3100 */ + + int minor; /* minor number */ + int crystal; /* 1 if 3.6864Mhz crystal 0 for 1.8432 */ + int loopback; /* 1 if we are in loopback mode */ + + /* for handling irqs: need workqueue since we do spi_sync */ + struct workqueue_struct *workqueue; + struct work_struct work; + /* set to 1 to make the workhandler exit as soon as possible */ + int force_end_work; + /* need to know we are suspending to avoid deadlock on workqueue */ + int suspending; + + /* hook for suspending MAX3100 via dedicated pin */ + void (*max3100_hw_suspend) (int suspend); + + /* poll time (in ms) for ctrl lines */ + int poll_time; + /* and its timer */ + struct timer_list timer; +}; + +static struct max3100_port *max3100s[MAX_MAX3100]; /* the chips */ +static DEFINE_MUTEX(max3100s_lock); /* race on probe */ + +static int max3100_do_parity(struct max3100_port *s, u16 c) +{ + int parity; + + if (s->parity & MAX3100_PARITY_ODD) + parity = 1; + else + parity = 0; + + if (s->parity & MAX3100_7BIT) + c &= 0x7f; + else + c &= 0xff; + + parity = parity ^ (hweight8(c) & 1); + return parity; +} + +static int max3100_check_parity(struct max3100_port *s, u16 c) +{ + return max3100_do_parity(s, c) == ((c >> 8) & 1); +} + +static void max3100_calc_parity(struct max3100_port *s, u16 *c) +{ + if (s->parity & MAX3100_7BIT) + *c &= 0x7f; + else + *c &= 0xff; + + if (s->parity & MAX3100_PARITY_ON) + *c |= max3100_do_parity(s, *c) << 8; +} + +static void max3100_work(struct work_struct *w); + +static void max3100_dowork(struct max3100_port *s) +{ + if (!s->force_end_work && !work_pending(&s->work) && + !freezing(current) && !s->suspending) + queue_work(s->workqueue, &s->work); +} + +static void max3100_timeout(unsigned long data) +{ + struct max3100_port *s = (struct max3100_port *)data; + + if (s->port.state) { + max3100_dowork(s); + mod_timer(&s->timer, jiffies + s->poll_time); + } +} + +static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) +{ + struct spi_message message; + u16 etx, erx; + int status; + struct spi_transfer tran = { + .tx_buf = &etx, + .rx_buf = &erx, + .len = 2, + }; + + etx = cpu_to_be16(tx); + spi_message_init(&message); + spi_message_add_tail(&tran, &message); + status = spi_sync(s->spi, &message); + if (status) { + dev_warn(&s->spi->dev, "error while calling spi_sync\n"); + return -EIO; + } + *rx = be16_to_cpu(erx); + s->tx_empty = (*rx & MAX3100_T) > 0; + dev_dbg(&s->spi->dev, "%04x - %04x\n", tx, *rx); + return 0; +} + +static int max3100_handlerx(struct max3100_port *s, u16 rx) +{ + unsigned int ch, flg, status = 0; + int ret = 0, cts; + + if (rx & MAX3100_R && s->rx_enabled) { + dev_dbg(&s->spi->dev, "%s\n", __func__); + ch = rx & (s->parity & MAX3100_7BIT ? 0x7f : 0xff); + if (rx & MAX3100_RAFE) { + s->port.icount.frame++; + flg = TTY_FRAME; + status |= MAX3100_STATUS_FE; + } else { + if (s->parity & MAX3100_PARITY_ON) { + if (max3100_check_parity(s, rx)) { + s->port.icount.rx++; + flg = TTY_NORMAL; + } else { + s->port.icount.parity++; + flg = TTY_PARITY; + status |= MAX3100_STATUS_PE; + } + } else { + s->port.icount.rx++; + flg = TTY_NORMAL; + } + } + uart_insert_char(&s->port, status, MAX3100_STATUS_OE, ch, flg); + ret = 1; + } + + cts = (rx & MAX3100_CTS) > 0; + if (s->cts != cts) { + s->cts = cts; + uart_handle_cts_change(&s->port, cts ? TIOCM_CTS : 0); + } + + return ret; +} + +static void max3100_work(struct work_struct *w) +{ + struct max3100_port *s = container_of(w, struct max3100_port, work); + int rxchars; + u16 tx, rx; + int conf, cconf, rts, crts; + struct circ_buf *xmit = &s->port.state->xmit; + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + rxchars = 0; + do { + spin_lock(&s->conf_lock); + conf = s->conf; + cconf = s->conf_commit; + s->conf_commit = 0; + rts = s->rts; + crts = s->rts_commit; + s->rts_commit = 0; + spin_unlock(&s->conf_lock); + if (cconf) + max3100_sr(s, MAX3100_WC | conf, &rx); + if (crts) { + max3100_sr(s, MAX3100_WD | MAX3100_TE | + (s->rts ? MAX3100_RTS : 0), &rx); + rxchars += max3100_handlerx(s, rx); + } + + max3100_sr(s, MAX3100_RD, &rx); + rxchars += max3100_handlerx(s, rx); + + if (rx & MAX3100_T) { + tx = 0xffff; + if (s->port.x_char) { + tx = s->port.x_char; + s->port.icount.tx++; + s->port.x_char = 0; + } else if (!uart_circ_empty(xmit) && + !uart_tx_stopped(&s->port)) { + tx = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + s->port.icount.tx++; + } + if (tx != 0xffff) { + max3100_calc_parity(s, &tx); + tx |= MAX3100_WD | (s->rts ? MAX3100_RTS : 0); + max3100_sr(s, tx, &rx); + rxchars += max3100_handlerx(s, rx); + } + } + + if (rxchars > 16 && s->port.state->port.tty != NULL) { + tty_flip_buffer_push(s->port.state->port.tty); + rxchars = 0; + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + + } while (!s->force_end_work && + !freezing(current) && + ((rx & MAX3100_R) || + (!uart_circ_empty(xmit) && + !uart_tx_stopped(&s->port)))); + + if (rxchars > 0 && s->port.state->port.tty != NULL) + tty_flip_buffer_push(s->port.state->port.tty); +} + +static irqreturn_t max3100_irq(int irqno, void *dev_id) +{ + struct max3100_port *s = dev_id; + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + max3100_dowork(s); + return IRQ_HANDLED; +} + +static void max3100_enable_ms(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + if (s->poll_time > 0) + mod_timer(&s->timer, jiffies); + dev_dbg(&s->spi->dev, "%s\n", __func__); +} + +static void max3100_start_tx(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + max3100_dowork(s); +} + +static void max3100_stop_rx(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + s->rx_enabled = 0; + spin_lock(&s->conf_lock); + s->conf &= ~MAX3100_RM; + s->conf_commit = 1; + spin_unlock(&s->conf_lock); + max3100_dowork(s); +} + +static unsigned int max3100_tx_empty(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + /* may not be truly up-to-date */ + max3100_dowork(s); + return s->tx_empty; +} + +static unsigned int max3100_get_mctrl(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + /* may not be truly up-to-date */ + max3100_dowork(s); + /* always assert DCD and DSR since these lines are not wired */ + return (s->cts ? TIOCM_CTS : 0) | TIOCM_DSR | TIOCM_CAR; +} + +static void max3100_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + int rts; + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + rts = (mctrl & TIOCM_RTS) > 0; + + spin_lock(&s->conf_lock); + if (s->rts != rts) { + s->rts = rts; + s->rts_commit = 1; + max3100_dowork(s); + } + spin_unlock(&s->conf_lock); +} + +static void +max3100_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + int baud = 0; + unsigned cflag; + u32 param_new, param_mask, parity = 0; + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + cflag = termios->c_cflag; + param_new = 0; + param_mask = 0; + + baud = tty_termios_baud_rate(termios); + param_new = s->conf & MAX3100_BAUD; + switch (baud) { + case 300: + if (s->crystal) + baud = s->baud; + else + param_new = 15; + break; + case 600: + param_new = 14 + s->crystal; + break; + case 1200: + param_new = 13 + s->crystal; + break; + case 2400: + param_new = 12 + s->crystal; + break; + case 4800: + param_new = 11 + s->crystal; + break; + case 9600: + param_new = 10 + s->crystal; + break; + case 19200: + param_new = 9 + s->crystal; + break; + case 38400: + param_new = 8 + s->crystal; + break; + case 57600: + param_new = 1 + s->crystal; + break; + case 115200: + param_new = 0 + s->crystal; + break; + case 230400: + if (s->crystal) + param_new = 0; + else + baud = s->baud; + break; + default: + baud = s->baud; + } + tty_termios_encode_baud_rate(termios, baud, baud); + s->baud = baud; + param_mask |= MAX3100_BAUD; + + if ((cflag & CSIZE) == CS8) { + param_new &= ~MAX3100_L; + parity &= ~MAX3100_7BIT; + } else { + param_new |= MAX3100_L; + parity |= MAX3100_7BIT; + cflag = (cflag & ~CSIZE) | CS7; + } + param_mask |= MAX3100_L; + + if (cflag & CSTOPB) + param_new |= MAX3100_ST; + else + param_new &= ~MAX3100_ST; + param_mask |= MAX3100_ST; + + if (cflag & PARENB) { + param_new |= MAX3100_PE; + parity |= MAX3100_PARITY_ON; + } else { + param_new &= ~MAX3100_PE; + parity &= ~MAX3100_PARITY_ON; + } + param_mask |= MAX3100_PE; + + if (cflag & PARODD) + parity |= MAX3100_PARITY_ODD; + else + parity &= ~MAX3100_PARITY_ODD; + + /* mask termios capabilities we don't support */ + cflag &= ~CMSPAR; + termios->c_cflag = cflag; + + s->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + s->port.ignore_status_mask |= + MAX3100_STATUS_PE | MAX3100_STATUS_FE | + MAX3100_STATUS_OE; + + /* we are sending char from a workqueue so enable */ + s->port.state->port.tty->low_latency = 1; + + if (s->poll_time > 0) + del_timer_sync(&s->timer); + + uart_update_timeout(port, termios->c_cflag, baud); + + spin_lock(&s->conf_lock); + s->conf = (s->conf & ~param_mask) | (param_new & param_mask); + s->conf_commit = 1; + s->parity = parity; + spin_unlock(&s->conf_lock); + max3100_dowork(s); + + if (UART_ENABLE_MS(&s->port, termios->c_cflag)) + max3100_enable_ms(&s->port); +} + +static void max3100_shutdown(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + if (s->suspending) + return; + + s->force_end_work = 1; + + if (s->poll_time > 0) + del_timer_sync(&s->timer); + + if (s->workqueue) { + flush_workqueue(s->workqueue); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + } + if (s->irq) + free_irq(s->irq, s); + + /* set shutdown mode to save power */ + if (s->max3100_hw_suspend) + s->max3100_hw_suspend(1); + else { + u16 tx, rx; + + tx = MAX3100_WC | MAX3100_SHDN; + max3100_sr(s, tx, &rx); + } +} + +static int max3100_startup(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + char b[12]; + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + s->conf = MAX3100_RM; + s->baud = s->crystal ? 230400 : 115200; + s->rx_enabled = 1; + + if (s->suspending) + return 0; + + s->force_end_work = 0; + s->parity = 0; + s->rts = 0; + + sprintf(b, "max3100-%d", s->minor); + s->workqueue = create_freezeable_workqueue(b); + if (!s->workqueue) { + dev_warn(&s->spi->dev, "cannot create workqueue\n"); + return -EBUSY; + } + INIT_WORK(&s->work, max3100_work); + + if (request_irq(s->irq, max3100_irq, + IRQF_TRIGGER_FALLING, "max3100", s) < 0) { + dev_warn(&s->spi->dev, "cannot allocate irq %d\n", s->irq); + s->irq = 0; + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + return -EBUSY; + } + + if (s->loopback) { + u16 tx, rx; + tx = 0x4001; + max3100_sr(s, tx, &rx); + } + + if (s->max3100_hw_suspend) + s->max3100_hw_suspend(0); + s->conf_commit = 1; + max3100_dowork(s); + /* wait for clock to settle */ + msleep(50); + + max3100_enable_ms(&s->port); + + return 0; +} + +static const char *max3100_type(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + return s->port.type == PORT_MAX3100 ? "MAX3100" : NULL; +} + +static void max3100_release_port(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); +} + +static void max3100_config_port(struct uart_port *port, int flags) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + if (flags & UART_CONFIG_TYPE) + s->port.type = PORT_MAX3100; +} + +static int max3100_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + int ret = -EINVAL; + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3100) + ret = 0; + return ret; +} + +static void max3100_stop_tx(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); +} + +static int max3100_request_port(struct uart_port *port) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + return 0; +} + +static void max3100_break_ctl(struct uart_port *port, int break_state) +{ + struct max3100_port *s = container_of(port, + struct max3100_port, + port); + + dev_dbg(&s->spi->dev, "%s\n", __func__); +} + +static struct uart_ops max3100_ops = { + .tx_empty = max3100_tx_empty, + .set_mctrl = max3100_set_mctrl, + .get_mctrl = max3100_get_mctrl, + .stop_tx = max3100_stop_tx, + .start_tx = max3100_start_tx, + .stop_rx = max3100_stop_rx, + .enable_ms = max3100_enable_ms, + .break_ctl = max3100_break_ctl, + .startup = max3100_startup, + .shutdown = max3100_shutdown, + .set_termios = max3100_set_termios, + .type = max3100_type, + .release_port = max3100_release_port, + .request_port = max3100_request_port, + .config_port = max3100_config_port, + .verify_port = max3100_verify_port, +}; + +static struct uart_driver max3100_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyMAX", + .dev_name = "ttyMAX", + .major = MAX3100_MAJOR, + .minor = MAX3100_MINOR, + .nr = MAX_MAX3100, +}; +static int uart_driver_registered; + +static int __devinit max3100_probe(struct spi_device *spi) +{ + int i, retval; + struct plat_max3100 *pdata; + u16 tx, rx; + + mutex_lock(&max3100s_lock); + + if (!uart_driver_registered) { + uart_driver_registered = 1; + retval = uart_register_driver(&max3100_uart_driver); + if (retval) { + printk(KERN_ERR "Couldn't register max3100 uart driver\n"); + mutex_unlock(&max3100s_lock); + return retval; + } + } + + for (i = 0; i < MAX_MAX3100; i++) + if (!max3100s[i]) + break; + if (i == MAX_MAX3100) { + dev_warn(&spi->dev, "too many MAX3100 chips\n"); + mutex_unlock(&max3100s_lock); + return -ENOMEM; + } + + max3100s[i] = kzalloc(sizeof(struct max3100_port), GFP_KERNEL); + if (!max3100s[i]) { + dev_warn(&spi->dev, + "kmalloc for max3100 structure %d failed!\n", i); + mutex_unlock(&max3100s_lock); + return -ENOMEM; + } + max3100s[i]->spi = spi; + max3100s[i]->irq = spi->irq; + spin_lock_init(&max3100s[i]->conf_lock); + dev_set_drvdata(&spi->dev, max3100s[i]); + pdata = spi->dev.platform_data; + max3100s[i]->crystal = pdata->crystal; + max3100s[i]->loopback = pdata->loopback; + max3100s[i]->poll_time = pdata->poll_time * HZ / 1000; + if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0) + max3100s[i]->poll_time = 1; + max3100s[i]->max3100_hw_suspend = pdata->max3100_hw_suspend; + max3100s[i]->minor = i; + init_timer(&max3100s[i]->timer); + max3100s[i]->timer.function = max3100_timeout; + max3100s[i]->timer.data = (unsigned long) max3100s[i]; + + dev_dbg(&spi->dev, "%s: adding port %d\n", __func__, i); + max3100s[i]->port.irq = max3100s[i]->irq; + max3100s[i]->port.uartclk = max3100s[i]->crystal ? 3686400 : 1843200; + max3100s[i]->port.fifosize = 16; + max3100s[i]->port.ops = &max3100_ops; + max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + max3100s[i]->port.line = i; + max3100s[i]->port.type = PORT_MAX3100; + max3100s[i]->port.dev = &spi->dev; + retval = uart_add_one_port(&max3100_uart_driver, &max3100s[i]->port); + if (retval < 0) + dev_warn(&spi->dev, + "uart_add_one_port failed for line %d with error %d\n", + i, retval); + + /* set shutdown mode to save power. Will be woken-up on open */ + if (max3100s[i]->max3100_hw_suspend) + max3100s[i]->max3100_hw_suspend(1); + else { + tx = MAX3100_WC | MAX3100_SHDN; + max3100_sr(max3100s[i], tx, &rx); + } + mutex_unlock(&max3100s_lock); + return 0; +} + +static int __devexit max3100_remove(struct spi_device *spi) +{ + struct max3100_port *s = dev_get_drvdata(&spi->dev); + int i; + + mutex_lock(&max3100s_lock); + + /* find out the index for the chip we are removing */ + for (i = 0; i < MAX_MAX3100; i++) + if (max3100s[i] == s) + break; + + dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i); + uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port); + kfree(max3100s[i]); + max3100s[i] = NULL; + + /* check if this is the last chip we have */ + for (i = 0; i < MAX_MAX3100; i++) + if (max3100s[i]) { + mutex_unlock(&max3100s_lock); + return 0; + } + pr_debug("removing max3100 driver\n"); + uart_unregister_driver(&max3100_uart_driver); + + mutex_unlock(&max3100s_lock); + return 0; +} + +#ifdef CONFIG_PM + +static int max3100_suspend(struct spi_device *spi, pm_message_t state) +{ + struct max3100_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + disable_irq(s->irq); + + s->suspending = 1; + uart_suspend_port(&max3100_uart_driver, &s->port); + + if (s->max3100_hw_suspend) + s->max3100_hw_suspend(1); + else { + /* no HW suspend, so do SW one */ + u16 tx, rx; + + tx = MAX3100_WC | MAX3100_SHDN; + max3100_sr(s, tx, &rx); + } + return 0; +} + +static int max3100_resume(struct spi_device *spi) +{ + struct max3100_port *s = dev_get_drvdata(&spi->dev); + + dev_dbg(&s->spi->dev, "%s\n", __func__); + + if (s->max3100_hw_suspend) + s->max3100_hw_suspend(0); + uart_resume_port(&max3100_uart_driver, &s->port); + s->suspending = 0; + + enable_irq(s->irq); + + s->conf_commit = 1; + if (s->workqueue) + max3100_dowork(s); + + return 0; +} + +#else +#define max3100_suspend NULL +#define max3100_resume NULL +#endif + +static struct spi_driver max3100_driver = { + .driver = { + .name = "max3100", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = max3100_probe, + .remove = __devexit_p(max3100_remove), + .suspend = max3100_suspend, + .resume = max3100_resume, +}; + +static int __init max3100_init(void) +{ + return spi_register_driver(&max3100_driver); +} +module_init(max3100_init); + +static void __exit max3100_exit(void) +{ + spi_unregister_driver(&max3100_driver); +} +module_exit(max3100_exit); + +MODULE_DESCRIPTION("MAX3100 driver"); +MODULE_AUTHOR("Christian Pellegrin "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:max3100"); diff --git a/drivers/tty/serial/max3107-aava.c b/drivers/tty/serial/max3107-aava.c new file mode 100644 index 000000000000..a1fe304f2f52 --- /dev/null +++ b/drivers/tty/serial/max3107-aava.c @@ -0,0 +1,344 @@ +/* + * max3107.c - spi uart protocol driver for Maxim 3107 + * Based on max3100.c + * by Christian Pellegrin + * and max3110.c + * by Feng Tang + * + * Copyright (C) Aavamobile 2009 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include "max3107.h" + +/* GPIO direction to input function */ +static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration register */ + buf[0] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to input */ + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO configuration register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO direction to output function */ +static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration and data registers */ + buf[0] = MAX3107_GPIOCFG_REG; + buf[1] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, "SPI transfer gpio failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to output */ + buf[0] |= (0x0001 << offset); + /* Set value */ + if (value) + buf[1] |= (0x0001 << offset); + else + buf[1] &= ~(0x0001 << offset); + + /* Write new GPIO configuration and data register values */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO conf data w failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO value query function */ +static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO data register */ + buf[0] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Return value */ + return buf[0] & (0x0001 << offset); +} + +/* GPIO value set function */ +static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return; + } + + /* Read current GPIO configuration registers*/ + buf[0] = MAX3107_GPIODATA_REG; + buf[1] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO data and config read failed\n"); + return; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + if (!(buf[1] & (0x0001 << offset))) { + /* Configured as input, can't set value */ + dev_warn(&s->spi->dev, + "Trying to set value for input GPIO\n"); + return; + } + + /* Set value */ + if (value) + buf[0] |= (0x0001 << offset); + else + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO data register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n"); +} + +/* GPIO chip data */ +static struct gpio_chip max3107_gpio_chip = { + .owner = THIS_MODULE, + .direction_input = max3107_gpio_direction_in, + .direction_output = max3107_gpio_direction_out, + .get = max3107_gpio_get, + .set = max3107_gpio_set, + .can_sleep = 1, + .base = MAX3107_GPIO_BASE, + .ngpio = MAX3107_GPIO_COUNT, +}; + +/** + * max3107_aava_reset - reset on AAVA systems + * @spi: The SPI device we are probing + * + * Reset the device ready for probing. + */ + +static int max3107_aava_reset(struct spi_device *spi) +{ + /* Reset the chip */ + if (gpio_request(MAX3107_RESET_GPIO, "max3107")) { + pr_err("Requesting RESET GPIO failed\n"); + return -EIO; + } + if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) { + pr_err("Setting RESET GPIO to 0 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + msleep(MAX3107_RESET_DELAY); + if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) { + pr_err("Setting RESET GPIO to 1 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + gpio_free(MAX3107_RESET_GPIO); + msleep(MAX3107_WAKEUP_DELAY); + return 0; +} + +static int max3107_aava_configure(struct max3107_port *s) +{ + int retval; + + /* Initialize GPIO chip data */ + s->chip = max3107_gpio_chip; + s->chip.label = s->spi->modalias; + s->chip.dev = &s->spi->dev; + + /* Add GPIO chip */ + retval = gpiochip_add(&s->chip); + if (retval) { + dev_err(&s->spi->dev, "Adding GPIO chip failed\n"); + return retval; + } + + /* Temporary fix for EV2 boot problems, set modem reset to 0 */ + max3107_gpio_direction_out(&s->chip, 3, 0); + return 0; +} + +#if 0 +/* This will get enabled once we have the board stuff merged for this + specific case */ + +static const struct baud_table brg13_ext[] = { + { 300, MAX3107_BRG13_B300 }, + { 600, MAX3107_BRG13_B600 }, + { 1200, MAX3107_BRG13_B1200 }, + { 2400, MAX3107_BRG13_B2400 }, + { 4800, MAX3107_BRG13_B4800 }, + { 9600, MAX3107_BRG13_B9600 }, + { 19200, MAX3107_BRG13_B19200 }, + { 57600, MAX3107_BRG13_B57600 }, + { 115200, MAX3107_BRG13_B115200 }, + { 230400, MAX3107_BRG13_B230400 }, + { 460800, MAX3107_BRG13_B460800 }, + { 921600, MAX3107_BRG13_B921600 }, + { 0, 0 } +}; + +static void max3107_aava_init(struct max3107_port *s) +{ + /*override for AAVA SC specific*/ + if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) { + if (get_koski_build_id() <= KOSKI_EV2) + if (s->ext_clk) { + s->brg_cfg = MAX3107_BRG13_B9600; + s->baud_tbl = (struct baud_table *)brg13_ext; + } + } +} +#endif + +static int __devexit max3107_aava_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + /* Remove GPIO chip */ + if (gpiochip_remove(&s->chip)) + dev_warn(&spi->dev, "Removing GPIO chip failed\n"); + + /* Then do the default remove */ + return max3107_remove(spi); +} + +/* Platform data */ +static struct max3107_plat aava_plat_data = { + .loopback = 0, + .ext_clk = 1, +/* .init = max3107_aava_init, */ + .configure = max3107_aava_configure, + .hw_suspend = max3107_hw_susp, + .polled_mode = 0, + .poll_time = 0, +}; + + +static int __devinit max3107_probe_aava(struct spi_device *spi) +{ + int err = max3107_aava_reset(spi); + if (err < 0) + return err; + return max3107_probe(spi, &aava_plat_data); +} + +/* Spi driver data */ +static struct spi_driver max3107_driver = { + .driver = { + .name = "aava-max3107", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = max3107_probe_aava, + .remove = __devexit_p(max3107_aava_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; + +/* Driver init function */ +static int __init max3107_init(void) +{ + return spi_register_driver(&max3107_driver); +} + +/* Driver exit function */ +static void __exit max3107_exit(void) +{ + spi_unregister_driver(&max3107_driver); +} + +module_init(max3107_init); +module_exit(max3107_exit); + +MODULE_DESCRIPTION("MAX3107 driver"); +MODULE_AUTHOR("Aavamobile"); +MODULE_ALIAS("aava-max3107-spi"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/max3107.c b/drivers/tty/serial/max3107.c new file mode 100644 index 000000000000..910870edf708 --- /dev/null +++ b/drivers/tty/serial/max3107.c @@ -0,0 +1,1213 @@ +/* + * max3107.c - spi uart protocol driver for Maxim 3107 + * Based on max3100.c + * by Christian Pellegrin + * and max3110.c + * by Feng Tang + * + * Copyright (C) Aavamobile 2009 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 "max3107.h" + +static const struct baud_table brg26_ext[] = { + { 300, MAX3107_BRG26_B300 }, + { 600, MAX3107_BRG26_B600 }, + { 1200, MAX3107_BRG26_B1200 }, + { 2400, MAX3107_BRG26_B2400 }, + { 4800, MAX3107_BRG26_B4800 }, + { 9600, MAX3107_BRG26_B9600 }, + { 19200, MAX3107_BRG26_B19200 }, + { 57600, MAX3107_BRG26_B57600 }, + { 115200, MAX3107_BRG26_B115200 }, + { 230400, MAX3107_BRG26_B230400 }, + { 460800, MAX3107_BRG26_B460800 }, + { 921600, MAX3107_BRG26_B921600 }, + { 0, 0 } +}; + +static const struct baud_table brg13_int[] = { + { 300, MAX3107_BRG13_IB300 }, + { 600, MAX3107_BRG13_IB600 }, + { 1200, MAX3107_BRG13_IB1200 }, + { 2400, MAX3107_BRG13_IB2400 }, + { 4800, MAX3107_BRG13_IB4800 }, + { 9600, MAX3107_BRG13_IB9600 }, + { 19200, MAX3107_BRG13_IB19200 }, + { 57600, MAX3107_BRG13_IB57600 }, + { 115200, MAX3107_BRG13_IB115200 }, + { 230400, MAX3107_BRG13_IB230400 }, + { 460800, MAX3107_BRG13_IB460800 }, + { 921600, MAX3107_BRG13_IB921600 }, + { 0, 0 } +}; + +static u32 get_new_brg(int baud, struct max3107_port *s) +{ + int i; + const struct baud_table *baud_tbl = s->baud_tbl; + + for (i = 0; i < 13; i++) { + if (baud == baud_tbl[i].baud) + return baud_tbl[i].new_brg; + } + + return 0; +} + +/* Perform SPI transfer for write/read of device register(s) */ +int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len) +{ + struct spi_message spi_msg; + struct spi_transfer spi_xfer; + + /* Initialize SPI ,message */ + spi_message_init(&spi_msg); + + /* Initialize SPI transfer */ + memset(&spi_xfer, 0, sizeof spi_xfer); + spi_xfer.len = len; + spi_xfer.tx_buf = tx; + spi_xfer.rx_buf = rx; + spi_xfer.speed_hz = MAX3107_SPI_SPEED; + + /* Add SPI transfer to SPI message */ + spi_message_add_tail(&spi_xfer, &spi_msg); + +#ifdef DBG_TRACE_SPI_DATA + { + int i; + pr_info("tx len %d:\n", spi_xfer.len); + for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) + pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]); + pr_info("\n"); + } +#endif + + /* Perform synchronous SPI transfer */ + if (spi_sync(s->spi, &spi_msg)) { + dev_err(&s->spi->dev, "spi_sync failure\n"); + return -EIO; + } + +#ifdef DBG_TRACE_SPI_DATA + if (spi_xfer.rx_buf) { + int i; + pr_info("rx len %d:\n", spi_xfer.len); + for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) + pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]); + pr_info("\n"); + } +#endif + return 0; +} +EXPORT_SYMBOL_GPL(max3107_rw); + +/* Puts received data to circular buffer */ +static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data, + int len) +{ + struct uart_port *port = &s->port; + struct tty_struct *tty; + + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + /* Insert received data */ + tty_insert_flip_string(tty, data, len); + /* Update RX counter */ + port->icount.rx += len; +} + +/* Handle data receiving */ +static void max3107_handlerx(struct max3107_port *s, u16 rxlvl) +{ + int i; + int j; + int len; /* SPI transfer buffer length */ + u16 *buf; + u8 *valid_str; + + if (!s->rx_enabled) + /* RX is disabled */ + return; + + if (rxlvl == 0) { + /* RX fifo is empty */ + return; + } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) { + dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl); + /* Ensure sanity of RX level */ + rxlvl = MAX3107_RX_FIFO_SIZE; + } + if ((s->rxbuf == 0) || (s->rxstr == 0)) { + dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n"); + return; + } + buf = s->rxbuf; + valid_str = s->rxstr; + while (rxlvl) { + pr_debug("rxlvl %d\n", rxlvl); + /* Clear buffer */ + memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2)); + len = 0; + if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) { + /* First disable RX FIFO interrupt */ + pr_debug("Disabling RX INT\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT; + buf[0] |= s->irqen_reg; + len++; + } + /* Just increase the length by amount of words in FIFO since + * buffer was zeroed and SPI transfer of 0x0000 means reading + * from RX FIFO + */ + len += rxlvl; + /* Append RX level query */ + buf[len] = MAX3107_RXFIFOLVL_REG; + len++; + + /* Perform the SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) { + dev_err(&s->spi->dev, "SPI transfer for RX h failed\n"); + return; + } + + /* Skip RX FIFO interrupt disabling word if it was added */ + j = ((len - 1) - rxlvl); + /* Read received words */ + for (i = 0; i < rxlvl; i++, j++) + valid_str[i] = (u8)buf[j]; + put_data_to_circ_buf(s, valid_str, rxlvl); + /* Get new RX level */ + rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK); + } + + if (s->rx_enabled) { + /* RX still enabled, re-enable RX FIFO interrupt */ + pr_debug("Enabling RX INT\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; + buf[0] |= s->irqen_reg; + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n"); + } + + /* Push the received data to receivers */ + if (s->port.state->port.tty) + tty_flip_buffer_push(s->port.state->port.tty); +} + + +/* Handle data sending */ +static void max3107_handletx(struct max3107_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + int i; + unsigned long flags; + int len; /* SPI transfer buffer length */ + u16 *buf; + + if (!s->tx_fifo_empty) + /* Don't send more data before previous data is sent */ + return; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + /* No data to send or TX is stopped */ + return; + + if (!s->txbuf) { + dev_warn(&s->spi->dev, "Txbuf isn't ready\n"); + return; + } + buf = s->txbuf; + /* Get length of data pending in circular buffer */ + len = uart_circ_chars_pending(xmit); + if (len) { + /* Limit to size of TX FIFO */ + if (len > MAX3107_TX_FIFO_SIZE) + len = MAX3107_TX_FIFO_SIZE; + + pr_debug("txlen %d\n", len); + + /* Update TX counter */ + s->port.icount.tx += len; + + /* TX FIFO will no longer be empty */ + s->tx_fifo_empty = 0; + + i = 0; + if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) { + /* First disable TX empty interrupt */ + pr_debug("Disabling TE INT\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT; + buf[i] |= s->irqen_reg; + i++; + len++; + } + /* Add data to send */ + spin_lock_irqsave(&s->port.lock, flags); + for ( ; i < len ; i++) { + buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG); + buf[i] |= ((u16)xmit->buf[xmit->tail] & + MAX3107_SPI_TX_DATA_MASK); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&s->port.lock, flags); + if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) { + /* Enable TX empty interrupt */ + pr_debug("Enabling TE INT\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT; + buf[i] |= s->irqen_reg; + i++; + len++; + } + if (!s->tx_enabled) { + /* Enable TX */ + pr_debug("Enable TX\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock_irqsave(&s->data_lock, flags); + s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT; + buf[i] |= s->mode1_reg; + spin_unlock_irqrestore(&s->data_lock, flags); + s->tx_enabled = 1; + i++; + len++; + } + + /* Perform the SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, len*2)) { + dev_err(&s->spi->dev, + "SPI transfer TX handling failed\n"); + return; + } + } + + /* Indicate wake up if circular buffer is getting low on data */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + +} + +/* Handle interrupts + * Also reads and returns current RX FIFO level + */ +static u16 handle_interrupt(struct max3107_port *s) +{ + u16 buf[4]; /* Buffer for SPI transfers */ + u8 irq_status; + u16 rx_level; + unsigned long flags; + + /* Read IRQ status register */ + buf[0] = MAX3107_IRQSTS_REG; + /* Read status IRQ status register */ + buf[1] = MAX3107_STS_IRQSTS_REG; + /* Read LSR IRQ status register */ + buf[2] = MAX3107_LSR_IRQSTS_REG; + /* Query RX level */ + buf[3] = MAX3107_RXFIFOLVL_REG; + + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) { + dev_err(&s->spi->dev, + "SPI transfer for INTR handling failed\n"); + return 0; + } + + irq_status = (u8)buf[0]; + pr_debug("IRQSTS %x\n", irq_status); + rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK); + + if (irq_status & MAX3107_IRQ_LSR_BIT) { + /* LSR interrupt */ + if (buf[2] & MAX3107_LSR_RXTO_BIT) + /* RX timeout interrupt, + * handled by normal RX handling + */ + pr_debug("RX TO INT\n"); + } + + if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) { + /* Tx empty interrupt, + * disable TX and set tx_fifo_empty flag + */ + pr_debug("TE INT, disabling TX\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock_irqsave(&s->data_lock, flags); + s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; + buf[0] |= s->mode1_reg; + spin_unlock_irqrestore(&s->data_lock, flags); + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer TX dis failed\n"); + s->tx_enabled = 0; + s->tx_fifo_empty = 1; + } + + if (irq_status & MAX3107_IRQ_RXFIFO_BIT) + /* RX FIFO interrupt, + * handled by normal RX handling + */ + pr_debug("RFIFO INT\n"); + + /* Return RX level */ + return rx_level; +} + +/* Trigger work thread*/ +static void max3107_dowork(struct max3107_port *s) +{ + if (!work_pending(&s->work) && !freezing(current) && !s->suspended) + queue_work(s->workqueue, &s->work); + else + dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n"); +} + +/* Work thread */ +static void max3107_work(struct work_struct *w) +{ + struct max3107_port *s = container_of(w, struct max3107_port, work); + u16 rxlvl = 0; + int len; /* SPI transfer buffer length */ + u16 buf[5]; /* Buffer for SPI transfers */ + unsigned long flags; + + /* Start by reading current RX FIFO level */ + buf[0] = MAX3107_RXFIFOLVL_REG; + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer RX lev failed\n"); + rxlvl = 0; + } else { + rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK); + } + + do { + pr_debug("rxlvl %d\n", rxlvl); + + /* Handle RX */ + max3107_handlerx(s, rxlvl); + rxlvl = 0; + + if (s->handle_irq) { + /* Handle pending interrupts + * We also get new RX FIFO level since new data may + * have been received while pushing received data to + * receivers + */ + s->handle_irq = 0; + rxlvl = handle_interrupt(s); + } + + /* Handle TX */ + max3107_handletx(s); + + /* Handle configuration changes */ + len = 0; + spin_lock_irqsave(&s->data_lock, flags); + if (s->mode1_commit) { + pr_debug("mode1_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + buf[len++] |= s->mode1_reg; + s->mode1_commit = 0; + } + if (s->lcr_commit) { + pr_debug("lcr_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); + buf[len++] |= s->lcr_reg; + s->lcr_commit = 0; + } + if (s->brg_commit) { + pr_debug("brg_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); + buf[len++] |= ((s->brg_cfg >> 16) & + MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); + buf[len++] |= ((s->brg_cfg >> 8) & + MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); + buf[len++] |= ((s->brg_cfg) & 0xff); + s->brg_commit = 0; + } + spin_unlock_irqrestore(&s->data_lock, flags); + + if (len > 0) { + if (max3107_rw(s, (u8 *)buf, NULL, len * 2)) + dev_err(&s->spi->dev, + "SPI transfer config failed\n"); + } + + /* Reloop if interrupt handling indicated data in RX FIFO */ + } while (rxlvl); + +} + +/* Set sleep mode */ +static void max3107_set_sleep(struct max3107_port *s, int mode) +{ + u16 buf[1]; /* Buffer for SPI transfer */ + unsigned long flags; + pr_debug("enter, mode %d\n", mode); + + buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock_irqsave(&s->data_lock, flags); + switch (mode) { + case MAX3107_DISABLE_FORCED_SLEEP: + s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_ENABLE_FORCED_SLEEP: + s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_DISABLE_AUTOSLEEP: + s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT; + break; + case MAX3107_ENABLE_AUTOSLEEP: + s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT; + break; + default: + spin_unlock_irqrestore(&s->data_lock, flags); + dev_warn(&s->spi->dev, "invalid sleep mode\n"); + return; + } + buf[0] |= s->mode1_reg; + spin_unlock_irqrestore(&s->data_lock, flags); + + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n"); + + if (mode == MAX3107_DISABLE_AUTOSLEEP || + mode == MAX3107_DISABLE_FORCED_SLEEP) + msleep(MAX3107_WAKEUP_DELAY); +} + +/* Perform full register initialization */ +static void max3107_register_init(struct max3107_port *s) +{ + u16 buf[11]; /* Buffer for SPI transfers */ + + /* 1. Configure baud rate, 9600 as default */ + s->baud = 9600; + /* the below is default*/ + if (s->ext_clk) { + s->brg_cfg = MAX3107_BRG26_B9600; + s->baud_tbl = (struct baud_table *)brg26_ext; + } else { + s->brg_cfg = MAX3107_BRG13_IB9600; + s->baud_tbl = (struct baud_table *)brg13_int; + } + + if (s->pdata->init) + s->pdata->init(s); + + buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG) + | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK); + buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG) + | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK); + buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG) + | ((s->brg_cfg) & 0xff); + + /* 2. Configure LCR register, 8N1 mode by default */ + s->lcr_reg = MAX3107_LCR_WORD_LEN_8; + buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG) + | s->lcr_reg; + + /* 3. Configure MODE 1 register */ + s->mode1_reg = 0; + /* Enable IRQ pin */ + s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT; + /* Disable TX */ + s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; + s->tx_enabled = 0; + /* RX is enabled */ + s->rx_enabled = 1; + buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG) + | s->mode1_reg; + + /* 4. Configure MODE 2 register */ + buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); + if (s->loopback) { + /* Enable loopback */ + buf[5] |= MAX3107_MODE2_LOOPBACK_BIT; + } + /* Reset FIFOs */ + buf[5] |= MAX3107_MODE2_FIFORST_BIT; + s->tx_fifo_empty = 1; + + /* 5. Configure FIFO trigger level register */ + buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG); + /* RX FIFO trigger for 16 words, TX FIFO trigger not used */ + buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0)); + + /* 6. Configure flow control levels */ + buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG); + /* Flow control halt level 96, resume level 48 */ + buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96)); + + /* 7. Configure flow control */ + buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG); + /* Enable auto CTS and auto RTS flow control */ + buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT); + + /* 8. Configure RX timeout register */ + buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG); + /* Timeout after 48 character intervals */ + buf[9] |= 0x0030; + + /* 9. Configure LSR interrupt enable register */ + buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG); + /* Enable RX timeout interrupt */ + buf[10] |= MAX3107_LSR_RXTO_BIT; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 22)) + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + + /* 10. Clear IRQ status register by reading it */ + buf[0] = MAX3107_IRQSTS_REG; + + /* 11. Configure interrupt enable register */ + /* Enable LSR interrupt */ + s->irqen_reg = MAX3107_IRQ_LSR_BIT; + /* Enable RX FIFO interrupt */ + s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; + buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG) + | s->irqen_reg; + + /* 12. Clear FIFO reset that was set in step 6 */ + buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); + if (s->loopback) { + /* Keep loopback enabled */ + buf[2] |= MAX3107_MODE2_LOOPBACK_BIT; + } + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6)) + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + +} + +/* IRQ handler */ +static irqreturn_t max3107_irq(int irqno, void *dev_id) +{ + struct max3107_port *s = dev_id; + + if (irqno != s->spi->irq) { + /* Unexpected IRQ */ + return IRQ_NONE; + } + + /* Indicate irq */ + s->handle_irq = 1; + + /* Trigger work thread */ + max3107_dowork(s); + + return IRQ_HANDLED; +} + +/* HW suspension function + * + * Currently autosleep is used to decrease current consumption, alternative + * approach would be to set the chip to reset mode if UART is not being + * used but that would mess the GPIOs + * + */ +void max3107_hw_susp(struct max3107_port *s, int suspend) +{ + pr_debug("enter, suspend %d\n", suspend); + + if (suspend) { + /* Suspend requested, + * enable autosleep to decrease current consumption + */ + s->suspended = 1; + max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP); + } else { + /* Resume requested, + * disable autosleep + */ + s->suspended = 0; + max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP); + } +} +EXPORT_SYMBOL_GPL(max3107_hw_susp); + +/* Modem status IRQ enabling */ +static void max3107_enable_ms(struct uart_port *port) +{ + /* Modem status not supported */ +} + +/* Data send function */ +static void max3107_start_tx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + /* Trigger work thread for sending data */ + max3107_dowork(s); +} + +/* Function for checking that there is no pending transfers */ +static unsigned int max3107_tx_empty(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + pr_debug("returning %d\n", + (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit))); + return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit); +} + +/* Function for stopping RX */ +static void max3107_stop_rx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + unsigned long flags; + + /* Set RX disabled in MODE 1 register */ + spin_lock_irqsave(&s->data_lock, flags); + s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT; + s->mode1_commit = 1; + spin_unlock_irqrestore(&s->data_lock, flags); + /* Set RX disabled */ + s->rx_enabled = 0; + /* Trigger work thread for doing the actual configuration change */ + max3107_dowork(s); +} + +/* Function for returning control pin states */ +static unsigned int max3107_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +/* Function for setting control pin states */ +static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* DCD and DSR are not wired and CTS/RTS is hadnled automatically + * so do nothing + */ +} + +/* Function for configuring UART parameters */ +static void max3107_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + struct tty_struct *tty; + int baud; + u16 new_lcr = 0; + u32 new_brg = 0; + unsigned long flags; + + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + /* Get new LCR register values */ + /* Word size */ + if ((termios->c_cflag & CSIZE) == CS7) + new_lcr |= MAX3107_LCR_WORD_LEN_7; + else + new_lcr |= MAX3107_LCR_WORD_LEN_8; + + /* Parity */ + if (termios->c_cflag & PARENB) { + new_lcr |= MAX3107_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + new_lcr |= MAX3107_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) { + /* 2 stop bits */ + new_lcr |= MAX3107_LCR_STOPLEN_BIT; + } + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Set status ignore mask */ + s->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + s->port.ignore_status_mask |= MAX3107_ALL_ERRORS; + + /* Set low latency to immediately handle pushed data */ + s->port.state->port.tty->low_latency = 1; + + /* Get new baud rate generator configuration */ + baud = tty_get_baud_rate(tty); + + spin_lock_irqsave(&s->data_lock, flags); + new_brg = get_new_brg(baud, s); + /* if can't find the corrent config, use previous */ + if (!new_brg) { + baud = s->baud; + new_brg = s->brg_cfg; + } + spin_unlock_irqrestore(&s->data_lock, flags); + tty_termios_encode_baud_rate(termios, baud, baud); + s->baud = baud; + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_lock_irqsave(&s->data_lock, flags); + if (s->lcr_reg != new_lcr) { + s->lcr_reg = new_lcr; + s->lcr_commit = 1; + } + if (s->brg_cfg != new_brg) { + s->brg_cfg = new_brg; + s->brg_commit = 1; + } + spin_unlock_irqrestore(&s->data_lock, flags); + + /* Trigger work thread for doing the actual configuration change */ + max3107_dowork(s); +} + +/* Port shutdown function */ +static void max3107_shutdown(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + if (s->suspended && s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 0); + + /* Free the interrupt */ + free_irq(s->spi->irq, s); + + if (s->workqueue) { + /* Flush and destroy work queue */ + flush_workqueue(s->workqueue); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + } + + /* Suspend HW */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 1); +} + +/* Port startup function */ +static int max3107_startup(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + /* Initialize work queue */ + s->workqueue = create_freezeable_workqueue("max3107"); + if (!s->workqueue) { + dev_err(&s->spi->dev, "Workqueue creation failed\n"); + return -EBUSY; + } + INIT_WORK(&s->work, max3107_work); + + /* Setup IRQ */ + if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING, + "max3107", s)) { + dev_err(&s->spi->dev, "IRQ reguest failed\n"); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + return -EBUSY; + } + + /* Resume HW */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 0); + + /* Init registers */ + max3107_register_init(s); + + return 0; +} + +/* Port type function */ +static const char *max3107_type(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + return s->spi->modalias; +} + +/* Port release function */ +static void max3107_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +/* Port request function */ +static int max3107_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +/* Port config function */ +static void max3107_config_port(struct uart_port *port, int flags) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + s->port.type = PORT_MAX3107; +} + +/* Port verify function */ +static int max3107_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107) + return 0; + + return -EINVAL; +} + +/* Port stop TX function */ +static void max3107_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +/* Port break control function */ +static void max3107_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support break control, do nothing */ +} + + +/* Port functions */ +static struct uart_ops max3107_ops = { + .tx_empty = max3107_tx_empty, + .set_mctrl = max3107_set_mctrl, + .get_mctrl = max3107_get_mctrl, + .stop_tx = max3107_stop_tx, + .start_tx = max3107_start_tx, + .stop_rx = max3107_stop_rx, + .enable_ms = max3107_enable_ms, + .break_ctl = max3107_break_ctl, + .startup = max3107_startup, + .shutdown = max3107_shutdown, + .set_termios = max3107_set_termios, + .type = max3107_type, + .release_port = max3107_release_port, + .request_port = max3107_request_port, + .config_port = max3107_config_port, + .verify_port = max3107_verify_port, +}; + +/* UART driver data */ +static struct uart_driver max3107_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyMAX", + .dev_name = "ttyMAX", + .nr = 1, +}; + +static int driver_registered = 0; + + + +/* 'Generic' platform data */ +static struct max3107_plat generic_plat_data = { + .loopback = 0, + .ext_clk = 1, + .hw_suspend = max3107_hw_susp, + .polled_mode = 0, + .poll_time = 0, +}; + + +/*******************************************************************/ + +/** + * max3107_probe - SPI bus probe entry point + * @spi: the spi device + * + * SPI wants us to probe this device and if appropriate claim it. + * Perform any platform specific requirements and then initialise + * the device. + */ + +int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata) +{ + struct max3107_port *s; + u16 buf[2]; /* Buffer for SPI transfers */ + int retval; + + pr_info("enter max3107 probe\n"); + + /* Allocate port structure */ + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + pr_err("Allocating port structure failed\n"); + return -ENOMEM; + } + + s->pdata = pdata; + + /* SPI Rx buffer + * +2 for RX FIFO interrupt + * disabling and RX level query + */ + s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL); + if (!s->rxbuf) { + pr_err("Allocating RX buffer failed\n"); + retval = -ENOMEM; + goto err_free4; + } + s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL); + if (!s->rxstr) { + pr_err("Allocating RX buffer failed\n"); + retval = -ENOMEM; + goto err_free3; + } + /* SPI Tx buffer + * SPI transfer buffer + * +3 for TX FIFO empty + * interrupt disabling and + * enabling and TX enabling + */ + s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL); + if (!s->txbuf) { + pr_err("Allocating TX buffer failed\n"); + retval = -ENOMEM; + goto err_free2; + } + /* Initialize shared data lock */ + spin_lock_init(&s->data_lock); + + /* SPI intializations */ + dev_set_drvdata(&spi->dev, s); + spi->mode = SPI_MODE_0; + spi->dev.platform_data = pdata; + spi->bits_per_word = 16; + s->ext_clk = pdata->ext_clk; + s->loopback = pdata->loopback; + spi_setup(spi); + s->spi = spi; + + /* Check REV ID to ensure we are talking to what we expect */ + buf[0] = MAX3107_REVID_REG; + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n"); + retval = -EIO; + goto err_free1; + } + if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 && + (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) { + dev_err(&s->spi->dev, "REVID %x does not match\n", + (buf[0] & MAX3107_SPI_RX_DATA_MASK)); + retval = -ENODEV; + goto err_free1; + } + + /* Disable all interrupts */ + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000); + buf[0] |= 0x0000; + + /* Configure clock source */ + buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG); + if (s->ext_clk) { + /* External clock */ + buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT; + } + + /* PLL bypass ON */ + buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + retval = -EIO; + goto err_free1; + } + + /* Register UART driver */ + if (!driver_registered) { + retval = uart_register_driver(&max3107_uart_driver); + if (retval) { + dev_err(&s->spi->dev, "Registering UART driver failed\n"); + goto err_free1; + } + driver_registered = 1; + } + + /* Initialize UART port data */ + s->port.fifosize = 128; + s->port.ops = &max3107_ops; + s->port.line = 0; + s->port.dev = &spi->dev; + s->port.uartclk = 9600; + s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + s->port.irq = s->spi->irq; + s->port.type = PORT_MAX3107; + + /* Add UART port */ + retval = uart_add_one_port(&max3107_uart_driver, &s->port); + if (retval < 0) { + dev_err(&s->spi->dev, "Adding UART port failed\n"); + goto err_free1; + } + + if (pdata->configure) { + retval = pdata->configure(s); + if (retval < 0) + goto err_free1; + } + + /* Go to suspend mode */ + if (pdata->hw_suspend) + pdata->hw_suspend(s, 1); + + return 0; + +err_free1: + kfree(s->txbuf); +err_free2: + kfree(s->rxstr); +err_free3: + kfree(s->rxbuf); +err_free4: + kfree(s); + return retval; +} +EXPORT_SYMBOL_GPL(max3107_probe); + +/* Driver remove function */ +int max3107_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + pr_info("enter max3107 remove\n"); + + /* Remove port */ + if (uart_remove_one_port(&max3107_uart_driver, &s->port)) + dev_warn(&s->spi->dev, "Removing UART port failed\n"); + + + /* Free TxRx buffer */ + kfree(s->rxbuf); + kfree(s->rxstr); + kfree(s->txbuf); + + /* Free port structure */ + kfree(s); + + return 0; +} +EXPORT_SYMBOL_GPL(max3107_remove); + +/* Driver suspend function */ +int max3107_suspend(struct spi_device *spi, pm_message_t state) +{ +#ifdef CONFIG_PM + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + pr_debug("enter suspend\n"); + + /* Suspend UART port */ + uart_suspend_port(&max3107_uart_driver, &s->port); + + /* Go to suspend mode */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 1); +#endif /* CONFIG_PM */ + return 0; +} +EXPORT_SYMBOL_GPL(max3107_suspend); + +/* Driver resume function */ +int max3107_resume(struct spi_device *spi) +{ +#ifdef CONFIG_PM + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + pr_debug("enter resume\n"); + + /* Resume from suspend */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 0); + + /* Resume UART port */ + uart_resume_port(&max3107_uart_driver, &s->port); +#endif /* CONFIG_PM */ + return 0; +} +EXPORT_SYMBOL_GPL(max3107_resume); + +static int max3107_probe_generic(struct spi_device *spi) +{ + return max3107_probe(spi, &generic_plat_data); +} + +/* Spi driver data */ +static struct spi_driver max3107_driver = { + .driver = { + .name = "max3107", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = max3107_probe_generic, + .remove = __devexit_p(max3107_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; + +/* Driver init function */ +static int __init max3107_init(void) +{ + pr_info("enter max3107 init\n"); + return spi_register_driver(&max3107_driver); +} + +/* Driver exit function */ +static void __exit max3107_exit(void) +{ + pr_info("enter max3107 exit\n"); + /* Unregister UART driver */ + if (driver_registered) + uart_unregister_driver(&max3107_uart_driver); + spi_unregister_driver(&max3107_driver); +} + +module_init(max3107_init); +module_exit(max3107_exit); + +MODULE_DESCRIPTION("MAX3107 driver"); +MODULE_AUTHOR("Aavamobile"); +MODULE_ALIAS("max3107-spi"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/max3107.h b/drivers/tty/serial/max3107.h new file mode 100644 index 000000000000..7ab632392502 --- /dev/null +++ b/drivers/tty/serial/max3107.h @@ -0,0 +1,441 @@ +/* + * max3107.h - spi uart protocol driver header for Maxim 3107 + * + * Copyright (C) Aavamobile 2009 + * Based on serial_max3100.h by Christian Pellegrin + * + * 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 _MAX3107_H +#define _MAX3107_H + +/* Serial error status definitions */ +#define MAX3107_PARITY_ERROR 1 +#define MAX3107_FRAME_ERROR 2 +#define MAX3107_OVERRUN_ERROR 4 +#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \ + MAX3107_FRAME_ERROR | \ + MAX3107_OVERRUN_ERROR) + +/* GPIO definitions */ +#define MAX3107_GPIO_BASE 88 +#define MAX3107_GPIO_COUNT 4 + + +/* GPIO connected to chip's reset pin */ +#define MAX3107_RESET_GPIO 87 + + +/* Chip reset delay */ +#define MAX3107_RESET_DELAY 10 + +/* Chip wakeup delay */ +#define MAX3107_WAKEUP_DELAY 50 + + +/* Sleep mode definitions */ +#define MAX3107_DISABLE_FORCED_SLEEP 0 +#define MAX3107_ENABLE_FORCED_SLEEP 1 +#define MAX3107_DISABLE_AUTOSLEEP 2 +#define MAX3107_ENABLE_AUTOSLEEP 3 + + +/* Definitions for register access with SPI transfers + * + * SPI transfer format: + * + * Master to slave bits xzzzzzzzyyyyyyyy + * Slave to master bits aaaaaaaabbbbbbbb + * + * where: + * x = 0 for reads, 1 for writes + * z = register address + * y = new register value if write, 0 if read + * a = unspecified + * b = register value if read, unspecified if write + */ + +/* SPI speed */ +#define MAX3107_SPI_SPEED (3125000 * 2) + +/* Write bit */ +#define MAX3107_WRITE_BIT (1 << 15) + +/* SPI TX data mask */ +#define MAX3107_SPI_RX_DATA_MASK (0x00ff) + +/* SPI RX data mask */ +#define MAX3107_SPI_TX_DATA_MASK (0x00ff) + +/* Register access masks */ +#define MAX3107_RHR_REG (0x0000) /* RX FIFO */ +#define MAX3107_THR_REG (0x0000) /* TX FIFO */ +#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */ +#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */ +#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */ +#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */ +#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */ +#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */ +#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */ +#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */ +#define MAX3107_MODE1_REG (0x0900) /* MODE1 */ +#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */ +#define MAX3107_LCR_REG (0x0b00) /* LCR */ +#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */ +#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */ +#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */ +#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */ +#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */ +#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */ +#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */ +#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */ +#define MAX3107_XON1_REG (0x1400) /* XON1 character */ +#define MAX3107_XON2_REG (0x1500) /* XON2 character */ +#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */ +#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */ +#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */ +#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */ +#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */ +#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */ +#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */ +#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */ +#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */ +#define MAX3107_REVID_REG (0x1f00) /* Revision identification */ + +/* IRQ register bits */ +#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ +#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ +#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ +#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ +#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ +#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ +#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ +#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ + +/* LSR register bits */ +#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ +#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ +#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ +#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ +#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ +#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ +#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ + +/* Special character register bits */ +#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ +#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ +#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ +#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ +#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ +#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ +#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Status register bits */ +#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ +#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ +#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ +#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ +#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */ +#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ +#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ +#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* MODE1 register bits */ +#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ +#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ +#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ +#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ +#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ +#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ +#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ +#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ + +/* MODE2 register bits */ +#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ +#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ +#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ +#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ +#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ +#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ +#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ +#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ + +/* LCR register bits */ +#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ +#define MAX3107_LCR_WORD_LEN_5 (0x0000) +#define MAX3107_LCR_WORD_LEN_6 (0x0001) +#define MAX3107_LCR_WORD_LEN_7 (0x0002) +#define MAX3107_LCR_WORD_LEN_8 (0x0003) + + +/* IRDA register bits */ +#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ +#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ +#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ +#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ +#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ +#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Flow control trigger level register masks */ +#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ +#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f) +#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4) + +/* FIFO interrupt trigger level register masks */ +#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f) +#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4) + +/* Flow control register bits */ +#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs + * are used in conjunction with + * XOFF2 for definition of + * special character */ +#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ +#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ +#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 + * + * SWFLOW bits 1 & 0 table: + * 00 -> no transmitter flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * and controls + * transmitter + * 10 -> receiver compares + * XON1 and XOFF1 + * and controls + * transmitter + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 and controls + * transmitter + */ +#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ +#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* GPIO configuration register bits */ +#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ +#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ +#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ +#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ +#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ +#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ +#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ +#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ + +/* GPIO DATA register bits */ +#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ +#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ +#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ +#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ +#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ +#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ +#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ +#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ + +/* PLL configuration register masks */ +#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */ +#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */ + +/* Baud rate generator configuration register masks and bits */ +#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of + * Baud rate generator divisor + */ +#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ +#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ +#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Clock source register bits */ +#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */ +#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ +#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ +#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ +#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ +#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */ +#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ + + +/* HW definitions */ +#define MAX3107_RX_FIFO_SIZE 128 +#define MAX3107_TX_FIFO_SIZE 128 +#define MAX3107_REVID1 0x00a0 +#define MAX3107_REVID2 0x00a1 + + +/* Baud rate generator configuration values for external clock 13MHz */ +#define MAX3107_BRG13_B300 (0x0A9400 | 0x05) +#define MAX3107_BRG13_B600 (0x054A00 | 0x03) +#define MAX3107_BRG13_B1200 (0x02A500 | 0x01) +#define MAX3107_BRG13_B2400 (0x015200 | 0x09) +#define MAX3107_BRG13_B4800 (0x00A900 | 0x04) +#define MAX3107_BRG13_B9600 (0x005400 | 0x0A) +#define MAX3107_BRG13_B19200 (0x002A00 | 0x05) +#define MAX3107_BRG13_B38400 (0x001500 | 0x03) +#define MAX3107_BRG13_B57600 (0x000E00 | 0x02) +#define MAX3107_BRG13_B115200 (0x000700 | 0x01) +#define MAX3107_BRG13_B230400 (0x000300 | 0x08) +#define MAX3107_BRG13_B460800 (0x000100 | 0x0c) +#define MAX3107_BRG13_B921600 (0x000100 | 0x1c) + +/* Baud rate generator configuration values for external clock 26MHz */ +#define MAX3107_BRG26_B300 (0x152800 | 0x0A) +#define MAX3107_BRG26_B600 (0x0A9400 | 0x05) +#define MAX3107_BRG26_B1200 (0x054A00 | 0x03) +#define MAX3107_BRG26_B2400 (0x02A500 | 0x01) +#define MAX3107_BRG26_B4800 (0x015200 | 0x09) +#define MAX3107_BRG26_B9600 (0x00A900 | 0x04) +#define MAX3107_BRG26_B19200 (0x005400 | 0x0A) +#define MAX3107_BRG26_B38400 (0x002A00 | 0x05) +#define MAX3107_BRG26_B57600 (0x001C00 | 0x03) +#define MAX3107_BRG26_B115200 (0x000E00 | 0x02) +#define MAX3107_BRG26_B230400 (0x000700 | 0x01) +#define MAX3107_BRG26_B460800 (0x000300 | 0x08) +#define MAX3107_BRG26_B921600 (0x000100 | 0x0C) + +/* Baud rate generator configuration values for internal clock */ +#define MAX3107_BRG13_IB300 (0x008000 | 0x00) +#define MAX3107_BRG13_IB600 (0x004000 | 0x00) +#define MAX3107_BRG13_IB1200 (0x002000 | 0x00) +#define MAX3107_BRG13_IB2400 (0x001000 | 0x00) +#define MAX3107_BRG13_IB4800 (0x000800 | 0x00) +#define MAX3107_BRG13_IB9600 (0x000400 | 0x00) +#define MAX3107_BRG13_IB19200 (0x000200 | 0x00) +#define MAX3107_BRG13_IB38400 (0x000100 | 0x00) +#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B) +#define MAX3107_BRG13_IB115200 (0x000000 | 0x05) +#define MAX3107_BRG13_IB230400 (0x000000 | 0x03) +#define MAX3107_BRG13_IB460800 (0x000000 | 0x00) +#define MAX3107_BRG13_IB921600 (0x000000 | 0x00) + + +struct baud_table { + int baud; + u32 new_brg; +}; + +struct max3107_port { + /* UART port structure */ + struct uart_port port; + + /* SPI device structure */ + struct spi_device *spi; + +#if defined(CONFIG_GPIOLIB) + /* GPIO chip stucture */ + struct gpio_chip chip; +#endif + + /* Workqueue that does all the magic */ + struct workqueue_struct *workqueue; + struct work_struct work; + + /* Lock for shared data */ + spinlock_t data_lock; + + /* Device configuration */ + int ext_clk; /* 1 if external clock used */ + int loopback; /* Current loopback mode state */ + int baud; /* Current baud rate */ + + /* State flags */ + int suspended; /* Indicates suspend mode */ + int tx_fifo_empty; /* Flag for TX FIFO state */ + int rx_enabled; /* Flag for receiver state */ + int tx_enabled; /* Flag for transmitter state */ + + u16 irqen_reg; /* Current IRQ enable register value */ + /* Shared data */ + u16 mode1_reg; /* Current mode1 register value*/ + int mode1_commit; /* Flag for setting new mode1 register value */ + u16 lcr_reg; /* Current LCR register value */ + int lcr_commit; /* Flag for setting new LCR register value */ + u32 brg_cfg; /* Current Baud rate generator config */ + int brg_commit; /* Flag for setting new baud rate generator + * config + */ + struct baud_table *baud_tbl; + int handle_irq; /* Indicates that IRQ should be handled */ + + /* Rx buffer and str*/ + u16 *rxbuf; + u8 *rxstr; + /* Tx buffer*/ + u16 *txbuf; + + struct max3107_plat *pdata; /* Platform data */ +}; + +/* Platform data structure */ +struct max3107_plat { + /* Loopback mode enable */ + int loopback; + /* External clock enable */ + int ext_clk; + /* Called during the register initialisation */ + void (*init)(struct max3107_port *s); + /* Called when the port is found and configured */ + int (*configure)(struct max3107_port *s); + /* HW suspend function */ + void (*hw_suspend) (struct max3107_port *s, int suspend); + /* Polling mode enable */ + int polled_mode; + /* Polling period if polling mode enabled */ + int poll_time; +}; + +extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len); +extern void max3107_hw_susp(struct max3107_port *s, int suspend); +extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata); +extern int max3107_remove(struct spi_device *spi); +extern int max3107_suspend(struct spi_device *spi, pm_message_t state); +extern int max3107_resume(struct spi_device *spi); + +#endif /* _LINUX_SERIAL_MAX3107_H */ diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c new file mode 100644 index 000000000000..3394b7cc1722 --- /dev/null +++ b/drivers/tty/serial/mcf.c @@ -0,0 +1,662 @@ +/****************************************************************************/ + +/* + * mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/****************************************************************************/ + +/* + * Some boards implement the DTR/DCD lines using GPIO lines, most + * don't. Dummy out the access macros for those that don't. Those + * that do should define these macros somewhere in there board + * specific inlude files. + */ +#if !defined(mcf_getppdcd) +#define mcf_getppdcd(p) (1) +#endif +#if !defined(mcf_getppdtr) +#define mcf_getppdtr(p) (1) +#endif +#if !defined(mcf_setppdtr) +#define mcf_setppdtr(p, v) do { } while (0) +#endif + +/****************************************************************************/ + +/* + * Local per-uart structure. + */ +struct mcf_uart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned char imr; /* Local IMR mirror */ +}; + +/****************************************************************************/ + +static unsigned int mcf_tx_empty(struct uart_port *port) +{ + return (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXEMPTY) ? + TIOCSER_TEMT : 0; +} + +/****************************************************************************/ + +static unsigned int mcf_get_mctrl(struct uart_port *port) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned int sigs; + + sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ? + 0 : TIOCM_CTS; + sigs |= (pp->sigs & TIOCM_RTS); + sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0); + sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0); + + return sigs; +} + +/****************************************************************************/ + +static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + + pp->sigs = sigs; + mcf_setppdtr(port->line, (sigs & TIOCM_DTR)); + if (sigs & TIOCM_RTS) + writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); + else + writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0); +} + +/****************************************************************************/ + +static void mcf_start_tx(struct uart_port *port) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + + pp->imr |= MCFUART_UIR_TXREADY; + writeb(pp->imr, port->membase + MCFUART_UIMR); +} + +/****************************************************************************/ + +static void mcf_stop_tx(struct uart_port *port) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + + pp->imr &= ~MCFUART_UIR_TXREADY; + writeb(pp->imr, port->membase + MCFUART_UIMR); +} + +/****************************************************************************/ + +static void mcf_stop_rx(struct uart_port *port) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + + pp->imr &= ~MCFUART_UIR_RXREADY; + writeb(pp->imr, port->membase + MCFUART_UIMR); +} + +/****************************************************************************/ + +static void mcf_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR); + else + writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR); + spin_unlock_irqrestore(&port->lock, flags); +} + +/****************************************************************************/ + +static void mcf_enable_ms(struct uart_port *port) +{ +} + +/****************************************************************************/ + +static int mcf_startup(struct uart_port *port) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Reset UART, get it into known state... */ + writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); + writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); + + /* Enable the UART transmitter and receiver */ + writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, + port->membase + MCFUART_UCR); + + /* Enable RX interrupts now */ + pp->imr = MCFUART_UIR_RXREADY; + writeb(pp->imr, port->membase + MCFUART_UIMR); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +/****************************************************************************/ + +static void mcf_shutdown(struct uart_port *port) +{ + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writeb(pp->imr, port->membase + MCFUART_UIMR); + + /* Disable UART transmitter and receiver */ + writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); + writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/****************************************************************************/ + +static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; +#if defined(CONFIG_M5272) + unsigned int baudfr; +#endif + unsigned char mr1, mr2; + + baud = uart_get_baud_rate(port, termios, old, 0, 230400); +#if defined(CONFIG_M5272) + baudclk = (MCF_BUSCLK / baud) / 32; + baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16; +#else + baudclk = ((MCF_BUSCLK / baud) + 16) / 32; +#endif + + mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; + mr2 = 0; + + switch (termios->c_cflag & CSIZE) { + case CS5: mr1 |= MCFUART_MR1_CS5; break; + case CS6: mr1 |= MCFUART_MR1_CS6; break; + case CS7: mr1 |= MCFUART_MR1_CS7; break; + case CS8: + default: mr1 |= MCFUART_MR1_CS8; break; + } + + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + mr1 |= MCFUART_MR1_PARITYMARK; + else + mr1 |= MCFUART_MR1_PARITYSPACE; + } else { + if (termios->c_cflag & PARODD) + mr1 |= MCFUART_MR1_PARITYODD; + else + mr1 |= MCFUART_MR1_PARITYEVEN; + } + } else { + mr1 |= MCFUART_MR1_PARITYNONE; + } + + if (termios->c_cflag & CSTOPB) + mr2 |= MCFUART_MR2_STOP2; + else + mr2 |= MCFUART_MR2_STOP1; + + if (termios->c_cflag & CRTSCTS) { + mr1 |= MCFUART_MR1_RXRTS; + mr2 |= MCFUART_MR2_TXCTS; + } + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); + writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); + writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); + writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); + writeb(mr1, port->membase + MCFUART_UMR); + writeb(mr2, port->membase + MCFUART_UMR); + writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1); + writeb((baudclk & 0xff), port->membase + MCFUART_UBG2); +#if defined(CONFIG_M5272) + writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD); +#endif + writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER, + port->membase + MCFUART_UCSR); + writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, + port->membase + MCFUART_UCR); + spin_unlock_irqrestore(&port->lock, flags); +} + +/****************************************************************************/ + +static void mcf_rx_chars(struct mcf_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char status, ch, flag; + + while ((status = readb(port->membase + MCFUART_USR)) & MCFUART_USR_RXREADY) { + ch = readb(port->membase + MCFUART_URB); + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & MCFUART_USR_RXERR) { + writeb(MCFUART_UCR_CMDRESETERR, + port->membase + MCFUART_UCR); + + if (status & MCFUART_USR_RXBREAK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & MCFUART_USR_RXPARITY) { + port->icount.parity++; + } else if (status & MCFUART_USR_RXOVERRUN) { + port->icount.overrun++; + } else if (status & MCFUART_USR_RXFRAMING) { + port->icount.frame++; + } + + status &= port->read_status_mask; + + if (status & MCFUART_USR_RXBREAK) + flag = TTY_BREAK; + else if (status & MCFUART_USR_RXPARITY) + flag = TTY_PARITY; + else if (status & MCFUART_USR_RXFRAMING) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +/****************************************************************************/ + +static void mcf_tx_chars(struct mcf_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + /* Send special char - probably flow control */ + writeb(port->x_char, port->membase + MCFUART_UTB); + port->x_char = 0; + port->icount.tx++; + return; + } + + while (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) { + if (xmit->head == xmit->tail) + break; + writeb(xmit->buf[xmit->tail], port->membase + MCFUART_UTB); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (xmit->head == xmit->tail) { + pp->imr &= ~MCFUART_UIR_TXREADY; + writeb(pp->imr, port->membase + MCFUART_UIMR); + } +} + +/****************************************************************************/ + +static irqreturn_t mcf_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct mcf_uart *pp = container_of(port, struct mcf_uart, port); + unsigned int isr; + irqreturn_t ret = IRQ_NONE; + + isr = readb(port->membase + MCFUART_UISR) & pp->imr; + + spin_lock(&port->lock); + if (isr & MCFUART_UIR_RXREADY) { + mcf_rx_chars(pp); + ret = IRQ_HANDLED; + } + if (isr & MCFUART_UIR_TXREADY) { + mcf_tx_chars(pp); + ret = IRQ_HANDLED; + } + spin_unlock(&port->lock); + + return ret; +} + +/****************************************************************************/ + +static void mcf_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_MCF; + port->fifosize = MCFUART_TXFIFOSIZE; + + /* Clear mask, so no surprise interrupts. */ + writeb(0, port->membase + MCFUART_UIMR); + + if (request_irq(port->irq, mcf_interrupt, IRQF_DISABLED, "UART", port)) + printk(KERN_ERR "MCF: unable to attach ColdFire UART %d " + "interrupt vector=%d\n", port->line, port->irq); +} + +/****************************************************************************/ + +static const char *mcf_type(struct uart_port *port) +{ + return (port->type == PORT_MCF) ? "ColdFire UART" : NULL; +} + +/****************************************************************************/ + +static int mcf_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +/****************************************************************************/ + +static void mcf_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +/****************************************************************************/ + +static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_MCF)) + return -EINVAL; + return 0; +} + +/****************************************************************************/ + +/* + * Define the basic serial functions we support. + */ +static const struct uart_ops mcf_uart_ops = { + .tx_empty = mcf_tx_empty, + .get_mctrl = mcf_get_mctrl, + .set_mctrl = mcf_set_mctrl, + .start_tx = mcf_start_tx, + .stop_tx = mcf_stop_tx, + .stop_rx = mcf_stop_rx, + .enable_ms = mcf_enable_ms, + .break_ctl = mcf_break_ctl, + .startup = mcf_startup, + .shutdown = mcf_shutdown, + .set_termios = mcf_set_termios, + .type = mcf_type, + .request_port = mcf_request_port, + .release_port = mcf_release_port, + .config_port = mcf_config_port, + .verify_port = mcf_verify_port, +}; + +static struct mcf_uart mcf_ports[4]; + +#define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) + +/****************************************************************************/ +#if defined(CONFIG_SERIAL_MCF_CONSOLE) +/****************************************************************************/ + +int __init early_mcf_setup(struct mcf_platform_uart *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { + port = &mcf_ports[i].port; + + port->line = i; + port->type = PORT_MCF; + port->mapbase = platp[i].mapbase; + port->membase = (platp[i].membase) ? platp[i].membase : + (unsigned char __iomem *) port->mapbase; + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = MCF_BUSCLK; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &mcf_uart_ops; + } + + return 0; +} + +/****************************************************************************/ + +static void mcf_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(mcf_ports + co->index)->port; + int i; + + for (i = 0; (i < 0x10000); i++) { + if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) + break; + } + writeb(c, port->membase + MCFUART_UTB); + for (i = 0; (i < 0x10000); i++) { + if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) + break; + } +} + +/****************************************************************************/ + +static void mcf_console_write(struct console *co, const char *s, unsigned int count) +{ + for (; (count); count--, s++) { + mcf_console_putc(co, *s); + if (*s == '\n') + mcf_console_putc(co, '\r'); + } +} + +/****************************************************************************/ + +static int __init mcf_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_SERIAL_MCF_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if ((co->index < 0) || (co->index >= MCF_MAXPORTS)) + co->index = 0; + port = &mcf_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +/****************************************************************************/ + +static struct uart_driver mcf_driver; + +static struct console mcf_console = { + .name = "ttyS", + .write = mcf_console_write, + .device = uart_console_device, + .setup = mcf_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mcf_driver, +}; + +static int __init mcf_console_init(void) +{ + register_console(&mcf_console); + return 0; +} + +console_initcall(mcf_console_init); + +#define MCF_CONSOLE &mcf_console + +/****************************************************************************/ +#else +/****************************************************************************/ + +#define MCF_CONSOLE NULL + +/****************************************************************************/ +#endif /* CONFIG_MCF_CONSOLE */ +/****************************************************************************/ + +/* + * Define the mcf UART driver structure. + */ +static struct uart_driver mcf_driver = { + .owner = THIS_MODULE, + .driver_name = "mcf", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = MCF_MAXPORTS, + .cons = MCF_CONSOLE, +}; + +/****************************************************************************/ + +static int __devinit mcf_probe(struct platform_device *pdev) +{ + struct mcf_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { + port = &mcf_ports[i].port; + + port->line = i; + port->type = PORT_MCF; + port->mapbase = platp[i].mapbase; + port->membase = (platp[i].membase) ? platp[i].membase : + (unsigned char __iomem *) platp[i].mapbase; + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = MCF_BUSCLK; + port->ops = &mcf_uart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&mcf_driver, port); + } + + return 0; +} + +/****************************************************************************/ + +static int __devexit mcf_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; (i < MCF_MAXPORTS); i++) { + port = &mcf_ports[i].port; + if (port) + uart_remove_one_port(&mcf_driver, port); + } + + return 0; +} + +/****************************************************************************/ + +static struct platform_driver mcf_platform_driver = { + .probe = mcf_probe, + .remove = __devexit_p(mcf_remove), + .driver = { + .name = "mcfuart", + .owner = THIS_MODULE, + }, +}; + +/****************************************************************************/ + +static int __init mcf_init(void) +{ + int rc; + + printk("ColdFire internal UART serial driver\n"); + + rc = uart_register_driver(&mcf_driver); + if (rc) + return rc; + rc = platform_driver_register(&mcf_platform_driver); + if (rc) + return rc; + return 0; +} + +/****************************************************************************/ + +static void __exit mcf_exit(void) +{ + platform_driver_unregister(&mcf_platform_driver); + uart_unregister_driver(&mcf_driver); +} + +/****************************************************************************/ + +module_init(mcf_init); +module_exit(mcf_exit); + +MODULE_AUTHOR("Greg Ungerer "); +MODULE_DESCRIPTION("Freescale ColdFire UART driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mcfuart"); + +/****************************************************************************/ diff --git a/drivers/tty/serial/mfd.c b/drivers/tty/serial/mfd.c new file mode 100644 index 000000000000..d40010a22ecd --- /dev/null +++ b/drivers/tty/serial/mfd.c @@ -0,0 +1,1513 @@ +/* + * mfd.c: driver for High Speed UART device of Intel Medfield platform + * + * Refer pxa.c, 8250.c and some other drivers in drivers/serial/ + * + * (C) Copyright 2010 Intel Corporation + * + * 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; version 2 + * of the License. + */ + +/* Notes: + * 1. DMA channel allocation: 0/1 channel are assigned to port 0, + * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans + * are used for RX, odd chans for TX + * + * 2. In A0 stepping, UART will not support TX half empty flag + * + * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always + * asserted, only when the HW is reset the DDCD and DDSR will + * be triggered + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MFD_HSU_A0_STEPPING 1 + +#define HSU_DMA_BUF_SIZE 2048 + +#define chan_readl(chan, offset) readl(chan->reg + offset) +#define chan_writel(chan, offset, val) writel(val, chan->reg + offset) + +#define mfd_readl(obj, offset) readl(obj->reg + offset) +#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) + +#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10) + +struct hsu_dma_buffer { + u8 *buf; + dma_addr_t dma_addr; + u32 dma_size; + u32 ofs; +}; + +struct hsu_dma_chan { + u32 id; + enum dma_data_direction dirt; + struct uart_hsu_port *uport; + void __iomem *reg; + struct timer_list rx_timer; /* only needed by RX channel */ +}; + +struct uart_hsu_port { + struct uart_port port; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned int lsr_break_flag; + char name[12]; + int index; + struct device *dev; + + struct hsu_dma_chan *txc; + struct hsu_dma_chan *rxc; + struct hsu_dma_buffer txbuf; + struct hsu_dma_buffer rxbuf; + int use_dma; /* flag for DMA/PIO */ + int running; + int dma_tx_on; +}; + +/* Top level data structure of HSU */ +struct hsu_port { + void __iomem *reg; + unsigned long paddr; + unsigned long iolen; + u32 irq; + + struct uart_hsu_port port[3]; + struct hsu_dma_chan chans[10]; + + struct dentry *debugfs; +}; + +static inline unsigned int serial_in(struct uart_hsu_port *up, int offset) +{ + unsigned int val; + + if (offset > UART_MSR) { + offset <<= 2; + val = readl(up->port.membase + offset); + } else + val = (unsigned int)readb(up->port.membase + offset); + + return val; +} + +static inline void serial_out(struct uart_hsu_port *up, int offset, int value) +{ + if (offset > UART_MSR) { + offset <<= 2; + writel(value, up->port.membase + offset); + } else { + unsigned char val = value & 0xff; + writeb(val, up->port.membase + offset); + } +} + +#ifdef CONFIG_DEBUG_FS + +#define HSU_REGS_BUFSIZE 1024 + +static int hsu_show_regs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t port_show_regs(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct uart_hsu_port *up = file->private_data; + char *buf; + u32 len = 0; + ssize_t ret; + + buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); + if (!buf) + return 0; + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MFD HSU port[%d] regs:\n", up->index); + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "=================================\n"); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "IER: \t\t0x%08x\n", serial_in(up, UART_IER)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "PS: \t\t0x%08x\n", serial_in(up, UART_PS)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV)); + + if (len > HSU_REGS_BUFSIZE) + len = HSU_REGS_BUFSIZE; + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; +} + +static ssize_t dma_show_regs(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hsu_dma_chan *chan = file->private_data; + char *buf; + u32 len = 0; + ssize_t ret; + + buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); + if (!buf) + return 0; + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MFD HSU DMA channel [%d] regs:\n", chan->id); + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "=================================\n"); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR)); + + if (len > HSU_REGS_BUFSIZE) + len = HSU_REGS_BUFSIZE; + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; +} + +static const struct file_operations port_regs_ops = { + .owner = THIS_MODULE, + .open = hsu_show_regs_open, + .read = port_show_regs, + .llseek = default_llseek, +}; + +static const struct file_operations dma_regs_ops = { + .owner = THIS_MODULE, + .open = hsu_show_regs_open, + .read = dma_show_regs, + .llseek = default_llseek, +}; + +static int hsu_debugfs_init(struct hsu_port *hsu) +{ + int i; + char name[32]; + + hsu->debugfs = debugfs_create_dir("hsu", NULL); + if (!hsu->debugfs) + return -ENOMEM; + + for (i = 0; i < 3; i++) { + snprintf(name, sizeof(name), "port_%d_regs", i); + debugfs_create_file(name, S_IFREG | S_IRUGO, + hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops); + } + + for (i = 0; i < 6; i++) { + snprintf(name, sizeof(name), "dma_chan_%d_regs", i); + debugfs_create_file(name, S_IFREG | S_IRUGO, + hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops); + } + + return 0; +} + +static void hsu_debugfs_remove(struct hsu_port *hsu) +{ + if (hsu->debugfs) + debugfs_remove_recursive(hsu->debugfs); +} + +#else +static inline int hsu_debugfs_init(struct hsu_port *hsu) +{ + return 0; +} + +static inline void hsu_debugfs_remove(struct hsu_port *hsu) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +static void serial_hsu_enable_ms(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +void hsu_dma_tx(struct uart_hsu_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + struct hsu_dma_buffer *dbuf = &up->txbuf; + int count; + + /* test_and_set_bit may be better, but anyway it's in lock protected mode */ + if (up->dma_tx_on) + return; + + /* Update the circ buf info */ + xmit->tail += dbuf->ofs; + xmit->tail &= UART_XMIT_SIZE - 1; + + up->port.icount.tx += dbuf->ofs; + dbuf->ofs = 0; + + /* Disable the channel */ + chan_writel(up->txc, HSU_CH_CR, 0x0); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) { + dma_sync_single_for_device(up->port.dev, + dbuf->dma_addr, + dbuf->dma_size, + DMA_TO_DEVICE); + + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + dbuf->ofs = count; + + /* Reprogram the channel */ + chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail); + chan_writel(up->txc, HSU_CH_D0TSR, count); + + /* Reenable the channel */ + chan_writel(up->txc, HSU_CH_DCR, 0x1 + | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24)); + up->dma_tx_on = 1; + chan_writel(up->txc, HSU_CH_CR, 0x1); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} + +/* The buffer is already cache coherent */ +void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf) +{ + dbuf->ofs = 0; + + chan_writel(rxc, HSU_CH_BSR, 32); + chan_writel(rxc, HSU_CH_MOTSR, 4); + + chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr); + chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size); + chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ + ); + chan_writel(rxc, HSU_CH_CR, 0x3); + + mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); +} + +/* Protected by spin_lock_irqsave(port->lock) */ +static void serial_hsu_start_tx(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + + if (up->use_dma) { + hsu_dma_tx(up); + } else if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_hsu_stop_tx(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + struct hsu_dma_chan *txc = up->txc; + + if (up->use_dma) + chan_writel(txc, HSU_CH_CR, 0x0); + else if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +/* This is always called in spinlock protected mode, so + * modify timeout timer is safe here */ +void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) +{ + struct hsu_dma_buffer *dbuf = &up->rxbuf; + struct hsu_dma_chan *chan = up->rxc; + struct uart_port *port = &up->port; + struct tty_struct *tty = port->state->port.tty; + int count; + + if (!tty) + return; + + /* + * First need to know how many is already transferred, + * then check if its a timeout DMA irq, and return + * the trail bytes out, push them up and reenable the + * channel + */ + + /* Timeout IRQ, need wait some time, see Errata 2 */ + if (int_sts & 0xf00) + udelay(2); + + /* Stop the channel */ + chan_writel(chan, HSU_CH_CR, 0x0); + + count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; + if (!count) { + /* Restart the channel before we leave */ + chan_writel(chan, HSU_CH_CR, 0x3); + return; + } + del_timer(&chan->rx_timer); + + dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, + dbuf->dma_size, DMA_FROM_DEVICE); + + /* + * Head will only wrap around when we recycle + * the DMA buffer, and when that happens, we + * explicitly set tail to 0. So head will + * always be greater than tail. + */ + tty_insert_flip_string(tty, dbuf->buf, count); + port->icount.rx += count; + + dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, + dbuf->dma_size, DMA_FROM_DEVICE); + + /* Reprogram the channel */ + chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr); + chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size); + chan_writel(chan, HSU_CH_DCR, 0x1 + | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ + ); + tty_flip_buffer_push(tty); + + chan_writel(chan, HSU_CH_CR, 0x3); + chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ; + add_timer(&chan->rx_timer); + +} + +static void serial_hsu_stop_rx(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + struct hsu_dma_chan *chan = up->rxc; + + if (up->use_dma) + chan_writel(chan, HSU_CH_CR, 0x2); + else { + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + } +} + +static inline void receive_chars(struct uart_hsu_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int ch, flag; + unsigned int max_count = 256; + + if (!tty) + return; + + do { + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + + dev_warn(up->dev, "We really rush into ERR/BI case" + "status = 0x%02x", *status); + /* For statistics only */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* Mask off conditions which should be ignored. */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE + if (up->port.cons && + up->port.cons->index == up->port.line) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && max_count--); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_hsu_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_hsu_stop_tx(&up->port); + return; + } + +#ifndef MFD_HSU_A0_STEPPING + count = up->port.fifosize / 2; +#else + /* + * A0 only supports fully empty IRQ, and the first char written + * into it won't clear the EMPT bit, so we may need be cautious + * by useing a shorter buffer + */ + count = up->port.fifosize - 4; +#endif + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_hsu_stop_tx(&up->port); +} + +static inline void check_modem_status(struct uart_hsu_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + /* We may only get DDCD when HW init and reset */ + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + /* Will start/stop_tx accordingly */ + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); +} + +/* + * This handles the interrupt from one port. + */ +static irqreturn_t port_irq(int irq, void *dev_id) +{ + struct uart_hsu_port *up = dev_id; + unsigned int iir, lsr; + unsigned long flags; + + if (unlikely(!up->running)) + return IRQ_NONE; + + spin_lock_irqsave(&up->port.lock, flags); + if (up->use_dma) { + lsr = serial_in(up, UART_LSR); + if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) + dev_warn(up->dev, + "Got lsr irq while using DMA, lsr = 0x%2x\n", + lsr); + check_modem_status(up); + spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_HANDLED; + } + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) { + spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_NONE; + } + + lsr = serial_in(up, UART_LSR); + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + check_modem_status(up); + + /* lsr will be renewed during the receive_chars */ + if (lsr & UART_LSR_THRE) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_HANDLED; +} + +static inline void dma_chan_irq(struct hsu_dma_chan *chan) +{ + struct uart_hsu_port *up = chan->uport; + unsigned long flags; + u32 int_sts; + + spin_lock_irqsave(&up->port.lock, flags); + + if (!up->use_dma || !up->running) + goto exit; + + /* + * No matter what situation, need read clear the IRQ status + * There is a bug, see Errata 5, HSD 2900918 + */ + int_sts = chan_readl(chan, HSU_CH_SR); + + /* Rx channel */ + if (chan->dirt == DMA_FROM_DEVICE) + hsu_dma_rx(up, int_sts); + + /* Tx channel */ + if (chan->dirt == DMA_TO_DEVICE) { + chan_writel(chan, HSU_CH_CR, 0x0); + up->dma_tx_on = 0; + hsu_dma_tx(up); + } + +exit: + spin_unlock_irqrestore(&up->port.lock, flags); + return; +} + +static irqreturn_t dma_irq(int irq, void *dev_id) +{ + struct hsu_port *hsu = dev_id; + u32 int_sts, i; + + int_sts = mfd_readl(hsu, HSU_GBL_DMAISR); + + /* Currently we only have 6 channels may be used */ + for (i = 0; i < 6; i++) { + if (int_sts & 0x1) + dma_chan_irq(&hsu->chans[i]); + int_sts >>= 1; + } + + return IRQ_HANDLED; +} + +static unsigned int serial_hsu_tx_empty(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_hsu_get_mctrl(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned char status; + unsigned int ret; + + status = serial_in(up, 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 serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned char mcr = 0; + + 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; + + mcr |= up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial_hsu_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * What special to do: + * 1. chose the 64B fifo mode + * 2. make sure not to select half empty mode for A0 stepping + * 3. start dma or pio depends on configuration + * 4. we only allocate dma memory when needed + */ +static int serial_hsu_startup(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); + + /* Clear the interrupt registers. */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* Now, initialize the UART, default is 8n1 */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.mctrl |= TIOCM_OUT2; + serial_hsu_set_mctrl(&up->port, up->port.mctrl); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + if (!up->use_dma) + up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE; + else + up->ier = 0; + serial_out(up, UART_IER, up->ier); + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* DMA init */ + if (up->use_dma) { + struct hsu_dma_buffer *dbuf; + struct circ_buf *xmit = &port->state->xmit; + + up->dma_tx_on = 0; + + /* First allocate the RX buffer */ + dbuf = &up->rxbuf; + dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL); + if (!dbuf->buf) { + up->use_dma = 0; + goto exit; + } + dbuf->dma_addr = dma_map_single(port->dev, + dbuf->buf, + HSU_DMA_BUF_SIZE, + DMA_FROM_DEVICE); + dbuf->dma_size = HSU_DMA_BUF_SIZE; + + /* Start the RX channel right now */ + hsu_dma_start_rx_chan(up->rxc, dbuf); + + /* Next init the TX DMA */ + dbuf = &up->txbuf; + dbuf->buf = xmit->buf; + dbuf->dma_addr = dma_map_single(port->dev, + dbuf->buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + dbuf->dma_size = UART_XMIT_SIZE; + + /* This should not be changed all around */ + chan_writel(up->txc, HSU_CH_BSR, 32); + chan_writel(up->txc, HSU_CH_MOTSR, 4); + dbuf->ofs = 0; + } + +exit: + /* And clear the interrupt registers again for luck. */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + up->running = 1; + return 0; +} + +static void serial_hsu_shutdown(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + + del_timer_sync(&up->rxc->rx_timer); + + /* Disable interrupts from this port */ + up->ier = 0; + serial_out(up, UART_IER, 0); + up->running = 0; + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_hsu_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* Disable break condition and FIFOs */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +static void +serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + struct tty_struct *tty = port->state->port.tty; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + u32 ps, mul; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + /* CMSPAR isn't supported by this driver */ + if (tty) + tty->termios->c_cflag &= ~CMSPAR; + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * The base clk is 50Mhz, and the baud rate come from: + * baud = 50M * MUL / (DIV * PS * DLAB) + * + * For those basic low baud rate we can get the direct + * scalar from 2746800, like 115200 = 2746800/24. For those + * higher baud rate, we handle them case by case, mainly by + * adjusting the MUL/PS registers, and DIV register is kept + * as default value 0x3d09 to make things simple + */ + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + + quot = 1; + ps = 0x10; + mul = 0x3600; + switch (baud) { + case 3500000: + mul = 0x3345; + ps = 0xC; + break; + case 1843200: + mul = 0x2400; + break; + case 3000000: + case 2500000: + case 2000000: + case 1500000: + case 1000000: + case 500000: + /* mul/ps/quot = 0x9C4/0x10/0x1 will make a 500000 bps */ + mul = baud / 500000 * 0x9C4; + break; + default: + /* Use uart_get_divisor to get quot for other baud rates */ + quot = 0; + } + + if (!quot) + quot = uart_get_divisor(port, baud); + + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B; + else if ((up->port.uartclk / quot) < (230400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B; + + fcr |= UART_FCR_HSU_64B_FIFO; +#ifdef MFD_HSU_A0_STEPPING + /* A0 doesn't support half empty IRQ */ + fcr |= UART_FCR_FULL_EMPT_TXI; +#endif + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* Update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* Characters to ignore */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* Ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts, disable + * MSI by default + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE | UART_MCR_RTS; + else + up->mcr &= ~UART_MCR_AFE; + + serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + serial_out(up, UART_LCR, cval); /* reset DLAB */ + serial_out(up, UART_MUL, mul); /* set MUL */ + serial_out(up, UART_PS, ps); /* set PS */ + up->lcr = cval; /* Save LCR */ + serial_hsu_set_mctrl(&up->port, up->port.mctrl); + serial_out(up, UART_FCR, fcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial_hsu_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ +} + +static void serial_hsu_release_port(struct uart_port *port) +{ +} + +static int serial_hsu_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_hsu_config_port(struct uart_port *port, int flags) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + up->port.type = PORT_MFD; +} + +static int +serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* We don't want the core code to modify any port params */ + return -EINVAL; +} + +static const char * +serial_hsu_type(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + return up->name; +} + +/* Mainly for uart console use */ +static struct uart_hsu_port *serial_hsu_ports[3]; +static struct uart_driver serial_hsu_reg; + +#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* Wait for transmitter & holding register to empty */ +static inline void wait_for_xmitr(struct uart_hsu_port *up) +{ + unsigned int status, tmout = 1000; + + /* Wait up to 1ms for the character to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while (!(status & BOTH_EMPTY)); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +static void serial_hsu_console_putchar(struct uart_port *port, int ch) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +/* + * 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. + */ +static void +serial_hsu_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_hsu_port *up = serial_hsu_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + /* First save the IER then disable the interrupts */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_hsu_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static struct console serial_hsu_console; + +static int __init +serial_hsu_console_setup(struct console *co, char *options) +{ + struct uart_hsu_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (co->index == -1 || co->index >= serial_hsu_reg.nr) + co->index = 0; + up = serial_hsu_ports[co->index]; + if (!up) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + ret = uart_set_options(&up->port, co, baud, parity, bits, flow); + + return ret; +} + +static struct console serial_hsu_console = { + .name = "ttyMFD", + .write = serial_hsu_console_write, + .device = uart_console_device, + .setup = serial_hsu_console_setup, + .flags = CON_PRINTBUFFER, + .index = 2, + .data = &serial_hsu_reg, +}; +#endif + +struct uart_ops serial_hsu_pops = { + .tx_empty = serial_hsu_tx_empty, + .set_mctrl = serial_hsu_set_mctrl, + .get_mctrl = serial_hsu_get_mctrl, + .stop_tx = serial_hsu_stop_tx, + .start_tx = serial_hsu_start_tx, + .stop_rx = serial_hsu_stop_rx, + .enable_ms = serial_hsu_enable_ms, + .break_ctl = serial_hsu_break_ctl, + .startup = serial_hsu_startup, + .shutdown = serial_hsu_shutdown, + .set_termios = serial_hsu_set_termios, + .pm = serial_hsu_pm, + .type = serial_hsu_type, + .release_port = serial_hsu_release_port, + .request_port = serial_hsu_request_port, + .config_port = serial_hsu_config_port, + .verify_port = serial_hsu_verify_port, +}; + +static struct uart_driver serial_hsu_reg = { + .owner = THIS_MODULE, + .driver_name = "MFD serial", + .dev_name = "ttyMFD", + .major = TTY_MAJOR, + .minor = 128, + .nr = 3, +}; + +#ifdef CONFIG_PM +static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state) +{ + void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + + /* Make sure this is not the internal dma controller */ + if (priv && (pdev->device != 0x081E)) { + up = priv; + uart_suspend_port(&serial_hsu_reg, &up->port); + } + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int serial_hsu_resume(struct pci_dev *pdev) +{ + void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + ret = pci_enable_device(pdev); + if (ret) + dev_warn(&pdev->dev, + "HSU: can't re-enable device, try to continue\n"); + + if (priv && (pdev->device != 0x081E)) { + up = priv; + uart_resume_port(&serial_hsu_reg, &up->port); + } + return 0; +} +#else +#define serial_hsu_suspend NULL +#define serial_hsu_resume NULL +#endif + +/* temp global pointer before we settle down on using one or four PCI dev */ +static struct hsu_port *phsu; + +static int serial_hsu_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct uart_hsu_port *uport; + int index, ret; + + printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n", + pdev->vendor, pdev->device); + + switch (pdev->device) { + case 0x081B: + index = 0; + break; + case 0x081C: + index = 1; + break; + case 0x081D: + index = 2; + break; + case 0x081E: + /* internal DMA controller */ + index = 3; + break; + default: + dev_err(&pdev->dev, "HSU: out of index!"); + return -ENODEV; + } + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + if (index == 3) { + /* DMA controller */ + ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu); + if (ret) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto err_disable; + } + pci_set_drvdata(pdev, phsu); + } else { + /* UART port 0~2 */ + uport = &phsu->port[index]; + uport->port.irq = pdev->irq; + uport->port.dev = &pdev->dev; + uport->dev = &pdev->dev; + + ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport); + if (ret) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto err_disable; + } + uart_add_one_port(&serial_hsu_reg, &uport->port); + +#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE + if (index == 2) { + register_console(&serial_hsu_console); + uport->port.cons = &serial_hsu_console; + } +#endif + pci_set_drvdata(pdev, uport); + } + + return 0; + +err_disable: + pci_disable_device(pdev); + return ret; +} + +static void hsu_dma_rx_timeout(unsigned long data) +{ + struct hsu_dma_chan *chan = (void *)data; + struct uart_hsu_port *up = chan->uport; + struct hsu_dma_buffer *dbuf = &up->rxbuf; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; + + if (!count) { + mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); + goto exit; + } + + hsu_dma_rx(up, 0); +exit: + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void hsu_global_init(void) +{ + struct hsu_port *hsu; + struct uart_hsu_port *uport; + struct hsu_dma_chan *dchan; + int i, ret; + + hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL); + if (!hsu) + return; + + /* Get basic io resource and map it */ + hsu->paddr = 0xffa28000; + hsu->iolen = 0x1000; + + if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global"))) + pr_warning("HSU: error in request mem region\n"); + + hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen); + if (!hsu->reg) { + pr_err("HSU: error in ioremap\n"); + ret = -ENOMEM; + goto err_free_region; + } + + /* Initialise the 3 UART ports */ + uport = hsu->port; + for (i = 0; i < 3; i++) { + uport->port.type = PORT_MFD; + uport->port.iotype = UPIO_MEM; + uport->port.mapbase = (resource_size_t)hsu->paddr + + HSU_PORT_REG_OFFSET + + i * HSU_PORT_REG_LENGTH; + uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET + + i * HSU_PORT_REG_LENGTH; + + sprintf(uport->name, "hsu_port%d", i); + uport->port.fifosize = 64; + uport->port.ops = &serial_hsu_pops; + uport->port.line = i; + uport->port.flags = UPF_IOREMAP; + /* set the scalable maxim support rate to 2746800 bps */ + uport->port.uartclk = 115200 * 24 * 16; + + uport->running = 0; + uport->txc = &hsu->chans[i * 2]; + uport->rxc = &hsu->chans[i * 2 + 1]; + + serial_hsu_ports[i] = uport; + uport->index = i; + uport++; + } + + /* Initialise 6 dma channels */ + dchan = hsu->chans; + for (i = 0; i < 6; i++) { + dchan->id = i; + dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dchan->uport = &hsu->port[i/2]; + dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + + i * HSU_DMA_CHANS_REG_LENGTH; + + /* Work around for RX */ + if (dchan->dirt == DMA_FROM_DEVICE) { + init_timer(&dchan->rx_timer); + dchan->rx_timer.function = hsu_dma_rx_timeout; + dchan->rx_timer.data = (unsigned long)dchan; + } + dchan++; + } + + phsu = hsu; + hsu_debugfs_init(hsu); + return; + +err_free_region: + release_mem_region(hsu->paddr, hsu->iolen); + kfree(hsu); + return; +} + +static void serial_hsu_remove(struct pci_dev *pdev) +{ + void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + + if (!priv) + return; + + /* For port 0/1/2, priv is the address of uart_hsu_port */ + if (pdev->device != 0x081E) { + up = priv; + uart_remove_one_port(&serial_hsu_reg, &up->port); + } + + pci_set_drvdata(pdev, NULL); + free_irq(pdev->irq, priv); + pci_disable_device(pdev); +} + +/* First 3 are UART ports, and the 4th is the DMA */ +static const struct pci_device_id pci_ids[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) }, + {}, +}; + +static struct pci_driver hsu_pci_driver = { + .name = "HSU serial", + .id_table = pci_ids, + .probe = serial_hsu_probe, + .remove = __devexit_p(serial_hsu_remove), + .suspend = serial_hsu_suspend, + .resume = serial_hsu_resume, +}; + +static int __init hsu_pci_init(void) +{ + int ret; + + hsu_global_init(); + + ret = uart_register_driver(&serial_hsu_reg); + if (ret) + return ret; + + return pci_register_driver(&hsu_pci_driver); +} + +static void __exit hsu_pci_exit(void) +{ + pci_unregister_driver(&hsu_pci_driver); + uart_unregister_driver(&serial_hsu_reg); + + hsu_debugfs_remove(phsu); + + kfree(phsu); +} + +module_init(hsu_pci_init); +module_exit(hsu_pci_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:medfield-hsu"); diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c new file mode 100644 index 000000000000..126ec7f568ec --- /dev/null +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -0,0 +1,1527 @@ +/* + * Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs. + * + * FIXME According to the usermanual the status bits in the status register + * are only updated when the peripherals access the FIFO and not when the + * CPU access them. So since we use this bits to know when we stop writing + * and reading, they may not be updated in-time and a race condition may + * exists. But I haven't be able to prove this and I don't care. But if + * any problem arises, it might worth checking. The TX/RX FIFO Stats + * registers should be used in addition. + * Update: Actually, they seem updated ... At least the bits we use. + * + * + * Maintainer : Sylvain Munaut + * + * Some of the code has been inspired/copied from the 2.4 code written + * by Dale Farnsworth . + * + * Copyright (C) 2008 Freescale Semiconductor Inc. + * John Rigby + * Added support for MPC5121 + * Copyright (C) 2006 Secret Lab Technologies Ltd. + * Grant Likely + * Copyright (C) 2004-2006 Sylvain Munaut + * Copyright (C) 2003 MontaVista, Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_PSC_MAJOR 204 +#define SERIAL_PSC_MINOR 148 + + +#define ISR_PASS_LIMIT 256 /* Max number of iteration in the interrupt */ + + +static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM]; + /* Rem: - We use the read_status_mask as a shadow of + * psc->mpc52xx_psc_imr + * - It's important that is array is all zero on start as we + * use it to know if it's initialized or not ! If it's not sure + * it's cleared, then a memset(...,0,...) should be added to + * the console_init + */ + +/* lookup table for matching device nodes to index numbers */ +static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM]; + +static void mpc52xx_uart_of_enumerate(void); + + +#define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase)) + + +/* Forward declaration of the interruption handling routine */ +static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); +static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); + + +/* Simple macro to test if a port is console or not. This one is taken + * for serial_core.c and maybe should be moved to serial_core.h ? */ +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) \ + ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +/* ======================================================================== */ +/* PSC fifo operations for isolating differences between 52xx and 512x */ +/* ======================================================================== */ + +struct psc_ops { + void (*fifo_init)(struct uart_port *port); + int (*raw_rx_rdy)(struct uart_port *port); + int (*raw_tx_rdy)(struct uart_port *port); + int (*rx_rdy)(struct uart_port *port); + int (*tx_rdy)(struct uart_port *port); + int (*tx_empty)(struct uart_port *port); + void (*stop_rx)(struct uart_port *port); + void (*start_tx)(struct uart_port *port); + void (*stop_tx)(struct uart_port *port); + void (*rx_clr_irq)(struct uart_port *port); + void (*tx_clr_irq)(struct uart_port *port); + void (*write_char)(struct uart_port *port, unsigned char c); + unsigned char (*read_char)(struct uart_port *port); + void (*cw_disable_ints)(struct uart_port *port); + void (*cw_restore_ints)(struct uart_port *port); + unsigned int (*set_baudrate)(struct uart_port *port, + struct ktermios *new, + struct ktermios *old); + int (*clock)(struct uart_port *port, int enable); + int (*fifoc_init)(void); + void (*fifoc_uninit)(void); + void (*get_irq)(struct uart_port *, struct device_node *); + irqreturn_t (*handle_irq)(struct uart_port *port); +}; + +/* setting the prescaler and divisor reg is common for all chips */ +static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, + u16 prescaler, unsigned int divisor) +{ + /* select prescaler */ + out_be16(&psc->mpc52xx_psc_clock_select, prescaler); + out_8(&psc->ctur, divisor >> 8); + out_8(&psc->ctlr, divisor & 0xff); +} + +#ifdef CONFIG_PPC_MPC52xx +#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc52xx_psc_fifo_init(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); + + out_8(&fifo->rfcntl, 0x00); + out_be16(&fifo->rfalarm, 0x1ff); + out_8(&fifo->tfcntl, 0x07); + out_be16(&fifo->tfalarm, 0x80); + + port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_RXRDY; +} + +static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXRDY; +} + + +static int mpc52xx_psc_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_RXRDY; +} + +static int mpc52xx_psc_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_TXRDY; +} + +static int mpc52xx_psc_tx_empty(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXEMP; +} + +static void mpc52xx_psc_start_tx(struct uart_port *port) +{ + port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_tx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_rx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_rx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_tx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&PSC(port)->mpc52xx_psc_buffer_8, c); +} + +static unsigned char mpc52xx_psc_read_char(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_buffer_8); +} + +static void mpc52xx_psc_cw_disable_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, 0); +} + +static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + + /* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (32 * 0xffff) + 1, + port->uartclk / 32); + divisor = (port->uartclk + 16 * baud) / (32 * baud); + + /* enable the /32 prescaler and set the divisor */ + mpc52xx_set_divisor(PSC(port), 0xdd00, divisor); + return baud; +} + +static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + u16 prescaler; + + /* The 5200B has a selectable /4 or /32 prescaler, uartclk contains the + * ipb freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (32 * 0xffff) + 1, + port->uartclk / 4); + divisor = (port->uartclk + 2 * baud) / (4 * baud); + + /* select the proper prescaler and set the divisor */ + if (divisor > 0xffff) { + divisor = (divisor + 4) / 8; + prescaler = 0xdd00; /* /32 */ + } else + prescaler = 0xff00; /* /4 */ + mpc52xx_set_divisor(PSC(port), prescaler, divisor); + return baud; +} + +static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np) +{ + port->irqflags = IRQF_DISABLED; + port->irq = irq_of_parse_and_map(np, 0); +} + +/* 52xx specific interrupt handler. The caller holds the port lock */ +static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port) +{ + return mpc5xxx_uart_process_int(port); +} + +static struct psc_ops mpc52xx_psc_ops = { + .fifo_init = mpc52xx_psc_fifo_init, + .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, + .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, + .rx_rdy = mpc52xx_psc_rx_rdy, + .tx_rdy = mpc52xx_psc_tx_rdy, + .tx_empty = mpc52xx_psc_tx_empty, + .stop_rx = mpc52xx_psc_stop_rx, + .start_tx = mpc52xx_psc_start_tx, + .stop_tx = mpc52xx_psc_stop_tx, + .rx_clr_irq = mpc52xx_psc_rx_clr_irq, + .tx_clr_irq = mpc52xx_psc_tx_clr_irq, + .write_char = mpc52xx_psc_write_char, + .read_char = mpc52xx_psc_read_char, + .cw_disable_ints = mpc52xx_psc_cw_disable_ints, + .cw_restore_ints = mpc52xx_psc_cw_restore_ints, + .set_baudrate = mpc5200_psc_set_baudrate, + .get_irq = mpc52xx_psc_get_irq, + .handle_irq = mpc52xx_psc_handle_irq, +}; + +static struct psc_ops mpc5200b_psc_ops = { + .fifo_init = mpc52xx_psc_fifo_init, + .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, + .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, + .rx_rdy = mpc52xx_psc_rx_rdy, + .tx_rdy = mpc52xx_psc_tx_rdy, + .tx_empty = mpc52xx_psc_tx_empty, + .stop_rx = mpc52xx_psc_stop_rx, + .start_tx = mpc52xx_psc_start_tx, + .stop_tx = mpc52xx_psc_stop_tx, + .rx_clr_irq = mpc52xx_psc_rx_clr_irq, + .tx_clr_irq = mpc52xx_psc_tx_clr_irq, + .write_char = mpc52xx_psc_write_char, + .read_char = mpc52xx_psc_read_char, + .cw_disable_ints = mpc52xx_psc_cw_disable_ints, + .cw_restore_ints = mpc52xx_psc_cw_restore_ints, + .set_baudrate = mpc5200b_psc_set_baudrate, + .get_irq = mpc52xx_psc_get_irq, + .handle_irq = mpc52xx_psc_handle_irq, +}; + +#endif /* CONFIG_MPC52xx */ + +#ifdef CONFIG_PPC_MPC512x +#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) + +/* PSC FIFO Controller for mpc512x */ +struct psc_fifoc { + u32 fifoc_cmd; + u32 fifoc_int; + u32 fifoc_dma; + u32 fifoc_axe; + u32 fifoc_debug; +}; + +static struct psc_fifoc __iomem *psc_fifoc; +static unsigned int psc_fifoc_irq; + +static void mpc512x_psc_fifo_init(struct uart_port *port) +{ + /* /32 prescaler */ + out_be16(&PSC(port)->mpc52xx_psc_clock_select, 0xdd00); + + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->txalarm, 1); + out_be32(&FIFO_512x(port)->tximr, 0); + + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->rxalarm, 1); + out_be32(&FIFO_512x(port)->rximr, 0); + + out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc512x_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc512x_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc512x_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->rxsr) + & in_be32(&FIFO_512x(port)->rximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & in_be32(&FIFO_512x(port)->tximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc512x_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr); +} + +static void mpc512x_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr)); +} + +static void mpc512x_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr)); +} + +static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_512x(port)->txdata_8, c); +} + +static unsigned char mpc512x_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_512x(port)->rxdata_8); +} + +static void mpc512x_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_512x(port)->tximr) << 16 | + in_be32(&FIFO_512x(port)->rximr); + out_be32(&FIFO_512x(port)->tximr, 0); + out_be32(&FIFO_512x(port)->rximr, 0); +} + +static void mpc512x_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); +} + +static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + + /* + * The "MPC5121e Microcontroller Reference Manual, Rev. 3" says on + * pg. 30-10 that the chip supports a /32 and a /10 prescaler. + * Furthermore, it states that "After reset, the prescaler by 10 + * for the UART mode is selected", but the reset register value is + * 0x0000 which means a /32 prescaler. This is wrong. + * + * In reality using /32 prescaler doesn't work, as it is not supported! + * Use /16 or /10 prescaler, see "MPC5121e Hardware Design Guide", + * Chapter 4.1 PSC in UART Mode. + * Calculate with a /16 prescaler here. + */ + + /* uartclk contains the ips freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (16 * 0xffff) + 1, + port->uartclk / 16); + divisor = (port->uartclk + 8 * baud) / (16 * baud); + + /* enable the /16 prescaler and set the divisor */ + mpc52xx_set_divisor(PSC(port), 0xdd00, divisor); + return baud; +} + +/* Init PSC FIFO Controller */ +static int __init mpc512x_psc_fifoc_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, + "fsl,mpc5121-psc-fifo"); + if (!np) { + pr_err("%s: Can't find FIFOC node\n", __func__); + return -ENODEV; + } + + psc_fifoc = of_iomap(np, 0); + if (!psc_fifoc) { + pr_err("%s: Can't map FIFOC\n", __func__); + of_node_put(np); + return -ENODEV; + } + + psc_fifoc_irq = irq_of_parse_and_map(np, 0); + of_node_put(np); + if (psc_fifoc_irq == NO_IRQ) { + pr_err("%s: Can't get FIFOC irq\n", __func__); + iounmap(psc_fifoc); + return -ENODEV; + } + + return 0; +} + +static void __exit mpc512x_psc_fifoc_uninit(void) +{ + iounmap(psc_fifoc); +} + +/* 512x specific interrupt handler. The caller holds the port lock */ +static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) +{ + unsigned long fifoc_int; + int psc_num; + + /* Read pending PSC FIFOC interrupts */ + fifoc_int = in_be32(&psc_fifoc->fifoc_int); + + /* Check if it is an interrupt for this port */ + psc_num = (port->mapbase & 0xf00) >> 8; + if (test_bit(psc_num, &fifoc_int) || + test_bit(psc_num + 16, &fifoc_int)) + return mpc5xxx_uart_process_int(port); + + return IRQ_NONE; +} + +static int mpc512x_psc_clock(struct uart_port *port, int enable) +{ + struct clk *psc_clk; + int psc_num; + char clk_name[10]; + + if (uart_console(port)) + return 0; + + psc_num = (port->mapbase & 0xf00) >> 8; + snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num); + psc_clk = clk_get(port->dev, clk_name); + if (IS_ERR(psc_clk)) { + dev_err(port->dev, "Failed to get PSC clock entry!\n"); + return -ENODEV; + } + + dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis"); + + if (enable) + clk_enable(psc_clk); + else + clk_disable(psc_clk); + + return 0; +} + +static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) +{ + port->irqflags = IRQF_SHARED; + port->irq = psc_fifoc_irq; +} + +static struct psc_ops mpc512x_psc_ops = { + .fifo_init = mpc512x_psc_fifo_init, + .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, + .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, + .rx_rdy = mpc512x_psc_rx_rdy, + .tx_rdy = mpc512x_psc_tx_rdy, + .tx_empty = mpc512x_psc_tx_empty, + .stop_rx = mpc512x_psc_stop_rx, + .start_tx = mpc512x_psc_start_tx, + .stop_tx = mpc512x_psc_stop_tx, + .rx_clr_irq = mpc512x_psc_rx_clr_irq, + .tx_clr_irq = mpc512x_psc_tx_clr_irq, + .write_char = mpc512x_psc_write_char, + .read_char = mpc512x_psc_read_char, + .cw_disable_ints = mpc512x_psc_cw_disable_ints, + .cw_restore_ints = mpc512x_psc_cw_restore_ints, + .set_baudrate = mpc512x_psc_set_baudrate, + .clock = mpc512x_psc_clock, + .fifoc_init = mpc512x_psc_fifoc_init, + .fifoc_uninit = mpc512x_psc_fifoc_uninit, + .get_irq = mpc512x_psc_get_irq, + .handle_irq = mpc512x_psc_handle_irq, +}; +#endif + +static struct psc_ops *psc_ops; + +/* ======================================================================== */ +/* UART operations */ +/* ======================================================================== */ + +static unsigned int +mpc52xx_uart_tx_empty(struct uart_port *port) +{ + return psc_ops->tx_empty(port) ? TIOCSER_TEMT : 0; +} + +static void +mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + if (mctrl & TIOCM_RTS) + out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS); + else + out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS); +} + +static unsigned int +mpc52xx_uart_get_mctrl(struct uart_port *port) +{ + unsigned int ret = TIOCM_DSR; + u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + + if (!(status & MPC52xx_PSC_CTS)) + ret |= TIOCM_CTS; + if (!(status & MPC52xx_PSC_DCD)) + ret |= TIOCM_CAR; + + return ret; +} + +static void +mpc52xx_uart_stop_tx(struct uart_port *port) +{ + /* port->lock taken by caller */ + psc_ops->stop_tx(port); +} + +static void +mpc52xx_uart_start_tx(struct uart_port *port) +{ + /* port->lock taken by caller */ + psc_ops->start_tx(port); +} + +static void +mpc52xx_uart_send_xchar(struct uart_port *port, char ch) +{ + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + + port->x_char = ch; + if (ch) { + /* Make sure tx interrupts are on */ + /* Truly necessary ??? They should be anyway */ + psc_ops->start_tx(port); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void +mpc52xx_uart_stop_rx(struct uart_port *port) +{ + /* port->lock taken by caller */ + psc_ops->stop_rx(port); +} + +static void +mpc52xx_uart_enable_ms(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + + /* clear D_*-bits by reading them */ + in_8(&psc->mpc52xx_psc_ipcr); + /* enable CTS and DCD as IPC interrupts */ + out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD); + + port->read_status_mask |= MPC52xx_PSC_IMR_IPC; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static void +mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) +{ + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + + if (ctl == -1) + out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK); + else + out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int +mpc52xx_uart_startup(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + int ret; + + if (psc_ops->clock) { + ret = psc_ops->clock(port, 1); + if (ret) + return ret; + } + + /* Request IRQ */ + ret = request_irq(port->irq, mpc52xx_uart_int, + port->irqflags, "mpc52xx_psc_uart", port); + if (ret) + return ret; + + /* Reset/activate the port, clear and enable interrupts */ + out_8(&psc->command, MPC52xx_PSC_RST_RX); + out_8(&psc->command, MPC52xx_PSC_RST_TX); + + out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ + + psc_ops->fifo_init(port); + + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); + out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + + return 0; +} + +static void +mpc52xx_uart_shutdown(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + + /* Shut down the port. Leave TX active if on a console port */ + out_8(&psc->command, MPC52xx_PSC_RST_RX); + if (!uart_console(port)) + out_8(&psc->command, MPC52xx_PSC_RST_TX); + + port->read_status_mask = 0; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + + if (psc_ops->clock) + psc_ops->clock(port, 0); + + /* Release interrupt */ + free_irq(port->irq, port); +} + +static void +mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, + struct ktermios *old) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + unsigned long flags; + unsigned char mr1, mr2; + unsigned int j; + unsigned int baud; + + /* Prepare what we're gonna write */ + mr1 = 0; + + switch (new->c_cflag & CSIZE) { + case CS5: mr1 |= MPC52xx_PSC_MODE_5_BITS; + break; + case CS6: mr1 |= MPC52xx_PSC_MODE_6_BITS; + break; + case CS7: mr1 |= MPC52xx_PSC_MODE_7_BITS; + break; + case CS8: + default: mr1 |= MPC52xx_PSC_MODE_8_BITS; + } + + if (new->c_cflag & PARENB) { + mr1 |= (new->c_cflag & PARODD) ? + MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN; + } else + mr1 |= MPC52xx_PSC_MODE_PARNONE; + + + mr2 = 0; + + if (new->c_cflag & CSTOPB) + mr2 |= MPC52xx_PSC_MODE_TWO_STOP; + else + mr2 |= ((new->c_cflag & CSIZE) == CS5) ? + MPC52xx_PSC_MODE_ONE_STOP_5_BITS : + MPC52xx_PSC_MODE_ONE_STOP; + + if (new->c_cflag & CRTSCTS) { + mr1 |= MPC52xx_PSC_MODE_RXRTS; + mr2 |= MPC52xx_PSC_MODE_TXCTS; + } + + /* Get the lock */ + spin_lock_irqsave(&port->lock, flags); + + /* Do our best to flush TX & RX, so we don't lose anything */ + /* But we don't wait indefinitely ! */ + j = 5000000; /* Maximum wait */ + /* FIXME Can't receive chars since set_termios might be called at early + * boot for the console, all stuff is not yet ready to receive at that + * time and that just makes the kernel oops */ + /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */ + while (!mpc52xx_uart_tx_empty(port) && --j) + udelay(1); + + if (!j) + printk(KERN_ERR "mpc52xx_uart.c: " + "Unable to flush RX & TX fifos in-time in set_termios." + "Some chars may have been lost.\n"); + + /* Reset the TX & RX */ + out_8(&psc->command, MPC52xx_PSC_RST_RX); + out_8(&psc->command, MPC52xx_PSC_RST_TX); + + /* Send new mode settings */ + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + out_8(&psc->mode, mr1); + out_8(&psc->mode, mr2); + baud = psc_ops->set_baudrate(port, new, old); + + /* Update the per-port timeout */ + uart_update_timeout(port, new->c_cflag, baud); + + if (UART_ENABLE_MS(port, new->c_cflag)) + mpc52xx_uart_enable_ms(port); + + /* Reenable TX & RX */ + out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); + out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); + + /* We're all set, release the lock */ + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char * +mpc52xx_uart_type(struct uart_port *port) +{ + /* + * We keep using PORT_MPC52xx for historic reasons although it applies + * for MPC512x, too, but print "MPC5xxx" to not irritate users + */ + return port->type == PORT_MPC52xx ? "MPC5xxx PSC" : NULL; +} + +static void +mpc52xx_uart_release_port(struct uart_port *port) +{ + /* remapped by us ? */ + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc)); +} + +static int +mpc52xx_uart_request_port(struct uart_port *port) +{ + int err; + + if (port->flags & UPF_IOREMAP) /* Need to remap ? */ + port->membase = ioremap(port->mapbase, + sizeof(struct mpc52xx_psc)); + + if (!port->membase) + return -EINVAL; + + err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc), + "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY; + + if (err && (port->flags & UPF_IOREMAP)) { + iounmap(port->membase); + port->membase = NULL; + } + + return err; +} + +static void +mpc52xx_uart_config_port(struct uart_port *port, int flags) +{ + if ((flags & UART_CONFIG_TYPE) + && (mpc52xx_uart_request_port(port) == 0)) + port->type = PORT_MPC52xx; +} + +static int +mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPC52xx) + return -EINVAL; + + if ((ser->irq != port->irq) || + (ser->io_type != UPIO_MEM) || + (ser->baud_base != port->uartclk) || + (ser->iomem_base != (void *)port->mapbase) || + (ser->hub6 != 0)) + return -EINVAL; + + return 0; +} + + +static struct uart_ops mpc52xx_uart_ops = { + .tx_empty = mpc52xx_uart_tx_empty, + .set_mctrl = mpc52xx_uart_set_mctrl, + .get_mctrl = mpc52xx_uart_get_mctrl, + .stop_tx = mpc52xx_uart_stop_tx, + .start_tx = mpc52xx_uart_start_tx, + .send_xchar = mpc52xx_uart_send_xchar, + .stop_rx = mpc52xx_uart_stop_rx, + .enable_ms = mpc52xx_uart_enable_ms, + .break_ctl = mpc52xx_uart_break_ctl, + .startup = mpc52xx_uart_startup, + .shutdown = mpc52xx_uart_shutdown, + .set_termios = mpc52xx_uart_set_termios, +/* .pm = mpc52xx_uart_pm, Not supported yet */ +/* .set_wake = mpc52xx_uart_set_wake, Not supported yet */ + .type = mpc52xx_uart_type, + .release_port = mpc52xx_uart_release_port, + .request_port = mpc52xx_uart_request_port, + .config_port = mpc52xx_uart_config_port, + .verify_port = mpc52xx_uart_verify_port +}; + + +/* ======================================================================== */ +/* Interrupt handling */ +/* ======================================================================== */ + +static inline int +mpc52xx_uart_int_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + unsigned char ch, flag; + unsigned short status; + + /* While we can read, do so ! */ + while (psc_ops->raw_rx_rdy(port)) { + /* Get the char */ + ch = psc_ops->read_char(port); + + /* Handle sysreq char */ +#ifdef SUPPORT_SYSRQ + if (uart_handle_sysrq_char(port, ch)) { + port->sysrq = 0; + continue; + } +#endif + + /* Store it */ + + flag = TTY_NORMAL; + port->icount.rx++; + + status = in_be16(&PSC(port)->mpc52xx_psc_status); + + if (status & (MPC52xx_PSC_SR_PE | + MPC52xx_PSC_SR_FE | + MPC52xx_PSC_SR_RB)) { + + if (status & MPC52xx_PSC_SR_RB) { + flag = TTY_BREAK; + uart_handle_break(port); + port->icount.brk++; + } else if (status & MPC52xx_PSC_SR_PE) { + flag = TTY_PARITY; + port->icount.parity++; + } + else if (status & MPC52xx_PSC_SR_FE) { + flag = TTY_FRAME; + port->icount.frame++; + } + + /* Clear error condition */ + out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT); + + } + tty_insert_flip_char(tty, ch, flag); + if (status & MPC52xx_PSC_SR_OE) { + /* + * Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + port->icount.overrun++; + } + } + + spin_unlock(&port->lock); + tty_flip_buffer_push(tty); + spin_lock(&port->lock); + + return psc_ops->raw_rx_rdy(port); +} + +static inline int +mpc52xx_uart_int_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + /* Process out of band chars */ + if (port->x_char) { + psc_ops->write_char(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return 1; + } + + /* Nothing to do ? */ + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + mpc52xx_uart_stop_tx(port); + return 0; + } + + /* Send chars */ + while (psc_ops->raw_tx_rdy(port)) { + psc_ops->write_char(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + /* Wake up */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* Maybe we're done after all */ + if (uart_circ_empty(xmit)) { + mpc52xx_uart_stop_tx(port); + return 0; + } + + return 1; +} + +static irqreturn_t +mpc5xxx_uart_process_int(struct uart_port *port) +{ + unsigned long pass = ISR_PASS_LIMIT; + unsigned int keepgoing; + u8 status; + + /* While we have stuff to do, we continue */ + do { + /* If we don't find anything to do, we stop */ + keepgoing = 0; + + psc_ops->rx_clr_irq(port); + if (psc_ops->rx_rdy(port)) + keepgoing |= mpc52xx_uart_int_rx_chars(port); + + psc_ops->tx_clr_irq(port); + if (psc_ops->tx_rdy(port)) + keepgoing |= mpc52xx_uart_int_tx_chars(port); + + status = in_8(&PSC(port)->mpc52xx_psc_ipcr); + if (status & MPC52xx_PSC_D_DCD) + uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD)); + + if (status & MPC52xx_PSC_D_CTS) + uart_handle_cts_change(port, !(status & MPC52xx_PSC_CTS)); + + /* Limit number of iteration */ + if (!(--pass)) + keepgoing = 0; + + } while (keepgoing); + + return IRQ_HANDLED; +} + +static irqreturn_t +mpc52xx_uart_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + irqreturn_t ret; + + spin_lock(&port->lock); + + ret = psc_ops->handle_irq(port); + + spin_unlock(&port->lock); + + return ret; +} + +/* ======================================================================== */ +/* Console ( if applicable ) */ +/* ======================================================================== */ + +#ifdef CONFIG_SERIAL_MPC52xx_CONSOLE + +static void __init +mpc52xx_console_get_options(struct uart_port *port, + int *baud, int *parity, int *bits, int *flow) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + unsigned char mr1; + + pr_debug("mpc52xx_console_get_options(port=%p)\n", port); + + /* Read the mode registers */ + out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); + mr1 = in_8(&psc->mode); + + /* CT{U,L}R are write-only ! */ + *baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; + + /* Parse them */ + switch (mr1 & MPC52xx_PSC_MODE_BITS_MASK) { + case MPC52xx_PSC_MODE_5_BITS: + *bits = 5; + break; + case MPC52xx_PSC_MODE_6_BITS: + *bits = 6; + break; + case MPC52xx_PSC_MODE_7_BITS: + *bits = 7; + break; + case MPC52xx_PSC_MODE_8_BITS: + default: + *bits = 8; + } + + if (mr1 & MPC52xx_PSC_MODE_PARNONE) + *parity = 'n'; + else + *parity = mr1 & MPC52xx_PSC_MODE_PARODD ? 'o' : 'e'; +} + +static void +mpc52xx_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &mpc52xx_uart_ports[co->index]; + unsigned int i, j; + + /* Disable interrupts */ + psc_ops->cw_disable_ints(port); + + /* Wait the TX buffer to be empty */ + j = 5000000; /* Maximum wait */ + while (!mpc52xx_uart_tx_empty(port) && --j) + udelay(1); + + /* Write all the chars */ + for (i = 0; i < count; i++, s++) { + /* Line return handling */ + if (*s == '\n') + psc_ops->write_char(port, '\r'); + + /* Send the char */ + psc_ops->write_char(port, *s); + + /* Wait the TX buffer to be empty */ + j = 20000; /* Maximum wait */ + while (!mpc52xx_uart_tx_empty(port) && --j) + udelay(1); + } + + /* Restore interrupt state */ + psc_ops->cw_restore_ints(port); +} + + +static int __init +mpc52xx_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &mpc52xx_uart_ports[co->index]; + struct device_node *np = mpc52xx_uart_nodes[co->index]; + unsigned int uartclk; + struct resource res; + int ret; + + int baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_debug("mpc52xx_console_setup co=%p, co->index=%i, options=%s\n", + co, co->index, options); + + if ((co->index < 0) || (co->index >= MPC52xx_PSC_MAXNUM)) { + pr_debug("PSC%x out of range\n", co->index); + return -EINVAL; + } + + if (!np) { + pr_debug("PSC%x not found in device tree\n", co->index); + return -EINVAL; + } + + pr_debug("Console on ttyPSC%x is %s\n", + co->index, mpc52xx_uart_nodes[co->index]->full_name); + + /* Fetch register locations */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + pr_debug("Could not get resources for PSC%x\n", co->index); + return ret; + } + + uartclk = mpc5xxx_get_bus_frequency(np); + if (uartclk == 0) { + pr_debug("Could not find uart clock frequency!\n"); + return -EINVAL; + } + + /* Basic port init. Needed since we use some uart_??? func before + * real init for early access */ + spin_lock_init(&port->lock); + port->uartclk = uartclk; + port->ops = &mpc52xx_uart_ops; + port->mapbase = res.start; + port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc)); + port->irq = irq_of_parse_and_map(np, 0); + + if (port->membase == NULL) + return -EINVAL; + + pr_debug("mpc52xx-psc uart at %p, mapped to %p, irq=%x, freq=%i\n", + (void *)port->mapbase, port->membase, + port->irq, port->uartclk); + + /* Setup the port parameters accoding to options */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + mpc52xx_console_get_options(port, &baud, &parity, &bits, &flow); + + pr_debug("Setting console parameters: %i %i%c1 flow=%c\n", + baud, bits, parity, flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + + +static struct uart_driver mpc52xx_uart_driver; + +static struct console mpc52xx_console = { + .name = "ttyPSC", + .write = mpc52xx_console_write, + .device = uart_console_device, + .setup = mpc52xx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, /* Specified on the cmdline (e.g. console=ttyPSC0) */ + .data = &mpc52xx_uart_driver, +}; + + +static int __init +mpc52xx_console_init(void) +{ + mpc52xx_uart_of_enumerate(); + register_console(&mpc52xx_console); + return 0; +} + +console_initcall(mpc52xx_console_init); + +#define MPC52xx_PSC_CONSOLE &mpc52xx_console +#else +#define MPC52xx_PSC_CONSOLE NULL +#endif + + +/* ======================================================================== */ +/* UART Driver */ +/* ======================================================================== */ + +static struct uart_driver mpc52xx_uart_driver = { + .driver_name = "mpc52xx_psc_uart", + .dev_name = "ttyPSC", + .major = SERIAL_PSC_MAJOR, + .minor = SERIAL_PSC_MINOR, + .nr = MPC52xx_PSC_MAXNUM, + .cons = MPC52xx_PSC_CONSOLE, +}; + +/* ======================================================================== */ +/* OF Platform Driver */ +/* ======================================================================== */ + +static struct of_device_id mpc52xx_uart_of_match[] = { +#ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, }, + { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by old lite5200 device trees: */ + { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by efika: */ + { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, +#endif +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, +#endif + {}, +}; + +static int __devinit +mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *match) +{ + int idx = -1; + unsigned int uartclk; + struct uart_port *port = NULL; + struct resource res; + int ret; + + dev_dbg(&op->dev, "mpc52xx_uart_probe(op=%p, match=%p)\n", op, match); + + /* Check validity & presence */ + for (idx = 0; idx < MPC52xx_PSC_MAXNUM; idx++) + if (mpc52xx_uart_nodes[idx] == op->dev.of_node) + break; + if (idx >= MPC52xx_PSC_MAXNUM) + return -EINVAL; + pr_debug("Found %s assigned to ttyPSC%x\n", + mpc52xx_uart_nodes[idx]->full_name, idx); + + /* set the uart clock to the input clock of the psc, the different + * prescalers are taken into account in the set_baudrate() methods + * of the respective chip */ + uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node); + if (uartclk == 0) { + dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); + return -EINVAL; + } + + /* Init the port structure */ + port = &mpc52xx_uart_ports[idx]; + + spin_lock_init(&port->lock); + port->uartclk = uartclk; + port->fifosize = 512; + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF | + (uart_console(port) ? 0 : UPF_IOREMAP); + port->line = idx; + port->ops = &mpc52xx_uart_ops; + port->dev = &op->dev; + + /* Search for IRQ and mapbase */ + ret = of_address_to_resource(op->dev.of_node, 0, &res); + if (ret) + return ret; + + port->mapbase = res.start; + if (!port->mapbase) { + dev_dbg(&op->dev, "Could not allocate resources for PSC\n"); + return -EINVAL; + } + + psc_ops->get_irq(port, op->dev.of_node); + if (port->irq == NO_IRQ) { + dev_dbg(&op->dev, "Could not get irq\n"); + return -EINVAL; + } + + dev_dbg(&op->dev, "mpc52xx-psc uart at %p, irq=%x, freq=%i\n", + (void *)port->mapbase, port->irq, port->uartclk); + + /* Add the port to the uart sub-system */ + ret = uart_add_one_port(&mpc52xx_uart_driver, port); + if (ret) + return ret; + + dev_set_drvdata(&op->dev, (void *)port); + return 0; +} + +static int +mpc52xx_uart_of_remove(struct platform_device *op) +{ + struct uart_port *port = dev_get_drvdata(&op->dev); + dev_set_drvdata(&op->dev, NULL); + + if (port) + uart_remove_one_port(&mpc52xx_uart_driver, port); + + return 0; +} + +#ifdef CONFIG_PM +static int +mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) +{ + struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + + if (port) + uart_suspend_port(&mpc52xx_uart_driver, port); + + return 0; +} + +static int +mpc52xx_uart_of_resume(struct platform_device *op) +{ + struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + + if (port) + uart_resume_port(&mpc52xx_uart_driver, port); + + return 0; +} +#endif + +static void +mpc52xx_uart_of_assign(struct device_node *np) +{ + int i; + + /* Find the first free PSC number */ + for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) { + if (mpc52xx_uart_nodes[i] == NULL) { + of_node_get(np); + mpc52xx_uart_nodes[i] = np; + return; + } + } +} + +static void +mpc52xx_uart_of_enumerate(void) +{ + static int enum_done; + struct device_node *np; + const struct of_device_id *match; + int i; + + if (enum_done) + return; + + /* Assign index to each PSC in device tree */ + for_each_matching_node(np, mpc52xx_uart_of_match) { + match = of_match_node(mpc52xx_uart_of_match, np); + psc_ops = match->data; + mpc52xx_uart_of_assign(np); + } + + enum_done = 1; + + for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) { + if (mpc52xx_uart_nodes[i]) + pr_debug("%s assigned to ttyPSC%x\n", + mpc52xx_uart_nodes[i]->full_name, i); + } +} + +MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); + +static struct of_platform_driver mpc52xx_uart_of_driver = { + .probe = mpc52xx_uart_of_probe, + .remove = mpc52xx_uart_of_remove, +#ifdef CONFIG_PM + .suspend = mpc52xx_uart_of_suspend, + .resume = mpc52xx_uart_of_resume, +#endif + .driver = { + .name = "mpc52xx-psc-uart", + .owner = THIS_MODULE, + .of_match_table = mpc52xx_uart_of_match, + }, +}; + + +/* ======================================================================== */ +/* Module */ +/* ======================================================================== */ + +static int __init +mpc52xx_uart_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: MPC52xx PSC UART driver\n"); + + ret = uart_register_driver(&mpc52xx_uart_driver); + if (ret) { + printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", + __FILE__, ret); + return ret; + } + + mpc52xx_uart_of_enumerate(); + + /* + * Map the PSC FIFO Controller and init if on MPC512x. + */ + if (psc_ops && psc_ops->fifoc_init) { + ret = psc_ops->fifoc_init(); + if (ret) + return ret; + } + + ret = of_register_platform_driver(&mpc52xx_uart_of_driver); + if (ret) { + printk(KERN_ERR "%s: of_register_platform_driver failed (%i)\n", + __FILE__, ret); + uart_unregister_driver(&mpc52xx_uart_driver); + return ret; + } + + return 0; +} + +static void __exit +mpc52xx_uart_exit(void) +{ + if (psc_ops->fifoc_uninit) + psc_ops->fifoc_uninit(); + + of_unregister_platform_driver(&mpc52xx_uart_of_driver); + uart_unregister_driver(&mpc52xx_uart_driver); +} + + +module_init(mpc52xx_uart_init); +module_exit(mpc52xx_uart_exit); + +MODULE_AUTHOR("Sylvain Munaut "); +MODULE_DESCRIPTION("Freescale MPC52xx PSC UART"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c new file mode 100644 index 000000000000..6a9c6605666a --- /dev/null +++ b/drivers/tty/serial/mpsc.c @@ -0,0 +1,2159 @@ +/* + * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240, + * GT64260, MV64340, MV64360, GT96100, ... ). + * + * Author: Mark A. Greer + * + * Based on an old MPSC driver that was in the linuxppc tree. It appears to + * have been created by Chris Zankel (formerly of MontaVista) but there + * is no proper Copyright so I'm not sure. Apparently, parts were also + * taken from PPCBoot (now U-Boot). Also based on drivers/serial/8250.c + * by Russell King. + * + * 2004 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +/* + * The MPSC interface is much like a typical network controller's interface. + * That is, you set up separate rings of descriptors for transmitting and + * receiving data. There is also a pool of buffers with (one buffer per + * descriptor) that incoming data are dma'd into or outgoing data are dma'd + * out of. + * + * The MPSC requires two other controllers to be able to work. The Baud Rate + * Generator (BRG) provides a clock at programmable frequencies which determines + * the baud rate. The Serial DMA Controller (SDMA) takes incoming data from the + * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the + * MPSC. It is actually the SDMA interrupt that the driver uses to keep the + * transmit and receive "engines" going (i.e., indicate data has been + * transmitted or received). + * + * NOTES: + * + * 1) Some chips have an erratum where several regs cannot be + * read. To work around that, we keep a local copy of those regs in + * 'mpsc_port_info'. + * + * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr + * accesses system mem with coherency enabled. For that reason, the driver + * assumes that coherency for that ctlr has been disabled. This means + * that when in a cache coherent system, the driver has to manually manage + * the data cache on the areas that it touches because the dma_* macro are + * basically no-ops. + * + * 3) There is an erratum (on PPC) where you can't use the instruction to do + * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places + * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed. + * + * 4) AFAICT, hardware flow control isn't supported by the controller --MAG. + */ + + +#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MPSC_NUM_CTLRS 2 + +/* + * Descriptors and buffers must be cache line aligned. + * Buffers lengths must be multiple of cache line size. + * Number of Tx & Rx descriptors must be powers of 2. + */ +#define MPSC_RXR_ENTRIES 32 +#define MPSC_RXRE_SIZE dma_get_cache_alignment() +#define MPSC_RXR_SIZE (MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE) +#define MPSC_RXBE_SIZE dma_get_cache_alignment() +#define MPSC_RXB_SIZE (MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE) + +#define MPSC_TXR_ENTRIES 32 +#define MPSC_TXRE_SIZE dma_get_cache_alignment() +#define MPSC_TXR_SIZE (MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE) +#define MPSC_TXBE_SIZE dma_get_cache_alignment() +#define MPSC_TXB_SIZE (MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE) + +#define MPSC_DMA_ALLOC_SIZE (MPSC_RXR_SIZE + MPSC_RXB_SIZE + MPSC_TXR_SIZE \ + + MPSC_TXB_SIZE + dma_get_cache_alignment() /* for alignment */) + +/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */ +struct mpsc_rx_desc { + u16 bufsize; + u16 bytecnt; + u32 cmdstat; + u32 link; + u32 buf_ptr; +} __attribute((packed)); + +struct mpsc_tx_desc { + u16 bytecnt; + u16 shadow; + u32 cmdstat; + u32 link; + u32 buf_ptr; +} __attribute((packed)); + +/* + * Some regs that have the erratum that you can't read them are are shared + * between the two MPSC controllers. This struct contains those shared regs. + */ +struct mpsc_shared_regs { + phys_addr_t mpsc_routing_base_p; + phys_addr_t sdma_intr_base_p; + + void __iomem *mpsc_routing_base; + void __iomem *sdma_intr_base; + + u32 MPSC_MRR_m; + u32 MPSC_RCRR_m; + u32 MPSC_TCRR_m; + u32 SDMA_INTR_CAUSE_m; + u32 SDMA_INTR_MASK_m; +}; + +/* The main driver data structure */ +struct mpsc_port_info { + struct uart_port port; /* Overlay uart_port structure */ + + /* Internal driver state for this ctlr */ + u8 ready; + u8 rcv_data; + tcflag_t c_iflag; /* save termios->c_iflag */ + tcflag_t c_cflag; /* save termios->c_cflag */ + + /* Info passed in from platform */ + u8 mirror_regs; /* Need to mirror regs? */ + u8 cache_mgmt; /* Need manual cache mgmt? */ + u8 brg_can_tune; /* BRG has baud tuning? */ + u32 brg_clk_src; + u16 mpsc_max_idle; + int default_baud; + int default_bits; + int default_parity; + int default_flow; + + /* Physical addresses of various blocks of registers (from platform) */ + phys_addr_t mpsc_base_p; + phys_addr_t sdma_base_p; + phys_addr_t brg_base_p; + + /* Virtual addresses of various blocks of registers (from platform) */ + void __iomem *mpsc_base; + void __iomem *sdma_base; + void __iomem *brg_base; + + /* Descriptor ring and buffer allocations */ + void *dma_region; + dma_addr_t dma_region_p; + + dma_addr_t rxr; /* Rx descriptor ring */ + dma_addr_t rxr_p; /* Phys addr of rxr */ + u8 *rxb; /* Rx Ring I/O buf */ + u8 *rxb_p; /* Phys addr of rxb */ + u32 rxr_posn; /* First desc w/ Rx data */ + + dma_addr_t txr; /* Tx descriptor ring */ + dma_addr_t txr_p; /* Phys addr of txr */ + u8 *txb; /* Tx Ring I/O buf */ + u8 *txb_p; /* Phys addr of txb */ + int txr_head; /* Where new data goes */ + int txr_tail; /* Where sent data comes off */ + spinlock_t tx_lock; /* transmit lock */ + + /* Mirrored values of regs we can't read (if 'mirror_regs' set) */ + u32 MPSC_MPCR_m; + u32 MPSC_CHR_1_m; + u32 MPSC_CHR_2_m; + u32 MPSC_CHR_10_m; + u32 BRG_BCR_m; + struct mpsc_shared_regs *shared_regs; +}; + +/* Hooks to platform-specific code */ +int mpsc_platform_register_driver(void); +void mpsc_platform_unregister_driver(void); + +/* Hooks back in to mpsc common to be called by platform-specific code */ +struct mpsc_port_info *mpsc_device_probe(int index); +struct mpsc_port_info *mpsc_device_remove(int index); + +/* Main MPSC Configuration Register Offsets */ +#define MPSC_MMCRL 0x0000 +#define MPSC_MMCRH 0x0004 +#define MPSC_MPCR 0x0008 +#define MPSC_CHR_1 0x000c +#define MPSC_CHR_2 0x0010 +#define MPSC_CHR_3 0x0014 +#define MPSC_CHR_4 0x0018 +#define MPSC_CHR_5 0x001c +#define MPSC_CHR_6 0x0020 +#define MPSC_CHR_7 0x0024 +#define MPSC_CHR_8 0x0028 +#define MPSC_CHR_9 0x002c +#define MPSC_CHR_10 0x0030 +#define MPSC_CHR_11 0x0034 + +#define MPSC_MPCR_FRZ (1 << 9) +#define MPSC_MPCR_CL_5 0 +#define MPSC_MPCR_CL_6 1 +#define MPSC_MPCR_CL_7 2 +#define MPSC_MPCR_CL_8 3 +#define MPSC_MPCR_SBL_1 0 +#define MPSC_MPCR_SBL_2 1 + +#define MPSC_CHR_2_TEV (1<<1) +#define MPSC_CHR_2_TA (1<<7) +#define MPSC_CHR_2_TTCS (1<<9) +#define MPSC_CHR_2_REV (1<<17) +#define MPSC_CHR_2_RA (1<<23) +#define MPSC_CHR_2_CRD (1<<25) +#define MPSC_CHR_2_EH (1<<31) +#define MPSC_CHR_2_PAR_ODD 0 +#define MPSC_CHR_2_PAR_SPACE 1 +#define MPSC_CHR_2_PAR_EVEN 2 +#define MPSC_CHR_2_PAR_MARK 3 + +/* MPSC Signal Routing */ +#define MPSC_MRR 0x0000 +#define MPSC_RCRR 0x0004 +#define MPSC_TCRR 0x0008 + +/* Serial DMA Controller Interface Registers */ +#define SDMA_SDC 0x0000 +#define SDMA_SDCM 0x0008 +#define SDMA_RX_DESC 0x0800 +#define SDMA_RX_BUF_PTR 0x0808 +#define SDMA_SCRDP 0x0810 +#define SDMA_TX_DESC 0x0c00 +#define SDMA_SCTDP 0x0c10 +#define SDMA_SFTDP 0x0c14 + +#define SDMA_DESC_CMDSTAT_PE (1<<0) +#define SDMA_DESC_CMDSTAT_CDL (1<<1) +#define SDMA_DESC_CMDSTAT_FR (1<<3) +#define SDMA_DESC_CMDSTAT_OR (1<<6) +#define SDMA_DESC_CMDSTAT_BR (1<<9) +#define SDMA_DESC_CMDSTAT_MI (1<<10) +#define SDMA_DESC_CMDSTAT_A (1<<11) +#define SDMA_DESC_CMDSTAT_AM (1<<12) +#define SDMA_DESC_CMDSTAT_CT (1<<13) +#define SDMA_DESC_CMDSTAT_C (1<<14) +#define SDMA_DESC_CMDSTAT_ES (1<<15) +#define SDMA_DESC_CMDSTAT_L (1<<16) +#define SDMA_DESC_CMDSTAT_F (1<<17) +#define SDMA_DESC_CMDSTAT_P (1<<18) +#define SDMA_DESC_CMDSTAT_EI (1<<23) +#define SDMA_DESC_CMDSTAT_O (1<<31) + +#define SDMA_DESC_DFLT (SDMA_DESC_CMDSTAT_O \ + | SDMA_DESC_CMDSTAT_EI) + +#define SDMA_SDC_RFT (1<<0) +#define SDMA_SDC_SFM (1<<1) +#define SDMA_SDC_BLMR (1<<6) +#define SDMA_SDC_BLMT (1<<7) +#define SDMA_SDC_POVR (1<<8) +#define SDMA_SDC_RIFB (1<<9) + +#define SDMA_SDCM_ERD (1<<7) +#define SDMA_SDCM_AR (1<<15) +#define SDMA_SDCM_STD (1<<16) +#define SDMA_SDCM_TXD (1<<23) +#define SDMA_SDCM_AT (1<<31) + +#define SDMA_0_CAUSE_RXBUF (1<<0) +#define SDMA_0_CAUSE_RXERR (1<<1) +#define SDMA_0_CAUSE_TXBUF (1<<2) +#define SDMA_0_CAUSE_TXEND (1<<3) +#define SDMA_1_CAUSE_RXBUF (1<<8) +#define SDMA_1_CAUSE_RXERR (1<<9) +#define SDMA_1_CAUSE_TXBUF (1<<10) +#define SDMA_1_CAUSE_TXEND (1<<11) + +#define SDMA_CAUSE_RX_MASK (SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR \ + | SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR) +#define SDMA_CAUSE_TX_MASK (SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND \ + | SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND) + +/* SDMA Interrupt registers */ +#define SDMA_INTR_CAUSE 0x0000 +#define SDMA_INTR_MASK 0x0080 + +/* Baud Rate Generator Interface Registers */ +#define BRG_BCR 0x0000 +#define BRG_BTR 0x0004 + +/* + * Define how this driver is known to the outside (we've been assigned a + * range on the "Low-density serial ports" major). + */ +#define MPSC_MAJOR 204 +#define MPSC_MINOR_START 44 +#define MPSC_DRIVER_NAME "MPSC" +#define MPSC_DEV_NAME "ttyMM" +#define MPSC_VERSION "1.00" + +static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS]; +static struct mpsc_shared_regs mpsc_shared_regs; +static struct uart_driver mpsc_reg; + +static void mpsc_start_rx(struct mpsc_port_info *pi); +static void mpsc_free_ring_mem(struct mpsc_port_info *pi); +static void mpsc_release_port(struct uart_port *port); +/* + ****************************************************************************** + * + * Baud Rate Generator Routines (BRG) + * + ****************************************************************************** + */ +static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src) +{ + u32 v; + + v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); + v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18); + + if (pi->brg_can_tune) + v &= ~(1 << 25); + + if (pi->mirror_regs) + pi->BRG_BCR_m = v; + writel(v, pi->brg_base + BRG_BCR); + + writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000, + pi->brg_base + BRG_BTR); +} + +static void mpsc_brg_enable(struct mpsc_port_info *pi) +{ + u32 v; + + v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); + v |= (1 << 16); + + if (pi->mirror_regs) + pi->BRG_BCR_m = v; + writel(v, pi->brg_base + BRG_BCR); +} + +static void mpsc_brg_disable(struct mpsc_port_info *pi) +{ + u32 v; + + v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); + v &= ~(1 << 16); + + if (pi->mirror_regs) + pi->BRG_BCR_m = v; + writel(v, pi->brg_base + BRG_BCR); +} + +/* + * To set the baud, we adjust the CDV field in the BRG_BCR reg. + * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1. + * However, the input clock is divided by 16 in the MPSC b/c of how + * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our + * calculation by 16 to account for that. So the real calculation + * that accounts for the way the mpsc is set up is: + * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1. + */ +static void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud) +{ + u32 cdv = (pi->port.uartclk / (baud << 5)) - 1; + u32 v; + + mpsc_brg_disable(pi); + v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR); + v = (v & 0xffff0000) | (cdv & 0xffff); + + if (pi->mirror_regs) + pi->BRG_BCR_m = v; + writel(v, pi->brg_base + BRG_BCR); + mpsc_brg_enable(pi); +} + +/* + ****************************************************************************** + * + * Serial DMA Routines (SDMA) + * + ****************************************************************************** + */ + +static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size) +{ + u32 v; + + pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n", + pi->port.line, burst_size); + + burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */ + + if (burst_size < 2) + v = 0x0; /* 1 64-bit word */ + else if (burst_size < 4) + v = 0x1; /* 2 64-bit words */ + else if (burst_size < 8) + v = 0x2; /* 4 64-bit words */ + else + v = 0x3; /* 8 64-bit words */ + + writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12), + pi->sdma_base + SDMA_SDC); +} + +static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size) +{ + pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line, + burst_size); + + writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f, + pi->sdma_base + SDMA_SDC); + mpsc_sdma_burstsize(pi, burst_size); +} + +static u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask) +{ + u32 old, v; + + pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask); + + old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m : + readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); + + mask &= 0xf; + if (pi->port.line) + mask <<= 8; + v &= ~mask; + + if (pi->mirror_regs) + pi->shared_regs->SDMA_INTR_MASK_m = v; + writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); + + if (pi->port.line) + old >>= 8; + return old & 0xf; +} + +static void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask) +{ + u32 v; + + pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask); + + v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m + : readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); + + mask &= 0xf; + if (pi->port.line) + mask <<= 8; + v |= mask; + + if (pi->mirror_regs) + pi->shared_regs->SDMA_INTR_MASK_m = v; + writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK); +} + +static void mpsc_sdma_intr_ack(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line); + + if (pi->mirror_regs) + pi->shared_regs->SDMA_INTR_CAUSE_m = 0; + writeb(0x00, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE + + pi->port.line); +} + +static void mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi, + struct mpsc_rx_desc *rxre_p) +{ + pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n", + pi->port.line, (u32)rxre_p); + + writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP); +} + +static void mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi, + struct mpsc_tx_desc *txre_p) +{ + writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP); + writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP); +} + +static void mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val) +{ + u32 v; + + v = readl(pi->sdma_base + SDMA_SDCM); + if (val) + v |= val; + else + v = 0; + wmb(); + writel(v, pi->sdma_base + SDMA_SDCM); + wmb(); +} + +static uint mpsc_sdma_tx_active(struct mpsc_port_info *pi) +{ + return readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_TXD; +} + +static void mpsc_sdma_start_tx(struct mpsc_port_info *pi) +{ + struct mpsc_tx_desc *txre, *txre_p; + + /* If tx isn't running & there's a desc ready to go, start it */ + if (!mpsc_sdma_tx_active(pi)) { + txre = (struct mpsc_tx_desc *)(pi->txr + + (pi->txr_tail * MPSC_TXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, + DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)txre, + (ulong)txre + MPSC_TXRE_SIZE); +#endif + + if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) { + txre_p = (struct mpsc_tx_desc *) + (pi->txr_p + (pi->txr_tail * MPSC_TXRE_SIZE)); + + mpsc_sdma_set_tx_ring(pi, txre_p); + mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD); + } + } +} + +static void mpsc_sdma_stop(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line); + + /* Abort any SDMA transfers */ + mpsc_sdma_cmd(pi, 0); + mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT); + + /* Clear the SDMA current and first TX and RX pointers */ + mpsc_sdma_set_tx_ring(pi, NULL); + mpsc_sdma_set_rx_ring(pi, NULL); + + /* Disable interrupts */ + mpsc_sdma_intr_mask(pi, 0xf); + mpsc_sdma_intr_ack(pi); +} + +/* + ****************************************************************************** + * + * Multi-Protocol Serial Controller Routines (MPSC) + * + ****************************************************************************** + */ + +static void mpsc_hw_init(struct mpsc_port_info *pi) +{ + u32 v; + + pr_debug("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line); + + /* Set up clock routing */ + if (pi->mirror_regs) { + v = pi->shared_regs->MPSC_MRR_m; + v &= ~0x1c7; + pi->shared_regs->MPSC_MRR_m = v; + writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR); + + v = pi->shared_regs->MPSC_RCRR_m; + v = (v & ~0xf0f) | 0x100; + pi->shared_regs->MPSC_RCRR_m = v; + writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR); + + v = pi->shared_regs->MPSC_TCRR_m; + v = (v & ~0xf0f) | 0x100; + pi->shared_regs->MPSC_TCRR_m = v; + writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR); + } else { + v = readl(pi->shared_regs->mpsc_routing_base + MPSC_MRR); + v &= ~0x1c7; + writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR); + + v = readl(pi->shared_regs->mpsc_routing_base + MPSC_RCRR); + v = (v & ~0xf0f) | 0x100; + writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR); + + v = readl(pi->shared_regs->mpsc_routing_base + MPSC_TCRR); + v = (v & ~0xf0f) | 0x100; + writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR); + } + + /* Put MPSC in UART mode & enabel Tx/Rx egines */ + writel(0x000004c4, pi->mpsc_base + MPSC_MMCRL); + + /* No preamble, 16x divider, low-latency, */ + writel(0x04400400, pi->mpsc_base + MPSC_MMCRH); + mpsc_set_baudrate(pi, pi->default_baud); + + if (pi->mirror_regs) { + pi->MPSC_CHR_1_m = 0; + pi->MPSC_CHR_2_m = 0; + } + writel(0, pi->mpsc_base + MPSC_CHR_1); + writel(0, pi->mpsc_base + MPSC_CHR_2); + writel(pi->mpsc_max_idle, pi->mpsc_base + MPSC_CHR_3); + writel(0, pi->mpsc_base + MPSC_CHR_4); + writel(0, pi->mpsc_base + MPSC_CHR_5); + writel(0, pi->mpsc_base + MPSC_CHR_6); + writel(0, pi->mpsc_base + MPSC_CHR_7); + writel(0, pi->mpsc_base + MPSC_CHR_8); + writel(0, pi->mpsc_base + MPSC_CHR_9); + writel(0, pi->mpsc_base + MPSC_CHR_10); +} + +static void mpsc_enter_hunt(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line); + + if (pi->mirror_regs) { + writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_EH, + pi->mpsc_base + MPSC_CHR_2); + /* Erratum prevents reading CHR_2 so just delay for a while */ + udelay(100); + } else { + writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_EH, + pi->mpsc_base + MPSC_CHR_2); + + while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_EH) + udelay(10); + } +} + +static void mpsc_freeze(struct mpsc_port_info *pi) +{ + u32 v; + + pr_debug("mpsc_freeze[%d]: Freezing\n", pi->port.line); + + v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : + readl(pi->mpsc_base + MPSC_MPCR); + v |= MPSC_MPCR_FRZ; + + if (pi->mirror_regs) + pi->MPSC_MPCR_m = v; + writel(v, pi->mpsc_base + MPSC_MPCR); +} + +static void mpsc_unfreeze(struct mpsc_port_info *pi) +{ + u32 v; + + v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : + readl(pi->mpsc_base + MPSC_MPCR); + v &= ~MPSC_MPCR_FRZ; + + if (pi->mirror_regs) + pi->MPSC_MPCR_m = v; + writel(v, pi->mpsc_base + MPSC_MPCR); + + pr_debug("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line); +} + +static void mpsc_set_char_length(struct mpsc_port_info *pi, u32 len) +{ + u32 v; + + pr_debug("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line,len); + + v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : + readl(pi->mpsc_base + MPSC_MPCR); + v = (v & ~(0x3 << 12)) | ((len & 0x3) << 12); + + if (pi->mirror_regs) + pi->MPSC_MPCR_m = v; + writel(v, pi->mpsc_base + MPSC_MPCR); +} + +static void mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len) +{ + u32 v; + + pr_debug("mpsc_set_stop_bit_length[%d]: stop bits: %d\n", + pi->port.line, len); + + v = (pi->mirror_regs) ? pi->MPSC_MPCR_m : + readl(pi->mpsc_base + MPSC_MPCR); + + v = (v & ~(1 << 14)) | ((len & 0x1) << 14); + + if (pi->mirror_regs) + pi->MPSC_MPCR_m = v; + writel(v, pi->mpsc_base + MPSC_MPCR); +} + +static void mpsc_set_parity(struct mpsc_port_info *pi, u32 p) +{ + u32 v; + + pr_debug("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p); + + v = (pi->mirror_regs) ? pi->MPSC_CHR_2_m : + readl(pi->mpsc_base + MPSC_CHR_2); + + p &= 0x3; + v = (v & ~0xc000c) | (p << 18) | (p << 2); + + if (pi->mirror_regs) + pi->MPSC_CHR_2_m = v; + writel(v, pi->mpsc_base + MPSC_CHR_2); +} + +/* + ****************************************************************************** + * + * Driver Init Routines + * + ****************************************************************************** + */ + +static void mpsc_init_hw(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_init_hw[%d]: Initializing\n", pi->port.line); + + mpsc_brg_init(pi, pi->brg_clk_src); + mpsc_brg_enable(pi); + mpsc_sdma_init(pi, dma_get_cache_alignment()); /* burst a cacheline */ + mpsc_sdma_stop(pi); + mpsc_hw_init(pi); +} + +static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) +{ + int rc = 0; + + pr_debug("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n", + pi->port.line); + + if (!pi->dma_region) { + if (!dma_supported(pi->port.dev, 0xffffffff)) { + printk(KERN_ERR "MPSC: Inadequate DMA support\n"); + rc = -ENXIO; + } else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev, + MPSC_DMA_ALLOC_SIZE, + &pi->dma_region_p, GFP_KERNEL)) + == NULL) { + printk(KERN_ERR "MPSC: Can't alloc Desc region\n"); + rc = -ENOMEM; + } + } + + return rc; +} + +static void mpsc_free_ring_mem(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line); + + if (pi->dma_region) { + dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE, + pi->dma_region, pi->dma_region_p); + pi->dma_region = NULL; + pi->dma_region_p = (dma_addr_t)NULL; + } +} + +static void mpsc_init_rings(struct mpsc_port_info *pi) +{ + struct mpsc_rx_desc *rxre; + struct mpsc_tx_desc *txre; + dma_addr_t dp, dp_p; + u8 *bp, *bp_p; + int i; + + pr_debug("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line); + + BUG_ON(pi->dma_region == NULL); + + memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE); + + /* + * Descriptors & buffers are multiples of cacheline size and must be + * cacheline aligned. + */ + dp = ALIGN((u32)pi->dma_region, dma_get_cache_alignment()); + dp_p = ALIGN((u32)pi->dma_region_p, dma_get_cache_alignment()); + + /* + * Partition dma region into rx ring descriptor, rx buffers, + * tx ring descriptors, and tx buffers. + */ + pi->rxr = dp; + pi->rxr_p = dp_p; + dp += MPSC_RXR_SIZE; + dp_p += MPSC_RXR_SIZE; + + pi->rxb = (u8 *)dp; + pi->rxb_p = (u8 *)dp_p; + dp += MPSC_RXB_SIZE; + dp_p += MPSC_RXB_SIZE; + + pi->rxr_posn = 0; + + pi->txr = dp; + pi->txr_p = dp_p; + dp += MPSC_TXR_SIZE; + dp_p += MPSC_TXR_SIZE; + + pi->txb = (u8 *)dp; + pi->txb_p = (u8 *)dp_p; + + pi->txr_head = 0; + pi->txr_tail = 0; + + /* Init rx ring descriptors */ + dp = pi->rxr; + dp_p = pi->rxr_p; + bp = pi->rxb; + bp_p = pi->rxb_p; + + for (i = 0; i < MPSC_RXR_ENTRIES; i++) { + rxre = (struct mpsc_rx_desc *)dp; + + rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE); + rxre->bytecnt = cpu_to_be16(0); + rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O + | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F + | SDMA_DESC_CMDSTAT_L); + rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE); + rxre->buf_ptr = cpu_to_be32(bp_p); + + dp += MPSC_RXRE_SIZE; + dp_p += MPSC_RXRE_SIZE; + bp += MPSC_RXBE_SIZE; + bp_p += MPSC_RXBE_SIZE; + } + rxre->link = cpu_to_be32(pi->rxr_p); /* Wrap last back to first */ + + /* Init tx ring descriptors */ + dp = pi->txr; + dp_p = pi->txr_p; + bp = pi->txb; + bp_p = pi->txb_p; + + for (i = 0; i < MPSC_TXR_ENTRIES; i++) { + txre = (struct mpsc_tx_desc *)dp; + + txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE); + txre->buf_ptr = cpu_to_be32(bp_p); + + dp += MPSC_TXRE_SIZE; + dp_p += MPSC_TXRE_SIZE; + bp += MPSC_TXBE_SIZE; + bp_p += MPSC_TXBE_SIZE; + } + txre->link = cpu_to_be32(pi->txr_p); /* Wrap last back to first */ + + dma_cache_sync(pi->port.dev, (void *)pi->dma_region, + MPSC_DMA_ALLOC_SIZE, DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)pi->dma_region, + (ulong)pi->dma_region + + MPSC_DMA_ALLOC_SIZE); +#endif + + return; +} + +static void mpsc_uninit_rings(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_uninit_rings[%d]: Uninitializing rings\n",pi->port.line); + + BUG_ON(pi->dma_region == NULL); + + pi->rxr = 0; + pi->rxr_p = 0; + pi->rxb = NULL; + pi->rxb_p = NULL; + pi->rxr_posn = 0; + + pi->txr = 0; + pi->txr_p = 0; + pi->txb = NULL; + pi->txb_p = NULL; + pi->txr_head = 0; + pi->txr_tail = 0; +} + +static int mpsc_make_ready(struct mpsc_port_info *pi) +{ + int rc; + + pr_debug("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line); + + if (!pi->ready) { + mpsc_init_hw(pi); + if ((rc = mpsc_alloc_ring_mem(pi))) + return rc; + mpsc_init_rings(pi); + pi->ready = 1; + } + + return 0; +} + +#ifdef CONFIG_CONSOLE_POLL +static int serial_polled; +#endif + +/* + ****************************************************************************** + * + * Interrupt Handling Routines + * + ****************************************************************************** + */ + +static int mpsc_rx_intr(struct mpsc_port_info *pi) +{ + struct mpsc_rx_desc *rxre; + struct tty_struct *tty = pi->port.state->port.tty; + u32 cmdstat, bytes_in, i; + int rc = 0; + u8 *bp; + char flag = TTY_NORMAL; + + pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); + + rxre = (struct mpsc_rx_desc *)(pi->rxr + (pi->rxr_posn*MPSC_RXRE_SIZE)); + + dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, + DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + + /* + * Loop through Rx descriptors handling ones that have been completed. + */ + while (!((cmdstat = be32_to_cpu(rxre->cmdstat)) + & SDMA_DESC_CMDSTAT_O)) { + bytes_in = be16_to_cpu(rxre->bytecnt); +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return 0; + } +#endif + /* Following use of tty struct directly is deprecated */ + if (unlikely(tty_buffer_request_room(tty, bytes_in) + < bytes_in)) { + if (tty->low_latency) + tty_flip_buffer_push(tty); + /* + * If this failed then we will throw away the bytes + * but must do so to clear interrupts. + */ + } + + bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); + dma_cache_sync(pi->port.dev, (void *)bp, MPSC_RXBE_SIZE, + DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)bp, + (ulong)bp + MPSC_RXBE_SIZE); +#endif + + /* + * Other than for parity error, the manual provides little + * info on what data will be in a frame flagged by any of + * these errors. For parity error, it is the last byte in + * the buffer that had the error. As for the rest, I guess + * we'll assume there is no data in the buffer. + * If there is...it gets lost. + */ + if (unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR + | SDMA_DESC_CMDSTAT_FR + | SDMA_DESC_CMDSTAT_OR))) { + + pi->port.icount.rx++; + + if (cmdstat & SDMA_DESC_CMDSTAT_BR) { /* Break */ + pi->port.icount.brk++; + + if (uart_handle_break(&pi->port)) + goto next_frame; + } else if (cmdstat & SDMA_DESC_CMDSTAT_FR) { + pi->port.icount.frame++; + } else if (cmdstat & SDMA_DESC_CMDSTAT_OR) { + pi->port.icount.overrun++; + } + + cmdstat &= pi->port.read_status_mask; + + if (cmdstat & SDMA_DESC_CMDSTAT_BR) + flag = TTY_BREAK; + else if (cmdstat & SDMA_DESC_CMDSTAT_FR) + flag = TTY_FRAME; + else if (cmdstat & SDMA_DESC_CMDSTAT_OR) + flag = TTY_OVERRUN; + else if (cmdstat & SDMA_DESC_CMDSTAT_PE) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(&pi->port, *bp)) { + bp++; + bytes_in--; +#ifdef CONFIG_CONSOLE_POLL + if (unlikely(serial_polled)) { + serial_polled = 0; + return 0; + } +#endif + goto next_frame; + } + + if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR + | SDMA_DESC_CMDSTAT_FR + | SDMA_DESC_CMDSTAT_OR))) + && !(cmdstat & pi->port.ignore_status_mask)) { + tty_insert_flip_char(tty, *bp, flag); + } else { + for (i=0; iport.icount.rx += bytes_in; + } + +next_frame: + rxre->bytecnt = cpu_to_be16(0); + wmb(); + rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O + | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F + | SDMA_DESC_CMDSTAT_L); + wmb(); + dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, + DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + + /* Advance to next descriptor */ + pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1); + rxre = (struct mpsc_rx_desc *) + (pi->rxr + (pi->rxr_posn * MPSC_RXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, + DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + rc = 1; + } + + /* Restart rx engine, if its stopped */ + if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) + mpsc_start_rx(pi); + + tty_flip_buffer_push(tty); + return rc; +} + +static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr) +{ + struct mpsc_tx_desc *txre; + + txre = (struct mpsc_tx_desc *)(pi->txr + + (pi->txr_head * MPSC_TXRE_SIZE)); + + txre->bytecnt = cpu_to_be16(count); + txre->shadow = txre->bytecnt; + wmb(); /* ensure cmdstat is last field updated */ + txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F + | SDMA_DESC_CMDSTAT_L + | ((intr) ? SDMA_DESC_CMDSTAT_EI : 0)); + wmb(); + dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, + DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)txre, + (ulong)txre + MPSC_TXRE_SIZE); +#endif +} + +static void mpsc_copy_tx_data(struct mpsc_port_info *pi) +{ + struct circ_buf *xmit = &pi->port.state->xmit; + u8 *bp; + u32 i; + + /* Make sure the desc ring isn't full */ + while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES) + < (MPSC_TXR_ENTRIES - 1)) { + if (pi->port.x_char) { + /* + * Ideally, we should use the TCS field in + * CHR_1 to put the x_char out immediately but + * errata prevents us from being able to read + * CHR_2 to know that its safe to write to + * CHR_1. Instead, just put it in-band with + * all the other Tx data. + */ + bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); + *bp = pi->port.x_char; + pi->port.x_char = 0; + i = 1; + } else if (!uart_circ_empty(xmit) + && !uart_tx_stopped(&pi->port)) { + i = min((u32)MPSC_TXBE_SIZE, + (u32)uart_circ_chars_pending(xmit)); + i = min(i, (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail, + UART_XMIT_SIZE)); + bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); + memcpy(bp, &xmit->buf[xmit->tail], i); + xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&pi->port); + } else { /* All tx data copied into ring bufs */ + return; + } + + dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, + DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)bp, + (ulong)bp + MPSC_TXBE_SIZE); +#endif + mpsc_setup_tx_desc(pi, i, 1); + + /* Advance to next descriptor */ + pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); + } +} + +static int mpsc_tx_intr(struct mpsc_port_info *pi) +{ + struct mpsc_tx_desc *txre; + int rc = 0; + unsigned long iflags; + + spin_lock_irqsave(&pi->tx_lock, iflags); + + if (!mpsc_sdma_tx_active(pi)) { + txre = (struct mpsc_tx_desc *)(pi->txr + + (pi->txr_tail * MPSC_TXRE_SIZE)); + + dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, + DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)txre, + (ulong)txre + MPSC_TXRE_SIZE); +#endif + + while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) { + rc = 1; + pi->port.icount.tx += be16_to_cpu(txre->bytecnt); + pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1); + + /* If no more data to tx, fall out of loop */ + if (pi->txr_head == pi->txr_tail) + break; + + txre = (struct mpsc_tx_desc *)(pi->txr + + (pi->txr_tail * MPSC_TXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)txre, + MPSC_TXRE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)txre, + (ulong)txre + MPSC_TXRE_SIZE); +#endif + } + + mpsc_copy_tx_data(pi); + mpsc_sdma_start_tx(pi); /* start next desc if ready */ + } + + spin_unlock_irqrestore(&pi->tx_lock, iflags); + return rc; +} + +/* + * This is the driver's interrupt handler. To avoid a race, we first clear + * the interrupt, then handle any completed Rx/Tx descriptors. When done + * handling those descriptors, we restart the Rx/Tx engines if they're stopped. + */ +static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id) +{ + struct mpsc_port_info *pi = dev_id; + ulong iflags; + int rc = IRQ_NONE; + + pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line); + + spin_lock_irqsave(&pi->port.lock, iflags); + mpsc_sdma_intr_ack(pi); + if (mpsc_rx_intr(pi)) + rc = IRQ_HANDLED; + if (mpsc_tx_intr(pi)) + rc = IRQ_HANDLED; + spin_unlock_irqrestore(&pi->port.lock, iflags); + + pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line); + return rc; +} + +/* + ****************************************************************************** + * + * serial_core.c Interface routines + * + ****************************************************************************** + */ +static uint mpsc_tx_empty(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + ulong iflags; + uint rc; + + spin_lock_irqsave(&pi->port.lock, iflags); + rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT; + spin_unlock_irqrestore(&pi->port.lock, iflags); + + return rc; +} + +static void mpsc_set_mctrl(struct uart_port *port, uint mctrl) +{ + /* Have no way to set modem control lines AFAICT */ +} + +static uint mpsc_get_mctrl(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 mflags, status; + + status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m + : readl(pi->mpsc_base + MPSC_CHR_10); + + mflags = 0; + if (status & 0x1) + mflags |= TIOCM_CTS; + if (status & 0x2) + mflags |= TIOCM_CAR; + + return mflags | TIOCM_DSR; /* No way to tell if DSR asserted */ +} + +static void mpsc_stop_tx(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + pr_debug("mpsc_stop_tx[%d]\n", port->line); + + mpsc_freeze(pi); +} + +static void mpsc_start_tx(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + unsigned long iflags; + + spin_lock_irqsave(&pi->tx_lock, iflags); + + mpsc_unfreeze(pi); + mpsc_copy_tx_data(pi); + mpsc_sdma_start_tx(pi); + + spin_unlock_irqrestore(&pi->tx_lock, iflags); + + pr_debug("mpsc_start_tx[%d]\n", port->line); +} + +static void mpsc_start_rx(struct mpsc_port_info *pi) +{ + pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line); + + if (pi->rcv_data) { + mpsc_enter_hunt(pi); + mpsc_sdma_cmd(pi, SDMA_SDCM_ERD); + } +} + +static void mpsc_stop_rx(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line); + + if (pi->mirror_regs) { + writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA, + pi->mpsc_base + MPSC_CHR_2); + /* Erratum prevents reading CHR_2 so just delay for a while */ + udelay(100); + } else { + writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA, + pi->mpsc_base + MPSC_CHR_2); + + while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA) + udelay(10); + } + + mpsc_sdma_cmd(pi, SDMA_SDCM_AR); +} + +static void mpsc_enable_ms(struct uart_port *port) +{ +} + +static void mpsc_break_ctl(struct uart_port *port, int ctl) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + ulong flags; + u32 v; + + v = ctl ? 0x00ff0000 : 0; + + spin_lock_irqsave(&pi->port.lock, flags); + if (pi->mirror_regs) + pi->MPSC_CHR_1_m = v; + writel(v, pi->mpsc_base + MPSC_CHR_1); + spin_unlock_irqrestore(&pi->port.lock, flags); +} + +static int mpsc_startup(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 flag = 0; + int rc; + + pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n", + port->line, pi->port.irq); + + if ((rc = mpsc_make_ready(pi)) == 0) { + /* Setup IRQ handler */ + mpsc_sdma_intr_ack(pi); + + /* If irq's are shared, need to set flag */ + if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq) + flag = IRQF_SHARED; + + if (request_irq(pi->port.irq, mpsc_sdma_intr, flag, + "mpsc-sdma", pi)) + printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n", + pi->port.irq); + + mpsc_sdma_intr_unmask(pi, 0xf); + mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p + + (pi->rxr_posn * MPSC_RXRE_SIZE))); + } + + return rc; +} + +static void mpsc_shutdown(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line); + + mpsc_sdma_stop(pi); + free_irq(pi->port.irq, pi); +} + +static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 baud; + ulong flags; + u32 chr_bits, stop_bits, par; + + pi->c_iflag = termios->c_iflag; + pi->c_cflag = termios->c_cflag; + + switch (termios->c_cflag & CSIZE) { + case CS5: + chr_bits = MPSC_MPCR_CL_5; + break; + case CS6: + chr_bits = MPSC_MPCR_CL_6; + break; + case CS7: + chr_bits = MPSC_MPCR_CL_7; + break; + case CS8: + default: + chr_bits = MPSC_MPCR_CL_8; + break; + } + + if (termios->c_cflag & CSTOPB) + stop_bits = MPSC_MPCR_SBL_2; + else + stop_bits = MPSC_MPCR_SBL_1; + + par = MPSC_CHR_2_PAR_EVEN; + if (termios->c_cflag & PARENB) + if (termios->c_cflag & PARODD) + par = MPSC_CHR_2_PAR_ODD; +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + par = MPSC_CHR_2_PAR_MARK; + else + par = MPSC_CHR_2_PAR_SPACE; + } +#endif + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); + + spin_lock_irqsave(&pi->port.lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + + mpsc_set_char_length(pi, chr_bits); + mpsc_set_stop_bit_length(pi, stop_bits); + mpsc_set_parity(pi, par); + mpsc_set_baudrate(pi, baud); + + /* Characters/events to read */ + pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR; + + if (termios->c_iflag & INPCK) + pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE + | SDMA_DESC_CMDSTAT_FR; + + if (termios->c_iflag & (BRKINT | PARMRK)) + pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR; + + /* Characters/events to ignore */ + pi->port.ignore_status_mask = 0; + + if (termios->c_iflag & IGNPAR) + pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE + | SDMA_DESC_CMDSTAT_FR; + + if (termios->c_iflag & IGNBRK) { + pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR; + + if (termios->c_iflag & IGNPAR) + pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR; + } + + if ((termios->c_cflag & CREAD)) { + if (!pi->rcv_data) { + pi->rcv_data = 1; + mpsc_start_rx(pi); + } + } else if (pi->rcv_data) { + mpsc_stop_rx(port); + pi->rcv_data = 0; + } + + spin_unlock_irqrestore(&pi->port.lock, flags); +} + +static const char *mpsc_type(struct uart_port *port) +{ + pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME); + return MPSC_DRIVER_NAME; +} + +static int mpsc_request_port(struct uart_port *port) +{ + /* Should make chip/platform specific call */ + return 0; +} + +static void mpsc_release_port(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + + if (pi->ready) { + mpsc_uninit_rings(pi); + mpsc_free_ring_mem(pi); + pi->ready = 0; + } +} + +static void mpsc_config_port(struct uart_port *port, int flags) +{ +} + +static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + int rc = 0; + + pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line); + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC) + rc = -EINVAL; + else if (pi->port.irq != ser->irq) + rc = -EINVAL; + else if (ser->io_type != SERIAL_IO_MEM) + rc = -EINVAL; + else if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */ + rc = -EINVAL; + else if ((void *)pi->port.mapbase != ser->iomem_base) + rc = -EINVAL; + else if (pi->port.iobase != ser->port) + rc = -EINVAL; + else if (ser->hub6 != 0) + rc = -EINVAL; + + return rc; +} +#ifdef CONFIG_CONSOLE_POLL +/* Serial polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static char poll_buf[2048]; +static int poll_ptr; +static int poll_cnt; +static void mpsc_put_poll_char(struct uart_port *port, + unsigned char c); + +static int mpsc_get_poll_char(struct uart_port *port) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + struct mpsc_rx_desc *rxre; + u32 cmdstat, bytes_in, i; + u8 *bp; + + if (!serial_polled) + serial_polled = 1; + + pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line); + + if (poll_cnt) { + poll_cnt--; + return poll_buf[poll_ptr++]; + } + poll_ptr = 0; + poll_cnt = 0; + + while (poll_cnt == 0) { + rxre = (struct mpsc_rx_desc *)(pi->rxr + + (pi->rxr_posn*MPSC_RXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)rxre, + MPSC_RXRE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + /* + * Loop through Rx descriptors handling ones that have + * been completed. + */ + while (poll_cnt == 0 && + !((cmdstat = be32_to_cpu(rxre->cmdstat)) & + SDMA_DESC_CMDSTAT_O)){ + bytes_in = be16_to_cpu(rxre->bytecnt); + bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE); + dma_cache_sync(pi->port.dev, (void *) bp, + MPSC_RXBE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)bp, + (ulong)bp + MPSC_RXBE_SIZE); +#endif + if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR | + SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) && + !(cmdstat & pi->port.ignore_status_mask)) { + poll_buf[poll_cnt] = *bp; + poll_cnt++; + } else { + for (i = 0; i < bytes_in; i++) { + poll_buf[poll_cnt] = *bp++; + poll_cnt++; + } + pi->port.icount.rx += bytes_in; + } + rxre->bytecnt = cpu_to_be16(0); + wmb(); + rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | + SDMA_DESC_CMDSTAT_EI | + SDMA_DESC_CMDSTAT_F | + SDMA_DESC_CMDSTAT_L); + wmb(); + dma_cache_sync(pi->port.dev, (void *)rxre, + MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + + /* Advance to next descriptor */ + pi->rxr_posn = (pi->rxr_posn + 1) & + (MPSC_RXR_ENTRIES - 1); + rxre = (struct mpsc_rx_desc *)(pi->rxr + + (pi->rxr_posn * MPSC_RXRE_SIZE)); + dma_cache_sync(pi->port.dev, (void *)rxre, + MPSC_RXRE_SIZE, DMA_FROM_DEVICE); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + invalidate_dcache_range((ulong)rxre, + (ulong)rxre + MPSC_RXRE_SIZE); +#endif + } + + /* Restart rx engine, if its stopped */ + if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) + mpsc_start_rx(pi); + } + if (poll_cnt) { + poll_cnt--; + return poll_buf[poll_ptr++]; + } + + return 0; +} + + +static void mpsc_put_poll_char(struct uart_port *port, + unsigned char c) +{ + struct mpsc_port_info *pi = (struct mpsc_port_info *)port; + u32 data; + + data = readl(pi->mpsc_base + MPSC_MPCR); + writeb(c, pi->mpsc_base + MPSC_CHR_1); + mb(); + data = readl(pi->mpsc_base + MPSC_CHR_2); + data |= MPSC_CHR_2_TTCS; + writel(data, pi->mpsc_base + MPSC_CHR_2); + mb(); + + while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS); +} +#endif + +static struct uart_ops mpsc_pops = { + .tx_empty = mpsc_tx_empty, + .set_mctrl = mpsc_set_mctrl, + .get_mctrl = mpsc_get_mctrl, + .stop_tx = mpsc_stop_tx, + .start_tx = mpsc_start_tx, + .stop_rx = mpsc_stop_rx, + .enable_ms = mpsc_enable_ms, + .break_ctl = mpsc_break_ctl, + .startup = mpsc_startup, + .shutdown = mpsc_shutdown, + .set_termios = mpsc_set_termios, + .type = mpsc_type, + .release_port = mpsc_release_port, + .request_port = mpsc_request_port, + .config_port = mpsc_config_port, + .verify_port = mpsc_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = mpsc_get_poll_char, + .poll_put_char = mpsc_put_poll_char, +#endif +}; + +/* + ****************************************************************************** + * + * Console Interface Routines + * + ****************************************************************************** + */ + +#ifdef CONFIG_SERIAL_MPSC_CONSOLE +static void mpsc_console_write(struct console *co, const char *s, uint count) +{ + struct mpsc_port_info *pi = &mpsc_ports[co->index]; + u8 *bp, *dp, add_cr = 0; + int i; + unsigned long iflags; + + spin_lock_irqsave(&pi->tx_lock, iflags); + + while (pi->txr_head != pi->txr_tail) { + while (mpsc_sdma_tx_active(pi)) + udelay(100); + mpsc_sdma_intr_ack(pi); + mpsc_tx_intr(pi); + } + + while (mpsc_sdma_tx_active(pi)) + udelay(100); + + while (count > 0) { + bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); + + for (i = 0; i < MPSC_TXBE_SIZE; i++) { + if (count == 0) + break; + + if (add_cr) { + *(dp++) = '\r'; + add_cr = 0; + } else { + *(dp++) = *s; + + if (*(s++) == '\n') { /* add '\r' after '\n' */ + add_cr = 1; + count++; + } + } + + count--; + } + + dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, + DMA_BIDIRECTIONAL); +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ + flush_dcache_range((ulong)bp, + (ulong)bp + MPSC_TXBE_SIZE); +#endif + mpsc_setup_tx_desc(pi, i, 0); + pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); + mpsc_sdma_start_tx(pi); + + while (mpsc_sdma_tx_active(pi)) + udelay(100); + + pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1); + } + + spin_unlock_irqrestore(&pi->tx_lock, iflags); +} + +static int __init mpsc_console_setup(struct console *co, char *options) +{ + struct mpsc_port_info *pi; + int baud, bits, parity, flow; + + pr_debug("mpsc_console_setup[%d]: options: %s\n", co->index, options); + + if (co->index >= MPSC_NUM_CTLRS) + co->index = 0; + + pi = &mpsc_ports[co->index]; + + baud = pi->default_baud; + bits = pi->default_bits; + parity = pi->default_parity; + flow = pi->default_flow; + + if (!pi->port.ops) + return -ENODEV; + + spin_lock_init(&pi->port.lock); /* Temporary fix--copied from 8250.c */ + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&pi->port, co, baud, parity, bits, flow); +} + +static struct console mpsc_console = { + .name = MPSC_DEV_NAME, + .write = mpsc_console_write, + .device = uart_console_device, + .setup = mpsc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mpsc_reg, +}; + +static int __init mpsc_late_console_init(void) +{ + pr_debug("mpsc_late_console_init: Enter\n"); + + if (!(mpsc_console.flags & CON_ENABLED)) + register_console(&mpsc_console); + return 0; +} + +late_initcall(mpsc_late_console_init); + +#define MPSC_CONSOLE &mpsc_console +#else +#define MPSC_CONSOLE NULL +#endif +/* + ****************************************************************************** + * + * Dummy Platform Driver to extract & map shared register regions + * + ****************************************************************************** + */ +static void mpsc_resource_err(char *s) +{ + printk(KERN_WARNING "MPSC: Platform device resource error in %s\n", s); +} + +static int mpsc_shared_map_regs(struct platform_device *pd) +{ + struct resource *r; + + if ((r = platform_get_resource(pd, IORESOURCE_MEM, + MPSC_ROUTING_BASE_ORDER)) + && request_mem_region(r->start, + MPSC_ROUTING_REG_BLOCK_SIZE, + "mpsc_routing_regs")) { + mpsc_shared_regs.mpsc_routing_base = ioremap(r->start, + MPSC_ROUTING_REG_BLOCK_SIZE); + mpsc_shared_regs.mpsc_routing_base_p = r->start; + } else { + mpsc_resource_err("MPSC routing base"); + return -ENOMEM; + } + + if ((r = platform_get_resource(pd, IORESOURCE_MEM, + MPSC_SDMA_INTR_BASE_ORDER)) + && request_mem_region(r->start, + MPSC_SDMA_INTR_REG_BLOCK_SIZE, + "sdma_intr_regs")) { + mpsc_shared_regs.sdma_intr_base = ioremap(r->start, + MPSC_SDMA_INTR_REG_BLOCK_SIZE); + mpsc_shared_regs.sdma_intr_base_p = r->start; + } else { + iounmap(mpsc_shared_regs.mpsc_routing_base); + release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, + MPSC_ROUTING_REG_BLOCK_SIZE); + mpsc_resource_err("SDMA intr base"); + return -ENOMEM; + } + + return 0; +} + +static void mpsc_shared_unmap_regs(void) +{ + if (!mpsc_shared_regs.mpsc_routing_base) { + iounmap(mpsc_shared_regs.mpsc_routing_base); + release_mem_region(mpsc_shared_regs.mpsc_routing_base_p, + MPSC_ROUTING_REG_BLOCK_SIZE); + } + if (!mpsc_shared_regs.sdma_intr_base) { + iounmap(mpsc_shared_regs.sdma_intr_base); + release_mem_region(mpsc_shared_regs.sdma_intr_base_p, + MPSC_SDMA_INTR_REG_BLOCK_SIZE); + } + + mpsc_shared_regs.mpsc_routing_base = NULL; + mpsc_shared_regs.sdma_intr_base = NULL; + + mpsc_shared_regs.mpsc_routing_base_p = 0; + mpsc_shared_regs.sdma_intr_base_p = 0; +} + +static int mpsc_shared_drv_probe(struct platform_device *dev) +{ + struct mpsc_shared_pdata *pdata; + int rc = -ENODEV; + + if (dev->id == 0) { + if (!(rc = mpsc_shared_map_regs(dev))) { + pdata = (struct mpsc_shared_pdata *) + dev->dev.platform_data; + + mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val; + mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val; + mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val; + mpsc_shared_regs.SDMA_INTR_CAUSE_m = + pdata->intr_cause_val; + mpsc_shared_regs.SDMA_INTR_MASK_m = + pdata->intr_mask_val; + + rc = 0; + } + } + + return rc; +} + +static int mpsc_shared_drv_remove(struct platform_device *dev) +{ + int rc = -ENODEV; + + if (dev->id == 0) { + mpsc_shared_unmap_regs(); + mpsc_shared_regs.MPSC_MRR_m = 0; + mpsc_shared_regs.MPSC_RCRR_m = 0; + mpsc_shared_regs.MPSC_TCRR_m = 0; + mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0; + mpsc_shared_regs.SDMA_INTR_MASK_m = 0; + rc = 0; + } + + return rc; +} + +static struct platform_driver mpsc_shared_driver = { + .probe = mpsc_shared_drv_probe, + .remove = mpsc_shared_drv_remove, + .driver = { + .name = MPSC_SHARED_NAME, + }, +}; + +/* + ****************************************************************************** + * + * Driver Interface Routines + * + ****************************************************************************** + */ +static struct uart_driver mpsc_reg = { + .owner = THIS_MODULE, + .driver_name = MPSC_DRIVER_NAME, + .dev_name = MPSC_DEV_NAME, + .major = MPSC_MAJOR, + .minor = MPSC_MINOR_START, + .nr = MPSC_NUM_CTLRS, + .cons = MPSC_CONSOLE, +}; + +static int mpsc_drv_map_regs(struct mpsc_port_info *pi, + struct platform_device *pd) +{ + struct resource *r; + + if ((r = platform_get_resource(pd, IORESOURCE_MEM, MPSC_BASE_ORDER)) + && request_mem_region(r->start, MPSC_REG_BLOCK_SIZE, + "mpsc_regs")) { + pi->mpsc_base = ioremap(r->start, MPSC_REG_BLOCK_SIZE); + pi->mpsc_base_p = r->start; + } else { + mpsc_resource_err("MPSC base"); + goto err; + } + + if ((r = platform_get_resource(pd, IORESOURCE_MEM, + MPSC_SDMA_BASE_ORDER)) + && request_mem_region(r->start, + MPSC_SDMA_REG_BLOCK_SIZE, "sdma_regs")) { + pi->sdma_base = ioremap(r->start,MPSC_SDMA_REG_BLOCK_SIZE); + pi->sdma_base_p = r->start; + } else { + mpsc_resource_err("SDMA base"); + if (pi->mpsc_base) { + iounmap(pi->mpsc_base); + pi->mpsc_base = NULL; + } + goto err; + } + + if ((r = platform_get_resource(pd,IORESOURCE_MEM,MPSC_BRG_BASE_ORDER)) + && request_mem_region(r->start, + MPSC_BRG_REG_BLOCK_SIZE, "brg_regs")) { + pi->brg_base = ioremap(r->start, MPSC_BRG_REG_BLOCK_SIZE); + pi->brg_base_p = r->start; + } else { + mpsc_resource_err("BRG base"); + if (pi->mpsc_base) { + iounmap(pi->mpsc_base); + pi->mpsc_base = NULL; + } + if (pi->sdma_base) { + iounmap(pi->sdma_base); + pi->sdma_base = NULL; + } + goto err; + } + return 0; + +err: + return -ENOMEM; +} + +static void mpsc_drv_unmap_regs(struct mpsc_port_info *pi) +{ + if (!pi->mpsc_base) { + iounmap(pi->mpsc_base); + release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE); + } + if (!pi->sdma_base) { + iounmap(pi->sdma_base); + release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE); + } + if (!pi->brg_base) { + iounmap(pi->brg_base); + release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE); + } + + pi->mpsc_base = NULL; + pi->sdma_base = NULL; + pi->brg_base = NULL; + + pi->mpsc_base_p = 0; + pi->sdma_base_p = 0; + pi->brg_base_p = 0; +} + +static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi, + struct platform_device *pd, int num) +{ + struct mpsc_pdata *pdata; + + pdata = (struct mpsc_pdata *)pd->dev.platform_data; + + pi->port.uartclk = pdata->brg_clk_freq; + pi->port.iotype = UPIO_MEM; + pi->port.line = num; + pi->port.type = PORT_MPSC; + pi->port.fifosize = MPSC_TXBE_SIZE; + pi->port.membase = pi->mpsc_base; + pi->port.mapbase = (ulong)pi->mpsc_base; + pi->port.ops = &mpsc_pops; + + pi->mirror_regs = pdata->mirror_regs; + pi->cache_mgmt = pdata->cache_mgmt; + pi->brg_can_tune = pdata->brg_can_tune; + pi->brg_clk_src = pdata->brg_clk_src; + pi->mpsc_max_idle = pdata->max_idle; + pi->default_baud = pdata->default_baud; + pi->default_bits = pdata->default_bits; + pi->default_parity = pdata->default_parity; + pi->default_flow = pdata->default_flow; + + /* Initial values of mirrored regs */ + pi->MPSC_CHR_1_m = pdata->chr_1_val; + pi->MPSC_CHR_2_m = pdata->chr_2_val; + pi->MPSC_CHR_10_m = pdata->chr_10_val; + pi->MPSC_MPCR_m = pdata->mpcr_val; + pi->BRG_BCR_m = pdata->bcr_val; + + pi->shared_regs = &mpsc_shared_regs; + + pi->port.irq = platform_get_irq(pd, 0); +} + +static int mpsc_drv_probe(struct platform_device *dev) +{ + struct mpsc_port_info *pi; + int rc = -ENODEV; + + pr_debug("mpsc_drv_probe: Adding MPSC %d\n", dev->id); + + if (dev->id < MPSC_NUM_CTLRS) { + pi = &mpsc_ports[dev->id]; + + if (!(rc = mpsc_drv_map_regs(pi, dev))) { + mpsc_drv_get_platform_data(pi, dev, dev->id); + pi->port.dev = &dev->dev; + + if (!(rc = mpsc_make_ready(pi))) { + spin_lock_init(&pi->tx_lock); + if (!(rc = uart_add_one_port(&mpsc_reg, + &pi->port))) { + rc = 0; + } else { + mpsc_release_port((struct uart_port *) + pi); + mpsc_drv_unmap_regs(pi); + } + } else { + mpsc_drv_unmap_regs(pi); + } + } + } + + return rc; +} + +static int mpsc_drv_remove(struct platform_device *dev) +{ + pr_debug("mpsc_drv_exit: Removing MPSC %d\n", dev->id); + + if (dev->id < MPSC_NUM_CTLRS) { + uart_remove_one_port(&mpsc_reg, &mpsc_ports[dev->id].port); + mpsc_release_port((struct uart_port *) + &mpsc_ports[dev->id].port); + mpsc_drv_unmap_regs(&mpsc_ports[dev->id]); + return 0; + } else { + return -ENODEV; + } +} + +static struct platform_driver mpsc_driver = { + .probe = mpsc_drv_probe, + .remove = mpsc_drv_remove, + .driver = { + .name = MPSC_CTLR_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mpsc_drv_init(void) +{ + int rc; + + printk(KERN_INFO "Serial: MPSC driver\n"); + + memset(mpsc_ports, 0, sizeof(mpsc_ports)); + memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); + + if (!(rc = uart_register_driver(&mpsc_reg))) { + if (!(rc = platform_driver_register(&mpsc_shared_driver))) { + if ((rc = platform_driver_register(&mpsc_driver))) { + platform_driver_unregister(&mpsc_shared_driver); + uart_unregister_driver(&mpsc_reg); + } + } else { + uart_unregister_driver(&mpsc_reg); + } + } + + return rc; +} + +static void __exit mpsc_drv_exit(void) +{ + platform_driver_unregister(&mpsc_driver); + platform_driver_unregister(&mpsc_shared_driver); + uart_unregister_driver(&mpsc_reg); + memset(mpsc_ports, 0, sizeof(mpsc_ports)); + memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); +} + +module_init(mpsc_drv_init); +module_exit(mpsc_drv_exit); + +MODULE_AUTHOR("Mark A. Greer "); +MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver"); +MODULE_VERSION(MPSC_VERSION); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR); +MODULE_ALIAS("platform:" MPSC_CTLR_NAME); diff --git a/drivers/tty/serial/mrst_max3110.c b/drivers/tty/serial/mrst_max3110.c new file mode 100644 index 000000000000..b62857bf2fdb --- /dev/null +++ b/drivers/tty/serial/mrst_max3110.c @@ -0,0 +1,919 @@ +/* + * mrst_max3110.c - spi uart protocol driver for Maxim 3110 + * + * Copyright (c) 2008-2010, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Note: + * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has + * 1 word. If SPI master controller doesn't support sclk frequency change, + * then the char need be sent out one by one with some delay + * + * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE + * interrupt for a low speed UART device + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mrst_max3110.h" + +#define PR_FMT "mrst_max3110: " + +#define UART_TX_NEEDED 1 +#define CON_TX_NEEDED 2 +#define BIT_IRQ_PENDING 3 + +struct uart_max3110 { + struct uart_port port; + struct spi_device *spi; + char name[24]; + + wait_queue_head_t wq; + struct task_struct *main_thread; + struct task_struct *read_thread; + struct mutex thread_mutex;; + + u32 baud; + u16 cur_conf; + u8 clock; + u8 parity, word_7bits; + u16 irq; + + unsigned long uart_flags; + + /* console related */ + struct circ_buf con_xmit; +}; + +/* global data structure, may need be removed */ +static struct uart_max3110 *pmax; + +static void receive_chars(struct uart_max3110 *max, + unsigned char *str, int len); +static int max3110_read_multi(struct uart_max3110 *max, u8 *buf); +static void max3110_con_receive(struct uart_max3110 *max); + +static int max3110_write_then_read(struct uart_max3110 *max, + const void *txbuf, void *rxbuf, unsigned len, int always_fast) +{ + struct spi_device *spi = max->spi; + struct spi_message message; + struct spi_transfer x; + int ret; + + spi_message_init(&message); + memset(&x, 0, sizeof x); + x.len = len; + x.tx_buf = txbuf; + x.rx_buf = rxbuf; + spi_message_add_tail(&x, &message); + + if (always_fast) + x.speed_hz = spi->max_speed_hz; + else if (max->baud) + x.speed_hz = max->baud; + + /* Do the i/o */ + ret = spi_sync(spi, &message); + return ret; +} + +/* Write a 16b word to the device */ +static int max3110_out(struct uart_max3110 *max, const u16 out) +{ + void *buf; + u16 *obuf, *ibuf; + u8 ch; + int ret; + + buf = kzalloc(8, GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + obuf = buf; + ibuf = buf + 4; + *obuf = out; + ret = max3110_write_then_read(max, obuf, ibuf, 2, 1); + if (ret) { + pr_warning(PR_FMT "%s(): get err msg %d when sending 0x%x\n", + __func__, ret, out); + goto exit; + } + + /* If some valid data is read back */ + if (*ibuf & MAX3110_READ_DATA_AVAILABLE) { + ch = *ibuf & 0xff; + receive_chars(max, &ch, 1); + } + +exit: + kfree(buf); + return ret; +} + +/* + * This is usually used to read data from SPIC RX FIFO, which doesn't + * need any delay like flushing character out. + * + * Return how many valide bytes are read back + */ +static int max3110_read_multi(struct uart_max3110 *max, u8 *rxbuf) +{ + void *buf; + u16 *obuf, *ibuf; + u8 *pbuf, valid_str[M3110_RX_FIFO_DEPTH]; + int i, j, blen; + + blen = M3110_RX_FIFO_DEPTH * sizeof(u16); + buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); + if (!buf) { + pr_warning(PR_FMT "%s(): fail to alloc dma buffer\n", __func__); + return 0; + } + + /* tx/rx always have the same length */ + obuf = buf; + ibuf = buf + blen; + + if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) { + kfree(buf); + return 0; + } + + /* If caller doesn't provide a buffer, then handle received char */ + pbuf = rxbuf ? rxbuf : valid_str; + + for (i = 0, j = 0; i < M3110_RX_FIFO_DEPTH; i++) { + if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) + pbuf[j++] = ibuf[i] & 0xff; + } + + if (j && (pbuf == valid_str)) + receive_chars(max, valid_str, j); + + kfree(buf); + return j; +} + +static void serial_m3110_con_putchar(struct uart_port *port, int ch) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + struct circ_buf *xmit = &max->con_xmit; + + if (uart_circ_chars_free(xmit)) { + xmit->buf[xmit->head] = (char)ch; + xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); + } +} + +/* + * 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. + */ +static void serial_m3110_con_write(struct console *co, + const char *s, unsigned int count) +{ + if (!pmax) + return; + + uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); + + if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags)) + wake_up_process(pmax->main_thread); +} + +static int __init +serial_m3110_con_setup(struct console *co, char *options) +{ + struct uart_max3110 *max = pmax; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_info(PR_FMT "setting up console\n"); + + if (co->index == -1) + co->index = 0; + + if (!max) { + pr_err(PR_FMT "pmax is NULL, return"); + return -ENODEV; + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&max->port, co, baud, parity, bits, flow); +} + +static struct tty_driver *serial_m3110_con_device(struct console *co, + int *index) +{ + struct uart_driver *p = co->data; + *index = co->index; + return p->tty_driver; +} + +static struct uart_driver serial_m3110_reg; +static struct console serial_m3110_console = { + .name = "ttyS", + .write = serial_m3110_con_write, + .device = serial_m3110_con_device, + .setup = serial_m3110_con_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_m3110_reg, +}; + +static unsigned int serial_m3110_tx_empty(struct uart_port *port) +{ + return 1; +} + +static void serial_m3110_stop_tx(struct uart_port *port) +{ + return; +} + +/* stop_rx will be called in spin_lock env */ +static void serial_m3110_stop_rx(struct uart_port *port) +{ + return; +} + +#define WORDS_PER_XFER 128 +static void send_circ_buf(struct uart_max3110 *max, + struct circ_buf *xmit) +{ + void *buf; + u16 *obuf, *ibuf; + u8 valid_str[WORDS_PER_XFER]; + int i, j, len, blen, dma_size, left, ret = 0; + + + dma_size = WORDS_PER_XFER * sizeof(u16) * 2; + buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA); + if (!buf) + return; + obuf = buf; + ibuf = buf + dma_size/2; + + while (!uart_circ_empty(xmit)) { + left = uart_circ_chars_pending(xmit); + while (left) { + len = min(left, WORDS_PER_XFER); + blen = len * sizeof(u16); + memset(ibuf, 0, blen); + + for (i = 0; i < len; i++) { + obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + } + + /* Fail to send msg to console is not very critical */ + ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); + if (ret) + pr_warning(PR_FMT "%s(): get err msg %d\n", + __func__, ret); + + for (i = 0, j = 0; i < len; i++) { + if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) + valid_str[j++] = ibuf[i] & 0xff; + } + + if (j) + receive_chars(max, valid_str, j); + + max->port.icount.tx += len; + left -= len; + } + } + + kfree(buf); +} + +static void transmit_char(struct uart_max3110 *max) +{ + struct uart_port *port = &max->port; + struct circ_buf *xmit = &port->state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + send_circ_buf(max, xmit); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + serial_m3110_stop_tx(port); +} + +/* + * This will be called by uart_write() and tty_write, can't + * go to sleep + */ +static void serial_m3110_start_tx(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + + if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags)) + wake_up_process(max->main_thread); +} + +static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) +{ + struct uart_port *port = &max->port; + struct tty_struct *tty; + int usable; + + /* If uart is not opened, just return */ + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + while (len) { + usable = tty_buffer_request_room(tty, len); + if (usable) { + tty_insert_flip_string(tty, str, usable); + str += usable; + port->icount.rx += usable; + } + len -= usable; + } + tty_flip_buffer_push(tty); +} + +/* + * This routine will be used in read_thread or RX IRQ handling, + * it will first do one round buffer read(8 words), if there is some + * valid RX data, will try to read 5 more rounds till all data + * is read out. + * + * Use stack space as data buffer to save some system load, and chose + * 504 Btyes as a threadhold to do a bulk push to upper tty layer when + * receiving bulk data, a much bigger buffer may cause stack overflow + */ +static void max3110_con_receive(struct uart_max3110 *max) +{ + int loop = 1, num, total = 0; + u8 recv_buf[512], *pbuf; + + pbuf = recv_buf; + do { + num = max3110_read_multi(max, pbuf); + + if (num) { + loop = 5; + pbuf += num; + total += num; + + if (total >= 504) { + receive_chars(max, recv_buf, total); + pbuf = recv_buf; + total = 0; + } + } + } while (--loop); + + if (total) + receive_chars(max, recv_buf, total); +} + +static int max3110_main_thread(void *_max) +{ + struct uart_max3110 *max = _max; + wait_queue_head_t *wq = &max->wq; + int ret = 0; + struct circ_buf *xmit = &max->con_xmit; + + init_waitqueue_head(wq); + pr_info(PR_FMT "start main thread\n"); + + do { + wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop()); + + mutex_lock(&max->thread_mutex); + + if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags)) + max3110_con_receive(max); + + /* first handle console output */ + if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags)) + send_circ_buf(max, xmit); + + /* handle uart output */ + if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags)) + transmit_char(max); + + mutex_unlock(&max->thread_mutex); + + } while (!kthread_should_stop()); + + return ret; +} + +static irqreturn_t serial_m3110_irq(int irq, void *dev_id) +{ + struct uart_max3110 *max = dev_id; + + /* max3110's irq is a falling edge, not level triggered, + * so no need to disable the irq */ + if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags)) + wake_up_process(max->main_thread); + + return IRQ_HANDLED; +} + +/* if don't use RX IRQ, then need a thread to polling read */ +static int max3110_read_thread(void *_max) +{ + struct uart_max3110 *max = _max; + + pr_info(PR_FMT "start read thread\n"); + do { + /* + * If can't acquire the mutex, it means the main thread + * is running which will also perform the rx job + */ + if (mutex_trylock(&max->thread_mutex)) { + max3110_con_receive(max); + mutex_unlock(&max->thread_mutex); + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 20); + } while (!kthread_should_stop()); + + return 0; +} + +static int serial_m3110_startup(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + u16 config = 0; + int ret = 0; + + if (port->line != 0) { + pr_err(PR_FMT "uart port startup failed\n"); + return -1; + } + + /* Disable all IRQ and config it to 115200, 8n1 */ + config = WC_TAG | WC_FIFO_ENABLE + | WC_1_STOPBITS + | WC_8BIT_WORD + | WC_BAUD_DR2; + + /* as we use thread to handle tx/rx, need set low latency */ + port->state->port.tty->low_latency = 1; + + if (max->irq) { + max->read_thread = NULL; + ret = request_irq(max->irq, serial_m3110_irq, + IRQ_TYPE_EDGE_FALLING, "max3110", max); + if (ret) { + max->irq = 0; + pr_err(PR_FMT "unable to allocate IRQ, polling\n"); + } else { + /* Enable RX IRQ only */ + config |= WC_RXA_IRQ_ENABLE; + } + } + + if (max->irq == 0) { + /* If IRQ is disabled, start a read thread for input data */ + max->read_thread = + kthread_run(max3110_read_thread, max, "max3110_read"); + if (IS_ERR(max->read_thread)) { + ret = PTR_ERR(max->read_thread); + max->read_thread = NULL; + pr_err(PR_FMT "Can't create read thread!\n"); + return ret; + } + } + + ret = max3110_out(max, config); + if (ret) { + if (max->irq) + free_irq(max->irq, max); + if (max->read_thread) + kthread_stop(max->read_thread); + max->read_thread = NULL; + return ret; + } + + max->cur_conf = config; + return 0; +} + +static void serial_m3110_shutdown(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + u16 config; + + if (max->read_thread) { + kthread_stop(max->read_thread); + max->read_thread = NULL; + } + + if (max->irq) + free_irq(max->irq, max); + + /* Disable interrupts from this port */ + config = WC_TAG | WC_SW_SHDI; + max3110_out(max, config); +} + +static void serial_m3110_release_port(struct uart_port *port) +{ +} + +static int serial_m3110_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_m3110_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_MAX3100; +} + +static int +serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + + +static const char *serial_m3110_type(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + return max->name; +} + +static void +serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + unsigned char cval; + unsigned int baud, parity = 0; + int clk_div = -1; + u16 new_conf = max->cur_conf; + + switch (termios->c_cflag & CSIZE) { + case CS7: + cval = UART_LCR_WLEN7; + new_conf |= WC_7BIT_WORD; + break; + default: + /* We only support CS7 & CS8 */ + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + case CS8: + cval = UART_LCR_WLEN8; + new_conf |= WC_8BIT_WORD; + break; + } + + baud = uart_get_baud_rate(port, termios, old, 0, 230400); + + /* First calc the div for 1.8MHZ clock case */ + switch (baud) { + case 300: + clk_div = WC_BAUD_DR384; + break; + case 600: + clk_div = WC_BAUD_DR192; + break; + case 1200: + clk_div = WC_BAUD_DR96; + break; + case 2400: + clk_div = WC_BAUD_DR48; + break; + case 4800: + clk_div = WC_BAUD_DR24; + break; + case 9600: + clk_div = WC_BAUD_DR12; + break; + case 19200: + clk_div = WC_BAUD_DR6; + break; + case 38400: + clk_div = WC_BAUD_DR3; + break; + case 57600: + clk_div = WC_BAUD_DR2; + break; + case 115200: + clk_div = WC_BAUD_DR1; + break; + case 230400: + if (max->clock & MAX3110_HIGH_CLK) + break; + default: + /* Pick the previous baud rate */ + baud = max->baud; + clk_div = max->cur_conf & WC_BAUD_DIV_MASK; + tty_termios_encode_baud_rate(termios, baud, baud); + } + + if (max->clock & MAX3110_HIGH_CLK) { + clk_div += 1; + /* High clk version max3110 doesn't support B300 */ + if (baud == 300) { + baud = 600; + clk_div = WC_BAUD_DR384; + } + if (baud == 230400) + clk_div = WC_BAUD_DR1; + tty_termios_encode_baud_rate(termios, baud, baud); + } + + new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; + + if (unlikely(termios->c_cflag & CMSPAR)) + termios->c_cflag &= ~CMSPAR; + + if (termios->c_cflag & CSTOPB) + new_conf |= WC_2_STOPBITS; + else + new_conf &= ~WC_2_STOPBITS; + + if (termios->c_cflag & PARENB) { + new_conf |= WC_PARITY_ENABLE; + parity |= UART_LCR_PARITY; + } else + new_conf &= ~WC_PARITY_ENABLE; + + if (!(termios->c_cflag & PARODD)) + parity |= UART_LCR_EPAR; + max->parity = parity; + + uart_update_timeout(port, termios->c_cflag, baud); + + new_conf |= WC_TAG; + if (new_conf != max->cur_conf) { + if (!max3110_out(max, new_conf)) { + max->cur_conf = new_conf; + max->baud = baud; + } + } +} + +/* Don't handle hw handshaking */ +static unsigned int serial_m3110_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; +} + +static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void serial_m3110_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void serial_m3110_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ +} + +static void serial_m3110_enable_ms(struct uart_port *port) +{ +} + +struct uart_ops serial_m3110_ops = { + .tx_empty = serial_m3110_tx_empty, + .set_mctrl = serial_m3110_set_mctrl, + .get_mctrl = serial_m3110_get_mctrl, + .stop_tx = serial_m3110_stop_tx, + .start_tx = serial_m3110_start_tx, + .stop_rx = serial_m3110_stop_rx, + .enable_ms = serial_m3110_enable_ms, + .break_ctl = serial_m3110_break_ctl, + .startup = serial_m3110_startup, + .shutdown = serial_m3110_shutdown, + .set_termios = serial_m3110_set_termios, + .pm = serial_m3110_pm, + .type = serial_m3110_type, + .release_port = serial_m3110_release_port, + .request_port = serial_m3110_request_port, + .config_port = serial_m3110_config_port, + .verify_port = serial_m3110_verify_port, +}; + +static struct uart_driver serial_m3110_reg = { + .owner = THIS_MODULE, + .driver_name = "MRST serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = 1, + .cons = &serial_m3110_console, +}; + +#ifdef CONFIG_PM +static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) +{ + struct uart_max3110 *max = spi_get_drvdata(spi); + + disable_irq(max->irq); + uart_suspend_port(&serial_m3110_reg, &max->port); + max3110_out(max, max->cur_conf | WC_SW_SHDI); + return 0; +} + +static int serial_m3110_resume(struct spi_device *spi) +{ + struct uart_max3110 *max = spi_get_drvdata(spi); + + max3110_out(max, max->cur_conf); + uart_resume_port(&serial_m3110_reg, &max->port); + enable_irq(max->irq); + return 0; +} +#else +#define serial_m3110_suspend NULL +#define serial_m3110_resume NULL +#endif + +static int __devinit serial_m3110_probe(struct spi_device *spi) +{ + struct uart_max3110 *max; + void *buffer; + u16 res; + int ret = 0; + + max = kzalloc(sizeof(*max), GFP_KERNEL); + if (!max) + return -ENOMEM; + + /* Set spi info */ + spi->bits_per_word = 16; + max->clock = MAX3110_HIGH_CLK; + + spi_setup(spi); + + max->port.type = PORT_MAX3100; + max->port.fifosize = 2; /* Only have 16b buffer */ + max->port.ops = &serial_m3110_ops; + max->port.line = 0; + max->port.dev = &spi->dev; + max->port.uartclk = 115200; + + max->spi = spi; + strcpy(max->name, spi->modalias); + max->irq = (u16)spi->irq; + + mutex_init(&max->thread_mutex); + + max->word_7bits = 0; + max->parity = 0; + max->baud = 0; + + max->cur_conf = 0; + max->uart_flags = 0; + + /* Check if reading configuration register returns something sane */ + + res = RC_TAG; + ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); + if (ret < 0 || res == 0 || res == 0xffff) { + printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)", + res); + ret = -ENODEV; + goto err_get_page; + } + + buffer = (void *)__get_free_page(GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto err_get_page; + } + max->con_xmit.buf = buffer; + max->con_xmit.head = 0; + max->con_xmit.tail = 0; + + max->main_thread = kthread_run(max3110_main_thread, + max, "max3110_main"); + if (IS_ERR(max->main_thread)) { + ret = PTR_ERR(max->main_thread); + goto err_kthread; + } + + spi_set_drvdata(spi, max); + pmax = max; + + /* Give membase a psudo value to pass serial_core's check */ + max->port.membase = (void *)0xff110000; + uart_add_one_port(&serial_m3110_reg, &max->port); + + return 0; + +err_kthread: + free_page((unsigned long)buffer); +err_get_page: + kfree(max); + return ret; +} + +static int __devexit serial_m3110_remove(struct spi_device *dev) +{ + struct uart_max3110 *max = spi_get_drvdata(dev); + + if (!max) + return 0; + + uart_remove_one_port(&serial_m3110_reg, &max->port); + + free_page((unsigned long)max->con_xmit.buf); + + if (max->main_thread) + kthread_stop(max->main_thread); + + kfree(max); + return 0; +} + +static struct spi_driver uart_max3110_driver = { + .driver = { + .name = "spi_max3111", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = serial_m3110_probe, + .remove = __devexit_p(serial_m3110_remove), + .suspend = serial_m3110_suspend, + .resume = serial_m3110_resume, +}; + +static int __init serial_m3110_init(void) +{ + int ret = 0; + + ret = uart_register_driver(&serial_m3110_reg); + if (ret) + return ret; + + ret = spi_register_driver(&uart_max3110_driver); + if (ret) + uart_unregister_driver(&serial_m3110_reg); + + return ret; +} + +static void __exit serial_m3110_exit(void) +{ + spi_unregister_driver(&uart_max3110_driver); + uart_unregister_driver(&serial_m3110_reg); +} + +module_init(serial_m3110_init); +module_exit(serial_m3110_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("max3110-uart"); diff --git a/drivers/tty/serial/mrst_max3110.h b/drivers/tty/serial/mrst_max3110.h new file mode 100644 index 000000000000..d1ef43af397c --- /dev/null +++ b/drivers/tty/serial/mrst_max3110.h @@ -0,0 +1,60 @@ +#ifndef _MRST_MAX3110_H +#define _MRST_MAX3110_H + +#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */ +#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */ + +/* status bits for all 4 MAX3110 operate modes */ +#define MAX3110_READ_DATA_AVAILABLE (1 << 15) +#define MAX3110_WRITE_BUF_EMPTY (1 << 14) + +#define WC_TAG (3 << 14) +#define RC_TAG (1 << 14) +#define WD_TAG (2 << 14) +#define RD_TAG (0 << 14) + +/* bits def for write configuration */ +#define WC_FIFO_ENABLE_MASK (1 << 13) +#define WC_FIFO_ENABLE (0 << 13) + +#define WC_SW_SHDI (1 << 12) + +#define WC_IRQ_MASK (0xF << 8) +#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */ +#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */ +#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9) +#define WC_REC_ACT_IRQ_ENABLE (1 << 8) + +#define WC_IRDA_ENABLE (1 << 7) + +#define WC_STOPBITS_MASK (1 << 6) +#define WC_2_STOPBITS (1 << 6) +#define WC_1_STOPBITS (0 << 6) + +#define WC_PARITY_ENABLE_MASK (1 << 5) +#define WC_PARITY_ENABLE (1 << 5) + +#define WC_WORDLEN_MASK (1 << 4) +#define WC_7BIT_WORD (1 << 4) +#define WC_8BIT_WORD (0 << 4) + +#define WC_BAUD_DIV_MASK (0xF) +#define WC_BAUD_DR1 (0x0) +#define WC_BAUD_DR2 (0x1) +#define WC_BAUD_DR4 (0x2) +#define WC_BAUD_DR8 (0x3) +#define WC_BAUD_DR16 (0x4) +#define WC_BAUD_DR32 (0x5) +#define WC_BAUD_DR64 (0x6) +#define WC_BAUD_DR128 (0x7) +#define WC_BAUD_DR3 (0x8) +#define WC_BAUD_DR6 (0x9) +#define WC_BAUD_DR12 (0xA) +#define WC_BAUD_DR24 (0xB) +#define WC_BAUD_DR48 (0xC) +#define WC_BAUD_DR96 (0xD) +#define WC_BAUD_DR192 (0xE) +#define WC_BAUD_DR384 (0xF) + +#define M3110_RX_FIFO_DEPTH 8 +#endif diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c new file mode 100644 index 000000000000..8e43a7b69e64 --- /dev/null +++ b/drivers/tty/serial/msm_serial.c @@ -0,0 +1,758 @@ +/* + * drivers/serial/msm_serial.c - driver for msm7k serial device and console + * + * Copyright (C) 2007 Google, Inc. + * Author: Robert Love + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#if defined(CONFIG_SERIAL_MSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +# define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msm_serial.h" + +struct msm_port { + struct uart_port uart; + char name[16]; + struct clk *clk; + unsigned int imr; +}; + +static void msm_stop_tx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr &= ~UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_start_tx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr |= UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_stop_rx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE); + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_enable_ms(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr |= UART_IMR_DELTA_CTS; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void handle_rx(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + unsigned int sr; + + /* + * Handle overrun. My understanding of the hardware is that overrun + * is not tied to the RX buffer, so we handle the case out of band. + */ + if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); + } + + /* and now the main RX loop */ + while ((sr = msm_read(port, UART_SR)) & UART_SR_RX_READY) { + unsigned int c; + char flag = TTY_NORMAL; + + c = msm_read(port, UART_RF); + + if (sr & UART_SR_RX_BREAK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (sr & UART_SR_PAR_FRAME_ERR) { + port->icount.frame++; + } else { + port->icount.rx++; + } + + /* Mask conditions we're ignorning. */ + sr &= port->read_status_mask; + + if (sr & UART_SR_RX_BREAK) { + flag = TTY_BREAK; + } else if (sr & UART_SR_PAR_FRAME_ERR) { + flag = TTY_FRAME; + } + + if (!uart_handle_sysrq_char(port, c)) + tty_insert_flip_char(tty, c, flag); + } + + tty_flip_buffer_push(tty); +} + +static void handle_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + struct msm_port *msm_port = UART_TO_MSM(port); + int sent_tx; + + if (port->x_char) { + msm_write(port, port->x_char, UART_TF); + port->icount.tx++; + port->x_char = 0; + } + + while (msm_read(port, UART_SR) & UART_SR_TX_READY) { + if (uart_circ_empty(xmit)) { + /* disable tx interrupts */ + msm_port->imr &= ~UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); + break; + } + + msm_write(port, xmit->buf[xmit->tail], UART_TF); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + sent_tx = 1; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void handle_delta_cts(struct uart_port *port) +{ + msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); + port->icount.cts++; + wake_up_interruptible(&port->state->port.delta_msr_wait); +} + +static irqreturn_t msm_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int misr; + + spin_lock(&port->lock); + misr = msm_read(port, UART_MISR); + msm_write(port, 0, UART_IMR); /* disable interrupt */ + + if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) + handle_rx(port); + if (misr & UART_IMR_TXLEV) + handle_tx(port); + if (misr & UART_IMR_DELTA_CTS) + handle_delta_cts(port); + + msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int msm_tx_empty(struct uart_port *port) +{ + return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0; +} + +static unsigned int msm_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; +} + +static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int mr; + + mr = msm_read(port, UART_MR1); + + if (!(mctrl & TIOCM_RTS)) { + mr &= ~UART_MR1_RX_RDY_CTL; + msm_write(port, mr, UART_MR1); + msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR); + } else { + mr |= UART_MR1_RX_RDY_CTL; + msm_write(port, mr, UART_MR1); + } +} + +static void msm_break_ctl(struct uart_port *port, int break_ctl) +{ + if (break_ctl) + msm_write(port, UART_CR_CMD_START_BREAK, UART_CR); + else + msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR); +} + +static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) +{ + unsigned int baud_code, rxstale, watermark; + + switch (baud) { + case 300: + baud_code = UART_CSR_300; + rxstale = 1; + break; + case 600: + baud_code = UART_CSR_600; + rxstale = 1; + break; + case 1200: + baud_code = UART_CSR_1200; + rxstale = 1; + break; + case 2400: + baud_code = UART_CSR_2400; + rxstale = 1; + break; + case 4800: + baud_code = UART_CSR_4800; + rxstale = 1; + break; + case 9600: + baud_code = UART_CSR_9600; + rxstale = 2; + break; + case 14400: + baud_code = UART_CSR_14400; + rxstale = 3; + break; + case 19200: + baud_code = UART_CSR_19200; + rxstale = 4; + break; + case 28800: + baud_code = UART_CSR_28800; + rxstale = 6; + break; + case 38400: + baud_code = UART_CSR_38400; + rxstale = 8; + break; + case 57600: + baud_code = UART_CSR_57600; + rxstale = 16; + break; + case 115200: + default: + baud_code = UART_CSR_115200; + baud = 115200; + rxstale = 31; + break; + } + + msm_write(port, baud_code, UART_CSR); + + /* RX stale watermark */ + watermark = UART_IPR_STALE_LSB & rxstale; + watermark |= UART_IPR_RXSTALE_LAST; + watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2); + msm_write(port, watermark, UART_IPR); + + /* set RX watermark */ + watermark = (port->fifosize * 3) / 4; + msm_write(port, watermark, UART_RFWR); + + /* set TX watermark */ + msm_write(port, 10, UART_TFWR); + + return baud; +} + +static void msm_reset(struct uart_port *port) +{ + /* reset everything */ + msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); + msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); + msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); + msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); +} + +static void msm_init_clock(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + clk_enable(msm_port->clk); + msm_serial_set_mnd_regs(port); +} + +static int msm_startup(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int data, rfr_level; + int ret; + + snprintf(msm_port->name, sizeof(msm_port->name), + "msm_serial%d", port->line); + + ret = request_irq(port->irq, msm_irq, IRQF_TRIGGER_HIGH, + msm_port->name, port); + if (unlikely(ret)) + return ret; + + msm_init_clock(port); + + if (likely(port->fifosize > 12)) + rfr_level = port->fifosize - 12; + else + rfr_level = port->fifosize; + + /* set automatic RFR level */ + data = msm_read(port, UART_MR1); + data &= ~UART_MR1_AUTO_RFR_LEVEL1; + data &= ~UART_MR1_AUTO_RFR_LEVEL0; + data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2); + data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; + msm_write(port, data, UART_MR1); + + /* make sure that RXSTALE count is non-zero */ + data = msm_read(port, UART_IPR); + if (unlikely(!data)) { + data |= UART_IPR_RXSTALE_LAST; + data |= UART_IPR_STALE_LSB; + msm_write(port, data, UART_IPR); + } + + msm_reset(port); + + msm_write(port, 0x05, UART_CR); /* enable TX & RX */ + + /* turn on RX and CTS interrupts */ + msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE | + UART_IMR_CURRENT_CTS; + msm_write(port, msm_port->imr, UART_IMR); + + return 0; +} + +static void msm_shutdown(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr = 0; + msm_write(port, 0, UART_IMR); /* disable interrupts */ + + clk_disable(msm_port->clk); + + free_irq(port->irq, port); +} + +static void msm_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, mr; + + spin_lock_irqsave(&port->lock, flags); + + /* calculate and set baud rate */ + baud = uart_get_baud_rate(port, termios, old, 300, 115200); + baud = msm_set_baud_rate(port, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* calculate parity */ + mr = msm_read(port, UART_MR2); + mr &= ~UART_MR2_PARITY_MODE; + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + mr |= UART_MR2_PARITY_MODE_ODD; + else if (termios->c_cflag & CMSPAR) + mr |= UART_MR2_PARITY_MODE_SPACE; + else + mr |= UART_MR2_PARITY_MODE_EVEN; + } + + /* calculate bits per char */ + mr &= ~UART_MR2_BITS_PER_CHAR; + switch (termios->c_cflag & CSIZE) { + case CS5: + mr |= UART_MR2_BITS_PER_CHAR_5; + break; + case CS6: + mr |= UART_MR2_BITS_PER_CHAR_6; + break; + case CS7: + mr |= UART_MR2_BITS_PER_CHAR_7; + break; + case CS8: + default: + mr |= UART_MR2_BITS_PER_CHAR_8; + break; + } + + /* calculate stop bits */ + mr &= ~(UART_MR2_STOP_BIT_LEN_ONE | UART_MR2_STOP_BIT_LEN_TWO); + if (termios->c_cflag & CSTOPB) + mr |= UART_MR2_STOP_BIT_LEN_TWO; + else + mr |= UART_MR2_STOP_BIT_LEN_ONE; + + /* set parity, bits per char, and stop bit */ + msm_write(port, mr, UART_MR2); + + /* calculate and set hardware flow control */ + mr = msm_read(port, UART_MR1); + mr &= ~(UART_MR1_CTS_CTL | UART_MR1_RX_RDY_CTL); + if (termios->c_cflag & CRTSCTS) { + mr |= UART_MR1_CTS_CTL; + mr |= UART_MR1_RX_RDY_CTL; + } + msm_write(port, mr, UART_MR1); + + /* Configure status bits to ignore based on termio flags. */ + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_SR_PAR_FRAME_ERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_SR_RX_BREAK; + + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *msm_type(struct uart_port *port) +{ + return "MSM"; +} + +static void msm_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *resource; + resource_size_t size; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return; + size = resource->end - resource->start + 1; + + release_mem_region(port->mapbase, size); + iounmap(port->membase); + port->membase = NULL; +} + +static int msm_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *resource; + resource_size_t size; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return -ENXIO; + size = resource->end - resource->start + 1; + + if (unlikely(!request_mem_region(port->mapbase, size, "msm_serial"))) + return -EBUSY; + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + release_mem_region(port->mapbase, size); + return -EBUSY; + } + + return 0; +} + +static void msm_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_MSM; + msm_request_port(port); + } +} + +static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM)) + return -EINVAL; + if (unlikely(port->irq != ser->irq)) + return -EINVAL; + return 0; +} + +static void msm_power(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + switch (state) { + case 0: + clk_enable(msm_port->clk); + break; + case 3: + clk_disable(msm_port->clk); + break; + default: + printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); + } +} + +static struct uart_ops msm_uart_pops = { + .tx_empty = msm_tx_empty, + .set_mctrl = msm_set_mctrl, + .get_mctrl = msm_get_mctrl, + .stop_tx = msm_stop_tx, + .start_tx = msm_start_tx, + .stop_rx = msm_stop_rx, + .enable_ms = msm_enable_ms, + .break_ctl = msm_break_ctl, + .startup = msm_startup, + .shutdown = msm_shutdown, + .set_termios = msm_set_termios, + .type = msm_type, + .release_port = msm_release_port, + .request_port = msm_request_port, + .config_port = msm_config_port, + .verify_port = msm_verify_port, + .pm = msm_power, +}; + +static struct msm_port msm_uart_ports[] = { + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 512, + .line = 0, + }, + }, + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 512, + .line = 1, + }, + }, + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 64, + .line = 2, + }, + }, +}; + +#define UART_NR ARRAY_SIZE(msm_uart_ports) + +static inline struct uart_port *get_port_from_line(unsigned int line) +{ + return &msm_uart_ports[line].uart; +} + +#ifdef CONFIG_SERIAL_MSM_CONSOLE + +static void msm_console_putchar(struct uart_port *port, int c) +{ + while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) + ; + msm_write(port, c, UART_TF); +} + +static void msm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + struct msm_port *msm_port; + + BUG_ON(co->index < 0 || co->index >= UART_NR); + + port = get_port_from_line(co->index); + msm_port = UART_TO_MSM(port); + + spin_lock(&port->lock); + uart_console_write(port, s, count, msm_console_putchar); + spin_unlock(&port->lock); +} + +static int __init msm_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud, flow, bits, parity; + + if (unlikely(co->index >= UART_NR || co->index < 0)) + return -ENXIO; + + port = get_port_from_line(co->index); + + if (unlikely(!port->membase)) + return -ENXIO; + + port->cons = co; + + msm_init_clock(port); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + bits = 8; + parity = 'n'; + flow = 'n'; + msm_write(port, UART_MR2_BITS_PER_CHAR_8 | UART_MR2_STOP_BIT_LEN_ONE, + UART_MR2); /* 8N1 */ + + if (baud < 300 || baud > 115200) + baud = 115200; + msm_set_baud_rate(port, baud); + + msm_reset(port); + + printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver msm_uart_driver; + +static struct console msm_console = { + .name = "ttyMSM", + .write = msm_console_write, + .device = uart_console_device, + .setup = msm_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &msm_uart_driver, +}; + +#define MSM_CONSOLE (&msm_console) + +#else +#define MSM_CONSOLE NULL +#endif + +static struct uart_driver msm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "msm_serial", + .dev_name = "ttyMSM", + .nr = UART_NR, + .cons = MSM_CONSOLE, +}; + +static int __init msm_serial_probe(struct platform_device *pdev) +{ + struct msm_port *msm_port; + struct resource *resource; + struct uart_port *port; + int irq; + + if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) + return -ENXIO; + + printk(KERN_INFO "msm_serial: detected port #%d\n", pdev->id); + + port = get_port_from_line(pdev->id); + port->dev = &pdev->dev; + msm_port = UART_TO_MSM(port); + + msm_port->clk = clk_get(&pdev->dev, "uart_clk"); + if (IS_ERR(msm_port->clk)) + return PTR_ERR(msm_port->clk); + port->uartclk = clk_get_rate(msm_port->clk); + printk(KERN_INFO "uartclk = %d\n", port->uartclk); + + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return -ENXIO; + port->mapbase = resource->start; + + irq = platform_get_irq(pdev, 0); + if (unlikely(irq < 0)) + return -ENXIO; + port->irq = irq; + + platform_set_drvdata(pdev, port); + + return uart_add_one_port(&msm_uart_driver, port); +} + +static int __devexit msm_serial_remove(struct platform_device *pdev) +{ + struct msm_port *msm_port = platform_get_drvdata(pdev); + + clk_put(msm_port->clk); + + return 0; +} + +static struct platform_driver msm_platform_driver = { + .remove = msm_serial_remove, + .driver = { + .name = "msm_serial", + .owner = THIS_MODULE, + }, +}; + +static int __init msm_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&msm_uart_driver); + if (unlikely(ret)) + return ret; + + ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); + if (unlikely(ret)) + uart_unregister_driver(&msm_uart_driver); + + printk(KERN_INFO "msm_serial: driver initialized\n"); + + return ret; +} + +static void __exit msm_serial_exit(void) +{ +#ifdef CONFIG_SERIAL_MSM_CONSOLE + unregister_console(&msm_console); +#endif + platform_driver_unregister(&msm_platform_driver); + uart_unregister_driver(&msm_uart_driver); +} + +module_init(msm_serial_init); +module_exit(msm_serial_exit); + +MODULE_AUTHOR("Robert Love "); +MODULE_DESCRIPTION("Driver for msm7x serial device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h new file mode 100644 index 000000000000..f6ca9ca79e98 --- /dev/null +++ b/drivers/tty/serial/msm_serial.h @@ -0,0 +1,173 @@ +/* + * drivers/serial/msm_serial.h + * + * Copyright (C) 2007 Google, Inc. + * Author: Robert Love + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef __DRIVERS_SERIAL_MSM_SERIAL_H +#define __DRIVERS_SERIAL_MSM_SERIAL_H + +#define UART_MR1 0x0000 + +#define UART_MR1_AUTO_RFR_LEVEL0 0x3F +#define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 +#define UART_MR1_RX_RDY_CTL (1 << 7) +#define UART_MR1_CTS_CTL (1 << 6) + +#define UART_MR2 0x0004 +#define UART_MR2_ERROR_MODE (1 << 6) +#define UART_MR2_BITS_PER_CHAR 0x30 +#define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) +#define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) +#define UART_MR2_BITS_PER_CHAR_7 (0x2 << 4) +#define UART_MR2_BITS_PER_CHAR_8 (0x3 << 4) +#define UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2) +#define UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2) +#define UART_MR2_PARITY_MODE_NONE 0x0 +#define UART_MR2_PARITY_MODE_ODD 0x1 +#define UART_MR2_PARITY_MODE_EVEN 0x2 +#define UART_MR2_PARITY_MODE_SPACE 0x3 +#define UART_MR2_PARITY_MODE 0x3 + +#define UART_CSR 0x0008 +#define UART_CSR_115200 0xFF +#define UART_CSR_57600 0xEE +#define UART_CSR_38400 0xDD +#define UART_CSR_28800 0xCC +#define UART_CSR_19200 0xBB +#define UART_CSR_14400 0xAA +#define UART_CSR_9600 0x99 +#define UART_CSR_4800 0x77 +#define UART_CSR_2400 0x55 +#define UART_CSR_1200 0x44 +#define UART_CSR_600 0x33 +#define UART_CSR_300 0x22 + +#define UART_TF 0x000C + +#define UART_CR 0x0010 +#define UART_CR_CMD_NULL (0 << 4) +#define UART_CR_CMD_RESET_RX (1 << 4) +#define UART_CR_CMD_RESET_TX (2 << 4) +#define UART_CR_CMD_RESET_ERR (3 << 4) +#define UART_CR_CMD_RESET_BREAK_INT (4 << 4) +#define UART_CR_CMD_START_BREAK (5 << 4) +#define UART_CR_CMD_STOP_BREAK (6 << 4) +#define UART_CR_CMD_RESET_CTS (7 << 4) +#define UART_CR_CMD_PACKET_MODE (9 << 4) +#define UART_CR_CMD_MODE_RESET (12 << 4) +#define UART_CR_CMD_SET_RFR (13 << 4) +#define UART_CR_CMD_RESET_RFR (14 << 4) +#define UART_CR_TX_DISABLE (1 << 3) +#define UART_CR_TX_ENABLE (1 << 3) +#define UART_CR_RX_DISABLE (1 << 3) +#define UART_CR_RX_ENABLE (1 << 3) + +#define UART_IMR 0x0014 +#define UART_IMR_TXLEV (1 << 0) +#define UART_IMR_RXSTALE (1 << 3) +#define UART_IMR_RXLEV (1 << 4) +#define UART_IMR_DELTA_CTS (1 << 5) +#define UART_IMR_CURRENT_CTS (1 << 6) + +#define UART_IPR_RXSTALE_LAST 0x20 +#define UART_IPR_STALE_LSB 0x1F +#define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 + +#define UART_IPR 0x0018 +#define UART_TFWR 0x001C +#define UART_RFWR 0x0020 +#define UART_HCR 0x0024 + +#define UART_MREG 0x0028 +#define UART_NREG 0x002C +#define UART_DREG 0x0030 +#define UART_MNDREG 0x0034 +#define UART_IRDA 0x0038 +#define UART_MISR_MODE 0x0040 +#define UART_MISR_RESET 0x0044 +#define UART_MISR_EXPORT 0x0048 +#define UART_MISR_VAL 0x004C +#define UART_TEST_CTRL 0x0050 + +#define UART_SR 0x0008 +#define UART_SR_HUNT_CHAR (1 << 7) +#define UART_SR_RX_BREAK (1 << 6) +#define UART_SR_PAR_FRAME_ERR (1 << 5) +#define UART_SR_OVERRUN (1 << 4) +#define UART_SR_TX_EMPTY (1 << 3) +#define UART_SR_TX_READY (1 << 2) +#define UART_SR_RX_FULL (1 << 1) +#define UART_SR_RX_READY (1 << 0) + +#define UART_RF 0x000C +#define UART_MISR 0x0010 +#define UART_ISR 0x0014 + +#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) + +static inline +void msm_write(struct uart_port *port, unsigned int val, unsigned int off) +{ + __raw_writel(val, port->membase + off); +} + +static inline +unsigned int msm_read(struct uart_port *port, unsigned int off) +{ + return __raw_readl(port->membase + off); +} + +/* + * Setup the MND registers to use the TCXO clock. + */ +static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) +{ + msm_write(port, 0x06, UART_MREG); + msm_write(port, 0xF1, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x1A, UART_MNDREG); +} + +/* + * Setup the MND registers to use the TCXO clock divided by 4. + */ +static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) +{ + msm_write(port, 0x18, UART_MREG); + msm_write(port, 0xF6, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x0A, UART_MNDREG); +} + +static inline +void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) +{ + if (port->uartclk == 19200000) + msm_serial_set_mnd_regs_tcxo(port); + else + msm_serial_set_mnd_regs_tcxoby4(port); +} + +/* + * TROUT has a specific defect that makes it report it's uartclk + * as 19.2Mhz (TCXO) when it's actually 4.8Mhz (TCXO/4). This special + * cases TROUT to use the right clock. + */ +#ifdef CONFIG_MACH_TROUT +#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_tcxoby4 +#else +#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk +#endif + +#endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */ diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c new file mode 100644 index 000000000000..9711e06a8374 --- /dev/null +++ b/drivers/tty/serial/mux.c @@ -0,0 +1,633 @@ +/* +** mux.c: +** serial driver for the Mux console found in some PA-RISC servers. +** +** (c) Copyright 2002 Ryan Bradetich +** (c) Copyright 2002 Hewlett-Packard Company +** +** 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 Driver currently only supports the console (port 0) on the MUX. +** Additional work will be needed on this driver to enable the full +** functionality of the MUX. +** +*/ + +#include +#include +#include +#include +#include +#include +#include /* for udelay */ +#include +#include +#include +#include + +#ifdef CONFIG_MAGIC_SYSRQ +#include +#define SUPPORT_SYSRQ +#endif + +#include + +#define MUX_OFFSET 0x800 +#define MUX_LINE_OFFSET 0x80 + +#define MUX_FIFO_SIZE 255 +#define MUX_POLL_DELAY (30 * HZ / 1000) + +#define IO_DATA_REG_OFFSET 0x3c +#define IO_DCOUNT_REG_OFFSET 0x40 + +#define MUX_EOFIFO(status) ((status & 0xF000) == 0xF000) +#define MUX_STATUS(status) ((status & 0xF000) == 0x8000) +#define MUX_BREAK(status) ((status & 0xF000) == 0x2000) + +#define MUX_NR 256 +static unsigned int port_cnt __read_mostly; +struct mux_port { + struct uart_port port; + int enabled; +}; +static struct mux_port mux_ports[MUX_NR]; + +static struct uart_driver mux_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyB", + .dev_name = "ttyB", + .major = MUX_MAJOR, + .minor = 0, + .nr = MUX_NR, +}; + +static struct timer_list mux_timer; + +#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + IO_DATA_REG_OFFSET) +#define UART_GET_FIFO_CNT(p) __raw_readl((p)->membase + IO_DCOUNT_REG_OFFSET) + +/** + * get_mux_port_count - Get the number of available ports on the Mux. + * @dev: The parisc device. + * + * This function is used to determine the number of ports the Mux + * supports. The IODC data reports the number of ports the Mux + * can support, but there are cases where not all the Mux ports + * are connected. This function can override the IODC and + * return the true port count. + */ +static int __init get_mux_port_count(struct parisc_device *dev) +{ + int status; + u8 iodc_data[32]; + unsigned long bytecnt; + + /* If this is the built-in Mux for the K-Class (Eole CAP/MUX), + * we only need to allocate resources for 1 port since the + * other 7 ports are not connected. + */ + if(dev->id.hversion == 0x15) + return 1; + + status = pdc_iodc_read(&bytecnt, dev->hpa.start, 0, iodc_data, 32); + BUG_ON(status != PDC_OK); + + /* Return the number of ports specified in the iodc data. */ + return ((((iodc_data)[4] & 0xf0) >> 4) * 8) + 8; +} + +/** + * mux_tx_empty - Check if the transmitter fifo is empty. + * @port: Ptr to the uart_port. + * + * This function test if the transmitter fifo for the port + * described by 'port' is empty. If it is empty, this function + * should return TIOCSER_TEMT, otherwise return 0. + */ +static unsigned int mux_tx_empty(struct uart_port *port) +{ + return UART_GET_FIFO_CNT(port) ? 0 : TIOCSER_TEMT; +} + +/** + * mux_set_mctrl - Set the current state of the modem control inputs. + * @ports: Ptr to the uart_port. + * @mctrl: Modem control bits. + * + * The Serial MUX does not support CTS, DCD or DSR so this function + * is ignored. + */ +static void mux_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/** + * mux_get_mctrl - Returns the current state of modem control inputs. + * @port: Ptr to the uart_port. + * + * The Serial MUX does not support CTS, DCD or DSR so these lines are + * treated as permanently active. + */ +static unsigned int mux_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/** + * mux_stop_tx - Stop transmitting characters. + * @port: Ptr to the uart_port. + * + * The Serial MUX does not support this function. + */ +static void mux_stop_tx(struct uart_port *port) +{ +} + +/** + * mux_start_tx - Start transmitting characters. + * @port: Ptr to the uart_port. + * + * The Serial Mux does not support this function. + */ +static void mux_start_tx(struct uart_port *port) +{ +} + +/** + * mux_stop_rx - Stop receiving characters. + * @port: Ptr to the uart_port. + * + * The Serial Mux does not support this function. + */ +static void mux_stop_rx(struct uart_port *port) +{ +} + +/** + * mux_enable_ms - Enable modum status interrupts. + * @port: Ptr to the uart_port. + * + * The Serial Mux does not support this function. + */ +static void mux_enable_ms(struct uart_port *port) +{ +} + +/** + * mux_break_ctl - Control the transmitssion of a break signal. + * @port: Ptr to the uart_port. + * @break_state: Raise/Lower the break signal. + * + * The Serial Mux does not support this function. + */ +static void mux_break_ctl(struct uart_port *port, int break_state) +{ +} + +/** + * mux_write - Write chars to the mux fifo. + * @port: Ptr to the uart_port. + * + * This function writes all the data from the uart buffer to + * the mux fifo. + */ +static void mux_write(struct uart_port *port) +{ + int count; + struct circ_buf *xmit = &port->state->xmit; + + if(port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if(uart_circ_empty(xmit) || uart_tx_stopped(port)) { + mux_stop_tx(port); + return; + } + + count = (port->fifosize) - UART_GET_FIFO_CNT(port); + do { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if(uart_circ_empty(xmit)) + break; + + } while(--count > 0); + + while(UART_GET_FIFO_CNT(port)) + udelay(1); + + if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + mux_stop_tx(port); +} + +/** + * mux_read - Read chars from the mux fifo. + * @port: Ptr to the uart_port. + * + * This reads all available data from the mux's fifo and pushes + * the data to the tty layer. + */ +static void mux_read(struct uart_port *port) +{ + int data; + struct tty_struct *tty = port->state->port.tty; + __u32 start_count = port->icount.rx; + + while(1) { + data = __raw_readl(port->membase + IO_DATA_REG_OFFSET); + + if (MUX_STATUS(data)) + continue; + + if (MUX_EOFIFO(data)) + break; + + port->icount.rx++; + + if (MUX_BREAK(data)) { + port->icount.brk++; + if(uart_handle_break(port)) + continue; + } + + if (uart_handle_sysrq_char(port, data & 0xffu)) + continue; + + tty_insert_flip_char(tty, data & 0xFF, TTY_NORMAL); + } + + if (start_count != port->icount.rx) { + tty_flip_buffer_push(tty); + } +} + +/** + * mux_startup - Initialize the port. + * @port: Ptr to the uart_port. + * + * Grab any resources needed for this port and start the + * mux timer. + */ +static int mux_startup(struct uart_port *port) +{ + mux_ports[port->line].enabled = 1; + return 0; +} + +/** + * mux_shutdown - Disable the port. + * @port: Ptr to the uart_port. + * + * Release any resources needed for the port. + */ +static void mux_shutdown(struct uart_port *port) +{ + mux_ports[port->line].enabled = 0; +} + +/** + * mux_set_termios - Chane port parameters. + * @port: Ptr to the uart_port. + * @termios: new termios settings. + * @old: old termios settings. + * + * The Serial Mux does not support this function. + */ +static void +mux_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ +} + +/** + * mux_type - Describe the port. + * @port: Ptr to the uart_port. + * + * Return a pointer to a string constant describing the + * specified port. + */ +static const char *mux_type(struct uart_port *port) +{ + return "Mux"; +} + +/** + * mux_release_port - Release memory and IO regions. + * @port: Ptr to the uart_port. + * + * Release any memory and IO region resources currently in use by + * the port. + */ +static void mux_release_port(struct uart_port *port) +{ +} + +/** + * mux_request_port - Request memory and IO regions. + * @port: Ptr to the uart_port. + * + * Request any memory and IO region resources required by the port. + * If any fail, no resources should be registered when this function + * returns, and it should return -EBUSY on failure. + */ +static int mux_request_port(struct uart_port *port) +{ + return 0; +} + +/** + * mux_config_port - Perform port autoconfiguration. + * @port: Ptr to the uart_port. + * @type: Bitmask of required configurations. + * + * Perform any autoconfiguration steps for the port. This function is + * called if the UPF_BOOT_AUTOCONF flag is specified for the port. + * [Note: This is required for now because of a bug in the Serial core. + * rmk has already submitted a patch to linus, should be available for + * 2.5.47.] + */ +static void mux_config_port(struct uart_port *port, int type) +{ + port->type = PORT_MUX; +} + +/** + * mux_verify_port - Verify the port information. + * @port: Ptr to the uart_port. + * @ser: Ptr to the serial information. + * + * Verify the new serial port information contained within serinfo is + * suitable for this port type. + */ +static int mux_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if(port->membase == NULL) + return -EINVAL; + + return 0; +} + +/** + * mux_drv_poll - Mux poll function. + * @unused: Unused variable + * + * This function periodically polls the Serial MUX to check for new data. + */ +static void mux_poll(unsigned long unused) +{ + int i; + + for(i = 0; i < port_cnt; ++i) { + if(!mux_ports[i].enabled) + continue; + + mux_read(&mux_ports[i].port); + mux_write(&mux_ports[i].port); + } + + mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); +} + + +#ifdef CONFIG_SERIAL_MUX_CONSOLE +static void mux_console_write(struct console *co, const char *s, unsigned count) +{ + /* Wait until the FIFO drains. */ + while(UART_GET_FIFO_CNT(&mux_ports[0].port)) + udelay(1); + + while(count--) { + if(*s == '\n') { + UART_PUT_CHAR(&mux_ports[0].port, '\r'); + } + UART_PUT_CHAR(&mux_ports[0].port, *s++); + } + +} + +static int mux_console_setup(struct console *co, char *options) +{ + return 0; +} + +struct tty_driver *mux_console_device(struct console *co, int *index) +{ + *index = co->index; + return mux_driver.tty_driver; +} + +static struct console mux_console = { + .name = "ttyB", + .write = mux_console_write, + .device = mux_console_device, + .setup = mux_console_setup, + .flags = CON_ENABLED | CON_PRINTBUFFER, + .index = 0, +}; + +#define MUX_CONSOLE &mux_console +#else +#define MUX_CONSOLE NULL +#endif + +static struct uart_ops mux_pops = { + .tx_empty = mux_tx_empty, + .set_mctrl = mux_set_mctrl, + .get_mctrl = mux_get_mctrl, + .stop_tx = mux_stop_tx, + .start_tx = mux_start_tx, + .stop_rx = mux_stop_rx, + .enable_ms = mux_enable_ms, + .break_ctl = mux_break_ctl, + .startup = mux_startup, + .shutdown = mux_shutdown, + .set_termios = mux_set_termios, + .type = mux_type, + .release_port = mux_release_port, + .request_port = mux_request_port, + .config_port = mux_config_port, + .verify_port = mux_verify_port, +}; + +/** + * mux_probe - Determine if the Serial Mux should claim this device. + * @dev: The parisc device. + * + * Deterimine if the Serial Mux should claim this chip (return 0) + * or not (return 1). + */ +static int __init mux_probe(struct parisc_device *dev) +{ + int i, status; + + int port_count = get_mux_port_count(dev); + printk(KERN_INFO "Serial mux driver (%d ports) Revision: 0.6\n", port_count); + + dev_set_drvdata(&dev->dev, (void *)(long)port_count); + request_mem_region(dev->hpa.start + MUX_OFFSET, + port_count * MUX_LINE_OFFSET, "Mux"); + + if(!port_cnt) { + mux_driver.cons = MUX_CONSOLE; + + status = uart_register_driver(&mux_driver); + if(status) { + printk(KERN_ERR "Serial mux: Unable to register driver.\n"); + return 1; + } + } + + for(i = 0; i < port_count; ++i, ++port_cnt) { + struct uart_port *port = &mux_ports[port_cnt].port; + port->iobase = 0; + port->mapbase = dev->hpa.start + MUX_OFFSET + + (i * MUX_LINE_OFFSET); + port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET); + port->iotype = UPIO_MEM; + port->type = PORT_MUX; + port->irq = NO_IRQ; + port->uartclk = 0; + port->fifosize = MUX_FIFO_SIZE; + port->ops = &mux_pops; + port->flags = UPF_BOOT_AUTOCONF; + port->line = port_cnt; + + /* The port->timeout needs to match what is present in + * uart_wait_until_sent in serial_core.c. Otherwise + * the time spent in msleep_interruptable will be very + * long, causing the appearance of a console hang. + */ + port->timeout = HZ / 50; + spin_lock_init(&port->lock); + + status = uart_add_one_port(&mux_driver, port); + BUG_ON(status); + } + + return 0; +} + +static int __devexit mux_remove(struct parisc_device *dev) +{ + int i, j; + int port_count = (long)dev_get_drvdata(&dev->dev); + + /* Find Port 0 for this card in the mux_ports list. */ + for(i = 0; i < port_cnt; ++i) { + if(mux_ports[i].port.mapbase == dev->hpa.start + MUX_OFFSET) + break; + } + BUG_ON(i + port_count > port_cnt); + + /* Release the resources associated with each port on the device. */ + for(j = 0; j < port_count; ++j, ++i) { + struct uart_port *port = &mux_ports[i].port; + + uart_remove_one_port(&mux_driver, port); + if(port->membase) + iounmap(port->membase); + } + + release_mem_region(dev->hpa.start + MUX_OFFSET, port_count * MUX_LINE_OFFSET); + return 0; +} + +/* Hack. This idea was taken from the 8250_gsc.c on how to properly order + * the serial port detection in the proper order. The idea is we always + * want the builtin mux to be detected before addin mux cards, so we + * specifically probe for the builtin mux cards first. + * + * This table only contains the parisc_device_id of known builtin mux + * devices. All other mux cards will be detected by the generic mux_tbl. + */ +static struct parisc_device_id builtin_mux_tbl[] = { + { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x15, 0x0000D }, /* All K-class */ + { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x44, 0x0000D }, /* E35, E45, and E55 */ + { 0, } +}; + +static struct parisc_device_id mux_tbl[] = { + { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D }, + { 0, } +}; + +MODULE_DEVICE_TABLE(parisc, builtin_mux_tbl); +MODULE_DEVICE_TABLE(parisc, mux_tbl); + +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), +}; + +static struct parisc_driver serial_mux_driver = { + .name = "serial_mux", + .id_table = mux_tbl, + .probe = mux_probe, + .remove = __devexit_p(mux_remove), +}; + +/** + * mux_init - Serial MUX initialization procedure. + * + * Register the Serial MUX driver. + */ +static int __init mux_init(void) +{ + register_parisc_driver(&builtin_serial_mux_driver); + register_parisc_driver(&serial_mux_driver); + + if(port_cnt > 0) { + /* Start the Mux timer */ + init_timer(&mux_timer); + mux_timer.function = mux_poll; + mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); + +#ifdef CONFIG_SERIAL_MUX_CONSOLE + register_console(&mux_console); +#endif + } + + return 0; +} + +/** + * mux_exit - Serial MUX cleanup procedure. + * + * Unregister the Serial MUX driver from the tty layer. + */ +static void __exit mux_exit(void) +{ + /* Delete the Mux timer. */ + if(port_cnt > 0) { + del_timer(&mux_timer); +#ifdef CONFIG_SERIAL_MUX_CONSOLE + unregister_console(&mux_console); +#endif + } + + unregister_parisc_driver(&builtin_serial_mux_driver); + unregister_parisc_driver(&serial_mux_driver); + uart_unregister_driver(&mux_driver); +} + +module_init(mux_init); +module_exit(mux_exit); + +MODULE_AUTHOR("Ryan Bradetich"); +MODULE_DESCRIPTION("Serial MUX driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(MUX_MAJOR); diff --git a/drivers/tty/serial/netx-serial.c b/drivers/tty/serial/netx-serial.c new file mode 100644 index 000000000000..7735c9f35fa0 --- /dev/null +++ b/drivers/tty/serial/netx-serial.c @@ -0,0 +1,750 @@ +/* + * drivers/serial/netx-serial.c + * + * Copyright (c) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_NX_MAJOR 204 +#define MINOR_START 170 + +enum uart_regs { + UART_DR = 0x00, + UART_SR = 0x04, + UART_LINE_CR = 0x08, + UART_BAUDDIV_MSB = 0x0c, + UART_BAUDDIV_LSB = 0x10, + UART_CR = 0x14, + UART_FR = 0x18, + UART_IIR = 0x1c, + UART_ILPR = 0x20, + UART_RTS_CR = 0x24, + UART_RTS_LEAD = 0x28, + UART_RTS_TRAIL = 0x2c, + UART_DRV_ENABLE = 0x30, + UART_BRM_CR = 0x34, + UART_RXFIFO_IRQLEVEL = 0x38, + UART_TXFIFO_IRQLEVEL = 0x3c, +}; + +#define SR_FE (1<<0) +#define SR_PE (1<<1) +#define SR_BE (1<<2) +#define SR_OE (1<<3) + +#define LINE_CR_BRK (1<<0) +#define LINE_CR_PEN (1<<1) +#define LINE_CR_EPS (1<<2) +#define LINE_CR_STP2 (1<<3) +#define LINE_CR_FEN (1<<4) +#define LINE_CR_5BIT (0<<5) +#define LINE_CR_6BIT (1<<5) +#define LINE_CR_7BIT (2<<5) +#define LINE_CR_8BIT (3<<5) +#define LINE_CR_BITS_MASK (3<<5) + +#define CR_UART_EN (1<<0) +#define CR_SIREN (1<<1) +#define CR_SIRLP (1<<2) +#define CR_MSIE (1<<3) +#define CR_RIE (1<<4) +#define CR_TIE (1<<5) +#define CR_RTIE (1<<6) +#define CR_LBE (1<<7) + +#define FR_CTS (1<<0) +#define FR_DSR (1<<1) +#define FR_DCD (1<<2) +#define FR_BUSY (1<<3) +#define FR_RXFE (1<<4) +#define FR_TXFF (1<<5) +#define FR_RXFF (1<<6) +#define FR_TXFE (1<<7) + +#define IIR_MIS (1<<0) +#define IIR_RIS (1<<1) +#define IIR_TIS (1<<2) +#define IIR_RTIS (1<<3) +#define IIR_MASK 0xf + +#define RTS_CR_AUTO (1<<0) +#define RTS_CR_RTS (1<<1) +#define RTS_CR_COUNT (1<<2) +#define RTS_CR_MOD2 (1<<3) +#define RTS_CR_RTS_POL (1<<4) +#define RTS_CR_CTS_CTR (1<<5) +#define RTS_CR_CTS_POL (1<<6) +#define RTS_CR_STICK (1<<7) + +#define UART_PORT_SIZE 0x40 +#define DRIVER_NAME "netx-uart" + +struct netx_port { + struct uart_port port; +}; + +static void netx_stop_tx(struct uart_port *port) +{ + unsigned int val; + val = readl(port->membase + UART_CR); + writel(val & ~CR_TIE, port->membase + UART_CR); +} + +static void netx_stop_rx(struct uart_port *port) +{ + unsigned int val; + val = readl(port->membase + UART_CR); + writel(val & ~CR_RIE, port->membase + UART_CR); +} + +static void netx_enable_ms(struct uart_port *port) +{ + unsigned int val; + val = readl(port->membase + UART_CR); + writel(val | CR_MSIE, port->membase + UART_CR); +} + +static inline void netx_transmit_buffer(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + writel(port->x_char, port->membase + UART_DR); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port) || uart_circ_empty(xmit)) { + netx_stop_tx(port); + return; + } + + do { + /* send xmit->buf[xmit->tail] + * out the port here */ + writel(xmit->buf[xmit->tail], port->membase + UART_DR); + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (!(readl(port->membase + UART_FR) & FR_TXFF)); + + if (uart_circ_empty(xmit)) + netx_stop_tx(port); +} + +static void netx_start_tx(struct uart_port *port) +{ + writel( + readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR); + + if (!(readl(port->membase + UART_FR) & FR_TXFF)) + netx_transmit_buffer(port); +} + +static unsigned int netx_tx_empty(struct uart_port *port) +{ + return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT; +} + +static void netx_txint(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + netx_stop_tx(port); + return; + } + + netx_transmit_buffer(port); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void netx_rxint(struct uart_port *port) +{ + unsigned char rx, flg, status; + struct tty_struct *tty = port->state->port.tty; + + while (!(readl(port->membase + UART_FR) & FR_RXFE)) { + rx = readl(port->membase + UART_DR); + flg = TTY_NORMAL; + port->icount.rx++; + status = readl(port->membase + UART_SR); + if (status & SR_BE) { + writel(0, port->membase + UART_SR); + if (uart_handle_break(port)) + continue; + } + + if (unlikely(status & (SR_FE | SR_PE | SR_OE))) { + + if (status & SR_PE) + port->icount.parity++; + else if (status & SR_FE) + port->icount.frame++; + if (status & SR_OE) + port->icount.overrun++; + + status &= port->read_status_mask; + + if (status & SR_BE) + flg = TTY_BREAK; + else if (status & SR_PE) + flg = TTY_PARITY; + else if (status & SR_FE) + flg = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, rx)) + continue; + + uart_insert_char(port, status, SR_OE, rx, flg); + } + + tty_flip_buffer_push(tty); + return; +} + +static irqreturn_t netx_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long flags; + unsigned char status; + + spin_lock_irqsave(&port->lock,flags); + + status = readl(port->membase + UART_IIR) & IIR_MASK; + while (status) { + if (status & IIR_RIS) + netx_rxint(port); + if (status & IIR_TIS) + netx_txint(port); + if (status & IIR_MIS) { + if (readl(port->membase + UART_FR) & FR_CTS) + uart_handle_cts_change(port, 1); + else + uart_handle_cts_change(port, 0); + } + writel(0, port->membase + UART_IIR); + status = readl(port->membase + UART_IIR) & IIR_MASK; + } + + spin_unlock_irqrestore(&port->lock,flags); + return IRQ_HANDLED; +} + +static unsigned int netx_get_mctrl(struct uart_port *port) +{ + unsigned int ret = TIOCM_DSR | TIOCM_CAR; + + if (readl(port->membase + UART_FR) & FR_CTS) + ret |= TIOCM_CTS; + + return ret; +} + +static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int val; + + /* FIXME: Locking needed ? */ + if (mctrl & TIOCM_RTS) { + val = readl(port->membase + UART_RTS_CR); + writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR); + } +} + +static void netx_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int line_cr; + spin_lock_irq(&port->lock); + + line_cr = readl(port->membase + UART_LINE_CR); + if (break_state != 0) + line_cr |= LINE_CR_BRK; + else + line_cr &= ~LINE_CR_BRK; + writel(line_cr, port->membase + UART_LINE_CR); + + spin_unlock_irq(&port->lock); +} + +static int netx_startup(struct uart_port *port) +{ + int ret; + + ret = request_irq(port->irq, netx_int, 0, + DRIVER_NAME, port); + if (ret) { + dev_err(port->dev, "unable to grab irq%d\n",port->irq); + goto exit; + } + + writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN, + port->membase + UART_LINE_CR); + + writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN, + port->membase + UART_CR); + +exit: + return ret; +} + +static void netx_shutdown(struct uart_port *port) +{ + writel(0, port->membase + UART_CR) ; + + free_irq(port->irq, port); +} + +static void +netx_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud, quot; + unsigned char old_cr; + unsigned char line_cr = LINE_CR_FEN; + unsigned char rts_cr = 0; + + switch (termios->c_cflag & CSIZE) { + case CS5: + line_cr |= LINE_CR_5BIT; + break; + case CS6: + line_cr |= LINE_CR_6BIT; + break; + case CS7: + line_cr |= LINE_CR_7BIT; + break; + case CS8: + line_cr |= LINE_CR_8BIT; + break; + } + + if (termios->c_cflag & CSTOPB) + line_cr |= LINE_CR_STP2; + + if (termios->c_cflag & PARENB) { + line_cr |= LINE_CR_PEN; + if (!(termios->c_cflag & PARODD)) + line_cr |= LINE_CR_EPS; + } + + if (termios->c_cflag & CRTSCTS) + rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = baud * 4096; + quot /= 1000; + quot *= 256; + quot /= 100000; + + spin_lock_irq(&port->lock); + + uart_update_timeout(port, termios->c_cflag, baud); + + old_cr = readl(port->membase + UART_CR); + + /* disable interrupts */ + writel(old_cr & ~(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE), + port->membase + UART_CR); + + /* drain transmitter */ + while (readl(port->membase + UART_FR) & FR_BUSY); + + /* disable UART */ + writel(old_cr & ~CR_UART_EN, port->membase + UART_CR); + + /* modem status interrupts */ + old_cr &= ~CR_MSIE; + if (UART_ENABLE_MS(port, termios->c_cflag)) + old_cr |= CR_MSIE; + + writel((quot>>8) & 0xff, port->membase + UART_BAUDDIV_MSB); + writel(quot & 0xff, port->membase + UART_BAUDDIV_LSB); + writel(line_cr, port->membase + UART_LINE_CR); + + writel(rts_cr, port->membase + UART_RTS_CR); + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; + } + + port->read_status_mask = 0; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SR_BE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SR_PE | SR_FE; + + writel(old_cr, port->membase + UART_CR); + + spin_unlock_irq(&port->lock); +} + +static const char *netx_type(struct uart_port *port) +{ + return port->type == PORT_NETX ? "NETX" : NULL; +} + +static void netx_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +static int netx_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + DRIVER_NAME) != NULL ? 0 : -EBUSY; +} + +static void netx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && netx_request_port(port) == 0) + port->type = PORT_NETX; +} + +static int +netx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_NETX) + ret = -EINVAL; + + return ret; +} + +static struct uart_ops netx_pops = { + .tx_empty = netx_tx_empty, + .set_mctrl = netx_set_mctrl, + .get_mctrl = netx_get_mctrl, + .stop_tx = netx_stop_tx, + .start_tx = netx_start_tx, + .stop_rx = netx_stop_rx, + .enable_ms = netx_enable_ms, + .break_ctl = netx_break_ctl, + .startup = netx_startup, + .shutdown = netx_shutdown, + .set_termios = netx_set_termios, + .type = netx_type, + .release_port = netx_release_port, + .request_port = netx_request_port, + .config_port = netx_config_port, + .verify_port = netx_verify_port, +}; + +static struct netx_port netx_ports[] = { + { + .port = { + .type = PORT_NETX, + .iotype = UPIO_MEM, + .membase = (char __iomem *)io_p2v(NETX_PA_UART0), + .mapbase = NETX_PA_UART0, + .irq = NETX_IRQ_UART0, + .uartclk = 100000000, + .fifosize = 16, + .flags = UPF_BOOT_AUTOCONF, + .ops = &netx_pops, + .line = 0, + }, + }, { + .port = { + .type = PORT_NETX, + .iotype = UPIO_MEM, + .membase = (char __iomem *)io_p2v(NETX_PA_UART1), + .mapbase = NETX_PA_UART1, + .irq = NETX_IRQ_UART1, + .uartclk = 100000000, + .fifosize = 16, + .flags = UPF_BOOT_AUTOCONF, + .ops = &netx_pops, + .line = 1, + }, + }, { + .port = { + .type = PORT_NETX, + .iotype = UPIO_MEM, + .membase = (char __iomem *)io_p2v(NETX_PA_UART2), + .mapbase = NETX_PA_UART2, + .irq = NETX_IRQ_UART2, + .uartclk = 100000000, + .fifosize = 16, + .flags = UPF_BOOT_AUTOCONF, + .ops = &netx_pops, + .line = 2, + }, + } +}; + +#ifdef CONFIG_SERIAL_NETX_CONSOLE + +static void netx_console_putchar(struct uart_port *port, int ch) +{ + while (readl(port->membase + UART_FR) & FR_BUSY); + writel(ch, port->membase + UART_DR); +} + +static void +netx_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &netx_ports[co->index].port; + unsigned char cr_save; + + cr_save = readl(port->membase + UART_CR); + writel(cr_save | CR_UART_EN, port->membase + UART_CR); + + uart_console_write(port, s, count, netx_console_putchar); + + while (readl(port->membase + UART_FR) & FR_BUSY); + writel(cr_save, port->membase + UART_CR); +} + +static void __init +netx_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits, int *flow) +{ + unsigned char line_cr; + + *baud = (readl(port->membase + UART_BAUDDIV_MSB) << 8) | + readl(port->membase + UART_BAUDDIV_LSB); + *baud *= 1000; + *baud /= 4096; + *baud *= 1000; + *baud /= 256; + *baud *= 100; + + line_cr = readl(port->membase + UART_LINE_CR); + *parity = 'n'; + if (line_cr & LINE_CR_PEN) { + if (line_cr & LINE_CR_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (line_cr & LINE_CR_BITS_MASK) { + case LINE_CR_8BIT: + *bits = 8; + break; + case LINE_CR_7BIT: + *bits = 7; + break; + case LINE_CR_6BIT: + *bits = 6; + break; + case LINE_CR_5BIT: + *bits = 5; + break; + } + + if (readl(port->membase + UART_RTS_CR) & RTS_CR_AUTO) + *flow = 'r'; +} + +static int __init +netx_console_setup(struct console *co, char *options) +{ + struct netx_port *sport; + int baud = 9600; + 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. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(netx_ports)) + co->index = 0; + sport = &netx_ports[co->index]; + + if (options) { + uart_parse_options(options, &baud, &parity, &bits, &flow); + } else { + /* if the UART is enabled, assume it has been correctly setup + * by the bootloader and get the options + */ + if (readl(sport->port.membase + UART_CR) & CR_UART_EN) { + netx_console_get_options(&sport->port, &baud, + &parity, &bits, &flow); + } + + } + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver netx_reg; +static struct console netx_console = { + .name = "ttyNX", + .write = netx_console_write, + .device = uart_console_device, + .setup = netx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &netx_reg, +}; + +static int __init netx_console_init(void) +{ + register_console(&netx_console); + return 0; +} +console_initcall(netx_console_init); + +#define NETX_CONSOLE &netx_console +#else +#define NETX_CONSOLE NULL +#endif + +static struct uart_driver netx_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = "ttyNX", + .major = SERIAL_NX_MAJOR, + .minor = MINOR_START, + .nr = ARRAY_SIZE(netx_ports), + .cons = NETX_CONSOLE, +}; + +static int serial_netx_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct netx_port *sport = platform_get_drvdata(pdev); + + if (sport) + uart_suspend_port(&netx_reg, &sport->port); + + return 0; +} + +static int serial_netx_resume(struct platform_device *pdev) +{ + struct netx_port *sport = platform_get_drvdata(pdev); + + if (sport) + uart_resume_port(&netx_reg, &sport->port); + + return 0; +} + +static int serial_netx_probe(struct platform_device *pdev) +{ + struct uart_port *port = &netx_ports[pdev->id].port; + + dev_info(&pdev->dev, "initialising\n"); + + port->dev = &pdev->dev; + + writel(1, port->membase + UART_RXFIFO_IRQLEVEL); + uart_add_one_port(&netx_reg, &netx_ports[pdev->id].port); + platform_set_drvdata(pdev, &netx_ports[pdev->id]); + + return 0; +} + +static int serial_netx_remove(struct platform_device *pdev) +{ + struct netx_port *sport = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (sport) + uart_remove_one_port(&netx_reg, &sport->port); + + return 0; +} + +static struct platform_driver serial_netx_driver = { + .probe = serial_netx_probe, + .remove = serial_netx_remove, + + .suspend = serial_netx_suspend, + .resume = serial_netx_resume, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init netx_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: NetX driver\n"); + + ret = uart_register_driver(&netx_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_netx_driver); + if (ret != 0) + uart_unregister_driver(&netx_reg); + + return 0; +} + +static void __exit netx_serial_exit(void) +{ + platform_driver_unregister(&serial_netx_driver); + uart_unregister_driver(&netx_reg); +} + +module_init(netx_serial_init); +module_exit(netx_serial_exit); + +MODULE_AUTHOR("Sascha Hauer"); +MODULE_DESCRIPTION("NetX serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/tty/serial/nwpserial.c b/drivers/tty/serial/nwpserial.c new file mode 100644 index 000000000000..de173671e3d0 --- /dev/null +++ b/drivers/tty/serial/nwpserial.c @@ -0,0 +1,477 @@ +/* + * Serial Port driver for a NWP uart device + * + * Copyright (C) 2008 IBM Corp., Benjamin Krill + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NWPSERIAL_NR 2 + +#define NWPSERIAL_STATUS_RXVALID 0x1 +#define NWPSERIAL_STATUS_TXFULL 0x2 + +struct nwpserial_port { + struct uart_port port; + dcr_host_t dcr_host; + unsigned int ier; + unsigned int mcr; +}; + +static DEFINE_MUTEX(nwpserial_mutex); +static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; + +static void wait_for_bits(struct nwpserial_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = dcr_read(up->dcr_host, UART_LSR); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & bits) != bits); +} + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static void nwpserial_console_putchar(struct uart_port *port, int c) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void +nwpserial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct nwpserial_port *up = &nwpserial_ports[co->index]; + unsigned long flags; + int locked = 1; + + if (oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); + + /* save and disable interrupt */ + up->ier = dcr_read(up->dcr_host, UART_IER); + dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); + + uart_console_write(&up->port, s, count, nwpserial_console_putchar); + + /* wait for transmitter to become empty */ + while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) + cpu_relax(); + + /* restore interrupt state */ + dcr_write(up->dcr_host, UART_IER, up->ier); + + if (locked) + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver nwpserial_reg; +static struct console nwpserial_console = { + .name = "ttySQ", + .write = nwpserial_console_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &nwpserial_reg, +}; +#define NWPSERIAL_CONSOLE (&nwpserial_console) +#else +#define NWPSERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ + +/**************************************************************************/ + +static int nwpserial_request_port(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_release_port(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_NWPSERIAL; +} + +static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) +{ + struct nwpserial_port *up = dev_id; + struct tty_struct *tty = up->port.state->port.tty; + irqreturn_t ret; + unsigned int iir; + unsigned char ch; + + spin_lock(&up->port.lock); + + /* check if the uart was the interrupt source. */ + iir = dcr_read(up->dcr_host, UART_IIR); + if (!iir) { + ret = IRQ_NONE; + goto out; + } + + do { + up->port.icount.rx++; + ch = dcr_read(up->dcr_host, UART_RX); + if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR); + + tty_flip_buffer_push(tty); + ret = IRQ_HANDLED; + + /* clear interrupt */ + dcr_write(up->dcr_host, UART_IIR, 1); +out: + spin_unlock(&up->port.lock); + return ret; +} + +static int nwpserial_startup(struct uart_port *port) +{ + struct nwpserial_port *up; + int err; + + up = container_of(port, struct nwpserial_port, port); + + /* disable flow control by default */ + up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; + dcr_write(up->dcr_host, UART_MCR, up->mcr); + + /* register interrupt handler */ + err = request_irq(up->port.irq, nwpserial_interrupt, + IRQF_SHARED, "nwpserial", up); + if (err) + return err; + + /* enable interrupts */ + up->ier = UART_IER_RDI; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* enable receiving */ + up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; + + return 0; +} + +static void nwpserial_shutdown(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + /* disable receiving */ + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* disable interrupts from this port */ + up->ier = 0; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* free irq */ + free_irq(up->port.irq, port); +} + +static int nwpserial_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char *nwpserial_type(struct uart_port *port) +{ + return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; +} + +static void nwpserial_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID + | NWPSERIAL_STATUS_TXFULL; + + up->port.ignore_status_mask = 0; + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* Copy back the old hardware settings */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void nwpserial_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static void nwpserial_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_stop_rx(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* don't forward any more data (like !CREAD) */ + up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; +} + +static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) +{ + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void nwpserial_start_tx(struct uart_port *port) +{ + struct nwpserial_port *up; + struct circ_buf *xmit; + up = container_of(port, struct nwpserial_port, port); + xmit = &up->port.state->xmit; + + if (port->x_char) { + nwpserial_putchar(up, up->port.x_char); + port->x_char = 0; + } + + while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { + nwpserial_putchar(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + } +} + +static unsigned int nwpserial_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* N/A */ +} + +static void nwpserial_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + +static unsigned int nwpserial_tx_empty(struct uart_port *port) +{ + struct nwpserial_port *up; + unsigned long flags; + int ret; + up = container_of(port, struct nwpserial_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + ret = dcr_read(up->dcr_host, UART_LSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +} + +static struct uart_ops nwpserial_pops = { + .tx_empty = nwpserial_tx_empty, + .set_mctrl = nwpserial_set_mctrl, + .get_mctrl = nwpserial_get_mctrl, + .stop_tx = nwpserial_stop_tx, + .start_tx = nwpserial_start_tx, + .stop_rx = nwpserial_stop_rx, + .enable_ms = nwpserial_enable_ms, + .break_ctl = nwpserial_break_ctl, + .startup = nwpserial_startup, + .shutdown = nwpserial_shutdown, + .set_termios = nwpserial_set_termios, + .type = nwpserial_type, + .release_port = nwpserial_release_port, + .request_port = nwpserial_request_port, + .config_port = nwpserial_config_port, + .verify_port = nwpserial_verify_port, +}; + +static struct uart_driver nwpserial_reg = { + .owner = THIS_MODULE, + .driver_name = "nwpserial", + .dev_name = "ttySQ", + .major = TTY_MAJOR, + .minor = 68, + .nr = NWPSERIAL_NR, + .cons = NWPSERIAL_CONSOLE, +}; + +int nwpserial_register_port(struct uart_port *port) +{ + struct nwpserial_port *up = NULL; + int ret = -1; + int i; + static int first = 1; + int dcr_len; + int dcr_base; + struct device_node *dn; + + mutex_lock(&nwpserial_mutex); + + dn = port->dev->of_node; + if (dn == NULL) + goto out; + + /* get dcr base. */ + dcr_base = dcr_resource_start(dn, 0); + + /* find matching entry */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.iobase == dcr_base) { + up = &nwpserial_ports[i]; + break; + } + + /* we didn't find a mtching entry, search for a free port */ + if (up == NULL) + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN && + nwpserial_ports[i].port.iobase == 0) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) { + ret = -EBUSY; + goto out; + } + + if (first) + uart_register_driver(&nwpserial_reg); + first = 0; + + up->port.membase = port->membase; + up->port.irq = port->irq; + up->port.uartclk = port->uartclk; + up->port.fifosize = port->fifosize; + up->port.regshift = port->regshift; + up->port.iotype = port->iotype; + up->port.flags = port->flags; + up->port.mapbase = port->mapbase; + up->port.private_data = port->private_data; + + if (port->dev) + up->port.dev = port->dev; + + if (up->port.iobase != dcr_base) { + up->port.ops = &nwpserial_pops; + up->port.fifosize = 16; + + spin_lock_init(&up->port.lock); + + up->port.iobase = dcr_base; + dcr_len = dcr_resource_len(dn, 0); + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); + goto out; + } + } + + ret = uart_add_one_port(&nwpserial_reg, &up->port); + if (ret == 0) + ret = up->port.line; + +out: + mutex_unlock(&nwpserial_mutex); + + return ret; +} +EXPORT_SYMBOL(nwpserial_register_port); + +void nwpserial_unregister_port(int line) +{ + struct nwpserial_port *up = &nwpserial_ports[line]; + mutex_lock(&nwpserial_mutex); + uart_remove_one_port(&nwpserial_reg, &up->port); + + up->port.type = PORT_UNKNOWN; + + mutex_unlock(&nwpserial_mutex); +} +EXPORT_SYMBOL(nwpserial_unregister_port); + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static int __init nwpserial_console_init(void) +{ + struct nwpserial_port *up = NULL; + struct device_node *dn; + const char *name; + int dcr_base; + int dcr_len; + int i; + + /* search for a free port */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) + return -1; + + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) + return -1; + + dn = of_find_node_by_path(name); + if (!dn) + return -1; + + spin_lock_init(&up->port.lock); + up->port.ops = &nwpserial_pops; + up->port.type = PORT_NWPSERIAL; + up->port.fifosize = 16; + + dcr_base = dcr_resource_start(dn, 0); + dcr_len = dcr_resource_len(dn, 0); + up->port.iobase = dcr_base; + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk("Cannot map DCR resources for SERIAL"); + return -1; + } + register_console(&nwpserial_console); + return 0; +} +console_initcall(nwpserial_console_init); +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c new file mode 100644 index 000000000000..5c7abe4c94dd --- /dev/null +++ b/drivers/tty/serial/of_serial.c @@ -0,0 +1,201 @@ +/* + * Serial Port driver for Open Firmware platform devices + * + * Copyright (C) 2006 Arnd Bergmann , IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct of_serial_info { + int type; + int line; +}; + +/* + * 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) +{ + struct resource resource; + struct device_node *np = ofdev->dev.of_node; + const __be32 *clk, *spd; + const __be32 *prop; + int ret, prop_size; + + memset(port, 0, sizeof *port); + spd = of_get_property(np, "current-speed", NULL); + clk = of_get_property(np, "clock-frequency", NULL); + if (!clk) { + dev_warn(&ofdev->dev, "no clock-frequency property set\n"); + return -ENODEV; + } + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_warn(&ofdev->dev, "invalid address\n"); + return ret; + } + + spin_lock_init(&port->lock); + port->mapbase = resource.start; + + /* Check for shifted address mapping */ + prop = of_get_property(np, "reg-offset", &prop_size); + if (prop && (prop_size == sizeof(u32))) + port->mapbase += be32_to_cpup(prop); + + /* Check for registers offset within the devices address range */ + prop = of_get_property(np, "reg-shift", &prop_size); + if (prop && (prop_size == sizeof(u32))) + port->regshift = be32_to_cpup(prop); + + port->irq = irq_of_parse_and_map(np, 0); + port->iotype = UPIO_MEM; + port->type = type; + port->uartclk = be32_to_cpup(clk); + port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP + | UPF_FIXED_PORT | UPF_FIXED_TYPE; + port->dev = &ofdev->dev; + /* If current-speed was set, then try not to change it. */ + if (spd) + port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); + + return 0; +} + +/* + * Try to register a serial port + */ +static int __devinit of_platform_serial_probe(struct platform_device *ofdev, + const struct of_device_id *id) +{ + struct of_serial_info *info; + struct uart_port port; + int port_type; + int ret; + + if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) + return -EBUSY; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + + port_type = (unsigned long)id->data; + ret = of_platform_serial_setup(ofdev, port_type, &port); + if (ret) + goto out; + + switch (port_type) { +#ifdef CONFIG_SERIAL_8250 + case PORT_8250 ... PORT_MAX_8250: + ret = serial8250_register_port(&port); + break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + ret = nwpserial_register_port(&port); + break; +#endif + default: + /* need to add code for these */ + case PORT_UNKNOWN: + dev_info(&ofdev->dev, "Unknown serial port found, ignored\n"); + ret = -ENODEV; + break; + } + if (ret < 0) + goto out; + + info->type = port_type; + info->line = ret; + dev_set_drvdata(&ofdev->dev, info); + return 0; +out: + kfree(info); + irq_dispose_mapping(port.irq); + return ret; +} + +/* + * Release a line + */ +static int of_platform_serial_remove(struct platform_device *ofdev) +{ + struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); + switch (info->type) { +#ifdef CONFIG_SERIAL_8250 + case PORT_8250 ... PORT_MAX_8250: + serial8250_unregister_port(info->line); + break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + nwpserial_unregister_port(info->line); + break; +#endif + default: + /* need to add code for these */ + break; + } + kfree(info); + return 0; +} + +/* + * A few common types, add more as needed. + */ +static struct of_device_id __devinitdata of_platform_serial_table[] = { + { .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, }, + { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, + { .type = "serial", .compatible = "ns16550a", .data = (void *)PORT_16550A, }, + { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, + { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, + { .type = "serial", .compatible = "ns16850", .data = (void *)PORT_16850, }, +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + { .type = "serial", .compatible = "ibm,qpace-nwp-serial", + .data = (void *)PORT_NWPSERIAL, }, +#endif + { .type = "serial", .data = (void *)PORT_UNKNOWN, }, + { /* end of list */ }, +}; + +static struct of_platform_driver of_platform_serial_driver = { + .driver = { + .name = "of_serial", + .owner = THIS_MODULE, + .of_match_table = of_platform_serial_table, + }, + .probe = of_platform_serial_probe, + .remove = of_platform_serial_remove, +}; + +static int __init of_platform_serial_init(void) +{ + return of_register_platform_driver(&of_platform_serial_driver); +} +module_init(of_platform_serial_init); + +static void __exit of_platform_serial_exit(void) +{ + return of_unregister_platform_driver(&of_platform_serial_driver); +}; +module_exit(of_platform_serial_exit); + +MODULE_AUTHOR("Arnd Bergmann "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices"); diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c new file mode 100644 index 000000000000..7f2f01058789 --- /dev/null +++ b/drivers/tty/serial/omap-serial.c @@ -0,0 +1,1359 @@ +/* + * Driver for OMAP-UART controller. + * Based on drivers/serial/8250.c + * + * Copyright (C) 2010 Texas Instruments. + * + * Authors: + * Govindraj R + * Thara Gopinath + * + * 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. + * + * Note: This driver is made seperate from 8250 driver as we cannot + * over load 8250 driver with omap platform specific configuration for + * features like DMA, it makes easier to implement features like DMA and + * hardware flow control and software flow control configuration with + * this driver as required for the omap-platform. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; + +/* Forward declaration of functions */ +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data); +static void serial_omap_rx_timeout(unsigned long uart_no); +static int serial_omap_start_rxdma(struct uart_omap_port *up); + +static inline unsigned int serial_in(struct uart_omap_port *up, int offset) +{ + offset <<= up->port.regshift; + return readw(up->port.membase + offset); +} + +static inline void serial_out(struct uart_omap_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + writew(value, up->port.membase + offset); +} + +static inline void serial_omap_clear_fifos(struct uart_omap_port *up) +{ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +/* + * serial_omap_get_divisor - calculate divisor value + * @port: uart port info + * @baud: baudrate for which divisor needs to be calculated. + * + * We have written our own function to get the divisor so as to support + * 13x mode. 3Mbps Baudrate as an different divisor. + * Reference OMAP TRM Chapter 17: + * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates + * referring to oversampling - divisor value + * baudrate 460,800 to 3,686,400 all have divisor 13 + * except 3,000,000 which has divisor value 16 + */ +static unsigned int +serial_omap_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int divisor; + + if (baud > OMAP_MODE13X_SPEED && baud != 3000000) + divisor = 13; + else + divisor = 16; + return port->uartclk/(baud * divisor); +} + +static void serial_omap_stop_rxdma(struct uart_omap_port *up) +{ + if (up->uart_dma.rx_dma_used) { + del_timer(&up->uart_dma.rx_timer); + omap_stop_dma(up->uart_dma.rx_dma_channel); + omap_free_dma(up->uart_dma.rx_dma_channel); + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_used = false; + } +} + +static void serial_omap_enable_ms(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void serial_omap_stop_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma && + up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { + /* + * Check if dma is still active. If yes do nothing, + * return. Else stop dma + */ + if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel)) + return; + omap_stop_dma(up->uart_dma.tx_dma_channel); + omap_free_dma(up->uart_dma.tx_dma_channel); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_stop_rx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma) + serial_omap_stop_rxdma(up); + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static inline void receive_chars(struct uart_omap_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int flag; + unsigned char ch, lsr = *status; + int max_count = 256; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) { + up->port.icount.parity++; + } else if (lsr & UART_LSR_FE) { + up->port.icount.frame++; + } + + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + lsr |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (lsr & UART_LSR_BI) + flag = TTY_BREAK; + else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); +ignore_char: + lsr = serial_in(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); +} + +static void transmit_chars(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_omap_stop_tx(&up->port); + return; + } + count = up->port.fifosize / 4; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_omap_stop_tx(&up->port); +} + +static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) +{ + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_start_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + struct circ_buf *xmit; + unsigned int start; + int ret = 0; + + if (!up->use_dma) { + serial_omap_enable_ier_thri(up); + return; + } + + if (up->uart_dma.tx_dma_used) + return; + + xmit = &up->port.state->xmit; + + if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) { + ret = omap_request_dma(up->uart_dma.uart_dma_tx, + "UART Tx DMA", + (void *)uart_tx_dma_callback, up, + &(up->uart_dma.tx_dma_channel)); + + if (ret < 0) { + serial_omap_enable_ier_thri(up); + return; + } + } + spin_lock(&(up->uart_dma.tx_lock)); + up->uart_dma.tx_dma_used = true; + spin_unlock(&(up->uart_dma.tx_lock)); + + start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + + UART_XMIT_SIZE) - start; + + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static unsigned int check_modem_status(struct uart_omap_port *up) +{ + unsigned int status; + + status = serial_in(up, UART_MSR); + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if ((status & UART_MSR_ANY_DELTA) == 0) + return status; + + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change + (&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change + (&up->port, status & UART_MSR_CTS); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + return status; +} + +/** + * serial_omap_irq() - This handles the interrupt from one port + * @irq: uart port irq number + * @dev_id: uart port info + */ +static inline irqreturn_t serial_omap_irq(int irq, void *dev_id) +{ + struct uart_omap_port *up = dev_id; + unsigned int iir, lsr; + unsigned long flags; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + if (iir & UART_IIR_RLSI) { + if (!up->use_dma) { + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + } else { + up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + if ((serial_omap_start_rxdma(up) != 0) && + (lsr & UART_LSR_DR)) + receive_chars(up, &lsr); + } + } + + check_modem_status(up); + if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI)) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + up->port_activity = jiffies; + return IRQ_HANDLED; +} + +static unsigned int serial_omap_tx_empty(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + unsigned int ret = 0; + + dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_omap_get_mctrl(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char status; + unsigned int ret = 0; + + status = check_modem_status(up); + dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id); + + 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 serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char mcr = 0; + + dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->pdev->id); + 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; + + mcr |= up->mcr; + serial_out(up, UART_MCR, mcr); +} + +static void serial_omap_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial_omap_startup(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags, + up->name, up); + if (retval) + return retval; + + dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id); + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_omap_clear_fifos(up); + /* For Hardware flow control */ + serial_out(up, UART_MCR, UART_MCR_RTS); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + spin_lock_irqsave(&up->port.lock, flags); + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + up->port.mctrl |= TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + up->msr_saved_flags = 0; + if (up->use_dma) { + free_page((unsigned long)up->port.state->xmit.buf); + up->port.state->xmit.buf = dma_alloc_coherent(NULL, + UART_XMIT_SIZE, + (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys), + 0); + init_timer(&(up->uart_dma.rx_timer)); + up->uart_dma.rx_timer.function = serial_omap_rx_timeout; + up->uart_dma.rx_timer.data = up->pdev->id; + /* Currently the buffer size is 4KB. Can increase it */ + up->uart_dma.rx_buf = dma_alloc_coherent(NULL, + up->uart_dma.rx_buf_size, + (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0); + } + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + + up->port_activity = jiffies; + return 0; +} + +static void serial_omap_shutdown(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id); + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_omap_clear_fifos(up); + + /* + * Read data port to reset things, and then free the irq + */ + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + if (up->use_dma) { + dma_free_coherent(up->port.dev, + UART_XMIT_SIZE, up->port.state->xmit.buf, + up->uart_dma.tx_buf_dma_phys); + up->port.state->xmit.buf = NULL; + serial_omap_stop_rx(port); + dma_free_coherent(up->port.dev, + up->uart_dma.rx_buf_size, up->uart_dma.rx_buf, + up->uart_dma.rx_buf_dma_phys); + up->uart_dma.rx_buf = NULL; + } + free_irq(up->port.irq, up); +} + +static inline void +serial_omap_configure_xonxoff + (struct uart_omap_port *up, struct ktermios *termios) +{ + unsigned char efr = 0; + + up->lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); + + serial_out(up, UART_XON1, termios->c_cc[VSTART]); + serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); + + /* clear SW control mode bits */ + efr = up->efr; + efr &= OMAP_UART_SW_CLR; + + /* + * IXON Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXON) + efr |= OMAP_UART_SW_TX; + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. + */ + if (termios->c_iflag & IXOFF) + efr |= OMAP_UART_SW_RX; + + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + up->mcr = serial_in(up, UART_MCR); + + /* + * IXANY Flag: + * Enable any character to restart output. + * Operation resumes after receiving any + * character after recognition of the XOFF character + */ + if (termios->c_iflag & IXANY) + up->mcr |= UART_MCR_XONANY; + + 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, efr | UART_EFR_SCD); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_LCR, up->lcr); +} + +static void +serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char cval = 0; + unsigned char efr = 0; + unsigned long flags = 0; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13); + quot = serial_omap_get_divisor(port, baud); + + up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 | + UART_FCR_ENABLE_FIFO; + if (up->use_dma) + up->fcr |= UART_FCR_DMA_SELECT; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * Modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, cval); /* reset DLAB */ + + /* FIFOs and DMA Settings */ + + /* FCR can be changed only when the + * baud clock is not running + * DLL_REG and DLH_REG set to 0. + */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_DLL, 0); + serial_out(up, UART_DLM, 0); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + /* FIFO ENABLE, DMA MODE */ + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + if (up->use_dma) { + serial_out(up, UART_TI752_TLR, 0); + serial_out(up, UART_OMAP_SCR, + (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8)); + } + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr); + + /* Protocol, Baud Rate, and Interrupt Settings */ + + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, cval); + + if (baud > 230400 && baud != 3000000) + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_13X_MODE); + else + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_16X_MODE); + + /* Hardware Flow Control Configuration */ + + if (termios->c_cflag & CRTSCTS) { + efr |= (UART_EFR_CTS | UART_EFR_RTS); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); + serial_out(up, UART_LCR, cval); + } + + serial_omap_set_mctrl(&up->port, up->port.mctrl); + /* Software Flow Control Configuration */ + if (termios->c_iflag & (IXON | IXOFF)) + serial_omap_configure_xonxoff(up, termios); + + spin_unlock_irqrestore(&up->port.lock, flags); + dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id); +} + +static void +serial_omap_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char efr; + + dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); + /* Enable module level wake up */ + serial_out(up, UART_OMAP_WER, + (state != 0) ? OMAP_UART_WER_MOD_WKUP : 0); +} + +static void serial_omap_release_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_release_port+\n"); +} + +static int serial_omap_request_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_request_port+\n"); + return 0; +} + +static void serial_omap_config_port(struct uart_port *port, int flags) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", + up->pdev->id); + up->port.type = PORT_OMAP; +} + +static int +serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + dev_dbg(port->dev, "serial_omap_verify_port+\n"); + return -EINVAL; +} + +static const char * +serial_omap_type(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->pdev->id); + return up->name; +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static inline void wait_for_xmitr(struct uart_omap_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) + break; + + udelay(1); + } + } +} + +#ifdef CONFIG_CONSOLE_POLL + +static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +static int serial_omap_poll_get_char(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned int status = serial_in(up, UART_LSR); + + if (!(status & UART_LSR_DR)) + return NO_POLL_CHAR; + + return serial_in(up, UART_RX); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + +static struct uart_omap_port *serial_omap_console_ports[4]; + +static struct uart_driver serial_omap_reg; + +static void serial_omap_console_putchar(struct uart_port *port, int ch) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +static void +serial_omap_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_omap_port *up = serial_omap_console_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_omap_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + check_modem_status(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init +serial_omap_console_setup(struct console *co, char *options) +{ + struct uart_omap_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (serial_omap_console_ports[co->index] == NULL) + return -ENODEV; + up = serial_omap_console_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_omap_console = { + .name = OMAP_SERIAL_NAME, + .write = serial_omap_console_write, + .device = uart_console_device, + .setup = serial_omap_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_omap_reg, +}; + +static void serial_omap_add_console_port(struct uart_omap_port *up) +{ + serial_omap_console_ports[up->pdev->id] = up; +} + +#define OMAP_CONSOLE (&serial_omap_console) + +#else + +#define OMAP_CONSOLE NULL + +static inline void serial_omap_add_console_port(struct uart_omap_port *up) +{} + +#endif + +static struct uart_ops serial_omap_pops = { + .tx_empty = serial_omap_tx_empty, + .set_mctrl = serial_omap_set_mctrl, + .get_mctrl = serial_omap_get_mctrl, + .stop_tx = serial_omap_stop_tx, + .start_tx = serial_omap_start_tx, + .stop_rx = serial_omap_stop_rx, + .enable_ms = serial_omap_enable_ms, + .break_ctl = serial_omap_break_ctl, + .startup = serial_omap_startup, + .shutdown = serial_omap_shutdown, + .set_termios = serial_omap_set_termios, + .pm = serial_omap_pm, + .type = serial_omap_type, + .release_port = serial_omap_release_port, + .request_port = serial_omap_request_port, + .config_port = serial_omap_config_port, + .verify_port = serial_omap_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = serial_omap_poll_put_char, + .poll_get_char = serial_omap_poll_get_char, +#endif +}; + +static struct uart_driver serial_omap_reg = { + .owner = THIS_MODULE, + .driver_name = "OMAP-SERIAL", + .dev_name = OMAP_SERIAL_NAME, + .nr = OMAP_MAX_HSUART_PORTS, + .cons = OMAP_CONSOLE, +}; + +static int +serial_omap_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct uart_omap_port *up = platform_get_drvdata(pdev); + + if (up) + uart_suspend_port(&serial_omap_reg, &up->port); + return 0; +} + +static int serial_omap_resume(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + if (up) + uart_resume_port(&serial_omap_reg, &up->port); + return 0; +} + +static void serial_omap_rx_timeout(unsigned long uart_no) +{ + struct uart_omap_port *up = ui[uart_no]; + unsigned int curr_dma_pos, curr_transmitted_size; + int ret = 0; + + curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel); + if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) || + (curr_dma_pos == 0)) { + if (jiffies_to_msecs(jiffies - up->port_activity) < + RX_TIMEOUT) { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } else { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + return; + } + + curr_transmitted_size = curr_dma_pos - + up->uart_dma.prev_rx_dma_pos; + up->port.icount.rx += curr_transmitted_size; + tty_insert_flip_string(up->port.state->port.tty, + up->uart_dma.rx_buf + + (up->uart_dma.prev_rx_dma_pos - + up->uart_dma.rx_buf_dma_phys), + curr_transmitted_size); + tty_flip_buffer_push(up->port.state->port.tty); + up->uart_dma.prev_rx_dma_pos = curr_dma_pos; + if (up->uart_dma.rx_buf_size + + up->uart_dma.rx_buf_dma_phys == curr_dma_pos) { + ret = serial_omap_start_rxdma(up); + if (ret < 0) { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + } else { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } + up->port_activity = jiffies; +} + +static void uart_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + return; +} + +static int serial_omap_start_rxdma(struct uart_omap_port *up) +{ + int ret = 0; + + if (up->uart_dma.rx_dma_channel == -1) { + ret = omap_request_dma(up->uart_dma.uart_dma_rx, + "UART Rx DMA", + (void *)uart_rx_dma_callback, up, + &(up->uart_dma.rx_dma_channel)); + if (ret < 0) + return ret; + + omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, + up->uart_dma.rx_buf_dma_phys, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.rx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_rx, 0); + } + up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys; + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.rx_dma_channel); + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + up->uart_dma.rx_dma_used = true; + return ret; +} + +static void serial_omap_continue_tx(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + unsigned int start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + if (uart_circ_empty(xmit)) + return; + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start; + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct uart_omap_port *up = (struct uart_omap_port *)data; + struct circ_buf *xmit = &up->port.state->xmit; + + xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \ + (UART_XMIT_SIZE - 1); + up->port.icount.tx += up->uart_dma.tx_buf_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) { + spin_lock(&(up->uart_dma.tx_lock)); + serial_omap_stop_tx(&up->port); + up->uart_dma.tx_dma_used = false; + spin_unlock(&(up->uart_dma.tx_lock)); + } else { + omap_stop_dma(up->uart_dma.tx_dma_channel); + serial_omap_continue_tx(up); + } + up->port_activity = jiffies; + return; +} + +static int serial_omap_probe(struct platform_device *pdev) +{ + struct uart_omap_port *up; + struct resource *mem, *irq, *dma_tx, *dma_rx; + struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + int ret = -ENOSPC; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->dev.driver->name)) { + dev_err(&pdev->dev, "memory region already claimed\n"); + return -EBUSY; + } + + dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!dma_rx) { + ret = -EINVAL; + goto err; + } + + dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!dma_tx) { + ret = -EINVAL; + goto err; + } + + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (up == NULL) { + ret = -ENOMEM; + goto do_release_region; + } + sprintf(up->name, "OMAP UART%d", pdev->id); + up->pdev = pdev; + up->port.dev = &pdev->dev; + up->port.type = PORT_OMAP; + up->port.iotype = UPIO_MEM; + up->port.irq = irq->start; + + up->port.regshift = 2; + up->port.fifosize = 64; + up->port.ops = &serial_omap_pops; + up->port.line = pdev->id; + + up->port.membase = omap_up_info->membase; + up->port.mapbase = omap_up_info->mapbase; + up->port.flags = omap_up_info->flags; + up->port.irqflags = omap_up_info->irqflags; + up->port.uartclk = omap_up_info->uartclk; + up->uart_dma.uart_base = mem->start; + + if (omap_up_info->dma_enabled) { + up->uart_dma.uart_dma_tx = dma_tx->start; + up->uart_dma.uart_dma_rx = dma_rx->start; + up->use_dma = 1; + up->uart_dma.rx_buf_size = 4096; + up->uart_dma.rx_timeout = 2; + spin_lock_init(&(up->uart_dma.tx_lock)); + spin_lock_init(&(up->uart_dma.rx_lock)); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + ui[pdev->id] = up; + serial_omap_add_console_port(up); + + ret = uart_add_one_port(&serial_omap_reg, &up->port); + if (ret != 0) + goto do_release_region; + + platform_set_drvdata(pdev, up); + return 0; +err: + dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", + pdev->id, __func__, ret); +do_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return ret; +} + +static int serial_omap_remove(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + if (up) { + uart_remove_one_port(&serial_omap_reg, &up->port); + kfree(up); + } + return 0; +} + +static struct platform_driver serial_omap_driver = { + .probe = serial_omap_probe, + .remove = serial_omap_remove, + + .suspend = serial_omap_suspend, + .resume = serial_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init serial_omap_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_omap_reg); + if (ret != 0) + return ret; + ret = platform_driver_register(&serial_omap_driver); + if (ret != 0) + uart_unregister_driver(&serial_omap_reg); + return ret; +} + +static void __exit serial_omap_exit(void) +{ + platform_driver_unregister(&serial_omap_driver); + uart_unregister_driver(&serial_omap_reg); +} + +module_init(serial_omap_init); +module_exit(serial_omap_exit); + +MODULE_DESCRIPTION("OMAP High Speed UART driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c new file mode 100644 index 000000000000..70a61458ec42 --- /dev/null +++ b/drivers/tty/serial/pch_uart.c @@ -0,0 +1,1451 @@ +/* + *Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + *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; version 2 of the License. + * + *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 + +enum { + PCH_UART_HANDLED_RX_INT_SHIFT, + PCH_UART_HANDLED_TX_INT_SHIFT, + PCH_UART_HANDLED_RX_ERR_INT_SHIFT, + PCH_UART_HANDLED_RX_TRG_INT_SHIFT, + PCH_UART_HANDLED_MS_INT_SHIFT, +}; + +enum { + PCH_UART_8LINE, + PCH_UART_2LINE, +}; + +#define PCH_UART_DRIVER_DEVICE "ttyPCH" + +#define PCH_UART_NR_GE_256FIFO 1 +#define PCH_UART_NR_GE_64FIFO 3 +#define PCH_UART_NR_GE (PCH_UART_NR_GE_256FIFO+PCH_UART_NR_GE_64FIFO) +#define PCH_UART_NR PCH_UART_NR_GE + +#define PCH_UART_HANDLED_RX_INT (1<<((PCH_UART_HANDLED_RX_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_TX_INT (1<<((PCH_UART_HANDLED_TX_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_RX_ERR_INT (1<<((\ + PCH_UART_HANDLED_RX_ERR_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_RX_TRG_INT (1<<((\ + PCH_UART_HANDLED_RX_TRG_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_MS_INT (1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1)) + +#define PCH_UART_RBR 0x00 +#define PCH_UART_THR 0x00 + +#define PCH_UART_IER_MASK (PCH_UART_IER_ERBFI|PCH_UART_IER_ETBEI|\ + PCH_UART_IER_ELSI|PCH_UART_IER_EDSSI) +#define PCH_UART_IER_ERBFI 0x00000001 +#define PCH_UART_IER_ETBEI 0x00000002 +#define PCH_UART_IER_ELSI 0x00000004 +#define PCH_UART_IER_EDSSI 0x00000008 + +#define PCH_UART_IIR_IP 0x00000001 +#define PCH_UART_IIR_IID 0x00000006 +#define PCH_UART_IIR_MSI 0x00000000 +#define PCH_UART_IIR_TRI 0x00000002 +#define PCH_UART_IIR_RRI 0x00000004 +#define PCH_UART_IIR_REI 0x00000006 +#define PCH_UART_IIR_TOI 0x00000008 +#define PCH_UART_IIR_FIFO256 0x00000020 +#define PCH_UART_IIR_FIFO64 PCH_UART_IIR_FIFO256 +#define PCH_UART_IIR_FE 0x000000C0 + +#define PCH_UART_FCR_FIFOE 0x00000001 +#define PCH_UART_FCR_RFR 0x00000002 +#define PCH_UART_FCR_TFR 0x00000004 +#define PCH_UART_FCR_DMS 0x00000008 +#define PCH_UART_FCR_FIFO256 0x00000020 +#define PCH_UART_FCR_RFTL 0x000000C0 + +#define PCH_UART_FCR_RFTL1 0x00000000 +#define PCH_UART_FCR_RFTL64 0x00000040 +#define PCH_UART_FCR_RFTL128 0x00000080 +#define PCH_UART_FCR_RFTL224 0x000000C0 +#define PCH_UART_FCR_RFTL16 PCH_UART_FCR_RFTL64 +#define PCH_UART_FCR_RFTL32 PCH_UART_FCR_RFTL128 +#define PCH_UART_FCR_RFTL56 PCH_UART_FCR_RFTL224 +#define PCH_UART_FCR_RFTL4 PCH_UART_FCR_RFTL64 +#define PCH_UART_FCR_RFTL8 PCH_UART_FCR_RFTL128 +#define PCH_UART_FCR_RFTL14 PCH_UART_FCR_RFTL224 +#define PCH_UART_FCR_RFTL_SHIFT 6 + +#define PCH_UART_LCR_WLS 0x00000003 +#define PCH_UART_LCR_STB 0x00000004 +#define PCH_UART_LCR_PEN 0x00000008 +#define PCH_UART_LCR_EPS 0x00000010 +#define PCH_UART_LCR_SP 0x00000020 +#define PCH_UART_LCR_SB 0x00000040 +#define PCH_UART_LCR_DLAB 0x00000080 +#define PCH_UART_LCR_NP 0x00000000 +#define PCH_UART_LCR_OP PCH_UART_LCR_PEN +#define PCH_UART_LCR_EP (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS) +#define PCH_UART_LCR_1P (PCH_UART_LCR_PEN | PCH_UART_LCR_SP) +#define PCH_UART_LCR_0P (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS |\ + PCH_UART_LCR_SP) + +#define PCH_UART_LCR_5BIT 0x00000000 +#define PCH_UART_LCR_6BIT 0x00000001 +#define PCH_UART_LCR_7BIT 0x00000002 +#define PCH_UART_LCR_8BIT 0x00000003 + +#define PCH_UART_MCR_DTR 0x00000001 +#define PCH_UART_MCR_RTS 0x00000002 +#define PCH_UART_MCR_OUT 0x0000000C +#define PCH_UART_MCR_LOOP 0x00000010 +#define PCH_UART_MCR_AFE 0x00000020 + +#define PCH_UART_LSR_DR 0x00000001 +#define PCH_UART_LSR_ERR (1<<7) + +#define PCH_UART_MSR_DCTS 0x00000001 +#define PCH_UART_MSR_DDSR 0x00000002 +#define PCH_UART_MSR_TERI 0x00000004 +#define PCH_UART_MSR_DDCD 0x00000008 +#define PCH_UART_MSR_CTS 0x00000010 +#define PCH_UART_MSR_DSR 0x00000020 +#define PCH_UART_MSR_RI 0x00000040 +#define PCH_UART_MSR_DCD 0x00000080 +#define PCH_UART_MSR_DELTA (PCH_UART_MSR_DCTS | PCH_UART_MSR_DDSR |\ + PCH_UART_MSR_TERI | PCH_UART_MSR_DDCD) + +#define PCH_UART_DLL 0x00 +#define PCH_UART_DLM 0x01 + +#define DIV_ROUND(a, b) (((a) + ((b)/2)) / (b)) + +#define PCH_UART_IID_RLS (PCH_UART_IIR_REI) +#define PCH_UART_IID_RDR (PCH_UART_IIR_RRI) +#define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI) +#define PCH_UART_IID_THRE (PCH_UART_IIR_TRI) +#define PCH_UART_IID_MS (PCH_UART_IIR_MSI) + +#define PCH_UART_HAL_PARITY_NONE (PCH_UART_LCR_NP) +#define PCH_UART_HAL_PARITY_ODD (PCH_UART_LCR_OP) +#define PCH_UART_HAL_PARITY_EVEN (PCH_UART_LCR_EP) +#define PCH_UART_HAL_PARITY_FIX1 (PCH_UART_LCR_1P) +#define PCH_UART_HAL_PARITY_FIX0 (PCH_UART_LCR_0P) +#define PCH_UART_HAL_5BIT (PCH_UART_LCR_5BIT) +#define PCH_UART_HAL_6BIT (PCH_UART_LCR_6BIT) +#define PCH_UART_HAL_7BIT (PCH_UART_LCR_7BIT) +#define PCH_UART_HAL_8BIT (PCH_UART_LCR_8BIT) +#define PCH_UART_HAL_STB1 0 +#define PCH_UART_HAL_STB2 (PCH_UART_LCR_STB) + +#define PCH_UART_HAL_CLR_TX_FIFO (PCH_UART_FCR_TFR) +#define PCH_UART_HAL_CLR_RX_FIFO (PCH_UART_FCR_RFR) +#define PCH_UART_HAL_CLR_ALL_FIFO (PCH_UART_HAL_CLR_TX_FIFO | \ + PCH_UART_HAL_CLR_RX_FIFO) + +#define PCH_UART_HAL_DMA_MODE0 0 +#define PCH_UART_HAL_FIFO_DIS 0 +#define PCH_UART_HAL_FIFO16 (PCH_UART_FCR_FIFOE) +#define PCH_UART_HAL_FIFO256 (PCH_UART_FCR_FIFOE | \ + PCH_UART_FCR_FIFO256) +#define PCH_UART_HAL_FIFO64 (PCH_UART_HAL_FIFO256) +#define PCH_UART_HAL_TRIGGER1 (PCH_UART_FCR_RFTL1) +#define PCH_UART_HAL_TRIGGER64 (PCH_UART_FCR_RFTL64) +#define PCH_UART_HAL_TRIGGER128 (PCH_UART_FCR_RFTL128) +#define PCH_UART_HAL_TRIGGER224 (PCH_UART_FCR_RFTL224) +#define PCH_UART_HAL_TRIGGER16 (PCH_UART_FCR_RFTL16) +#define PCH_UART_HAL_TRIGGER32 (PCH_UART_FCR_RFTL32) +#define PCH_UART_HAL_TRIGGER56 (PCH_UART_FCR_RFTL56) +#define PCH_UART_HAL_TRIGGER4 (PCH_UART_FCR_RFTL4) +#define PCH_UART_HAL_TRIGGER8 (PCH_UART_FCR_RFTL8) +#define PCH_UART_HAL_TRIGGER14 (PCH_UART_FCR_RFTL14) +#define PCH_UART_HAL_TRIGGER_L (PCH_UART_FCR_RFTL64) +#define PCH_UART_HAL_TRIGGER_M (PCH_UART_FCR_RFTL128) +#define PCH_UART_HAL_TRIGGER_H (PCH_UART_FCR_RFTL224) + +#define PCH_UART_HAL_RX_INT (PCH_UART_IER_ERBFI) +#define PCH_UART_HAL_TX_INT (PCH_UART_IER_ETBEI) +#define PCH_UART_HAL_RX_ERR_INT (PCH_UART_IER_ELSI) +#define PCH_UART_HAL_MS_INT (PCH_UART_IER_EDSSI) +#define PCH_UART_HAL_ALL_INT (PCH_UART_IER_MASK) + +#define PCH_UART_HAL_DTR (PCH_UART_MCR_DTR) +#define PCH_UART_HAL_RTS (PCH_UART_MCR_RTS) +#define PCH_UART_HAL_OUT (PCH_UART_MCR_OUT) +#define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP) +#define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE) + +struct pch_uart_buffer { + unsigned char *buf; + int size; +}; + +struct eg20t_port { + struct uart_port port; + int port_type; + void __iomem *membase; + resource_size_t mapbase; + unsigned int iobase; + struct pci_dev *pdev; + int fifo_size; + int base_baud; + int start_tx; + int start_rx; + int tx_empty; + int int_dis_flag; + int trigger; + int trigger_level; + struct pch_uart_buffer rxbuf; + unsigned int dmsr; + unsigned int fcr; + unsigned int use_dma; + unsigned int use_dma_flag; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx; + struct pch_dma_slave param_tx; + struct pch_dma_slave param_rx; + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct scatterlist sg_tx; + struct scatterlist sg_rx; + int tx_dma_use; + void *rx_buf_virt; + dma_addr_t rx_buf_dma; +}; + +static unsigned int default_baud = 9600; +static const int trigger_level_256[4] = { 1, 64, 128, 224 }; +static const int trigger_level_64[4] = { 1, 16, 32, 56 }; +static const int trigger_level_16[4] = { 1, 4, 8, 14 }; +static const int trigger_level_1[4] = { 1, 1, 1, 1 }; + +static void pch_uart_hal_request(struct pci_dev *pdev, int fifosize, + int base_baud) +{ + struct eg20t_port *priv = pci_get_drvdata(pdev); + + priv->trigger_level = 1; + priv->fcr = 0; +} + +static unsigned int get_msr(struct eg20t_port *priv, void __iomem *base) +{ + unsigned int msr = ioread8(base + UART_MSR); + priv->dmsr |= msr & PCH_UART_MSR_DELTA; + + return msr; +} + +static void pch_uart_hal_enable_interrupt(struct eg20t_port *priv, + unsigned int flag) +{ + u8 ier = ioread8(priv->membase + UART_IER); + ier |= flag & PCH_UART_IER_MASK; + iowrite8(ier, priv->membase + UART_IER); +} + +static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv, + unsigned int flag) +{ + u8 ier = ioread8(priv->membase + UART_IER); + ier &= ~(flag & PCH_UART_IER_MASK); + iowrite8(ier, priv->membase + UART_IER); +} + +static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, + unsigned int parity, unsigned int bits, + unsigned int stb) +{ + unsigned int dll, dlm, lcr; + int div; + + div = DIV_ROUND(priv->base_baud / 16, baud); + if (div < 0 || USHRT_MAX <= div) { + pr_err("Invalid Baud(div=0x%x)\n", div); + return -EINVAL; + } + + dll = (unsigned int)div & 0x00FFU; + dlm = ((unsigned int)div >> 8) & 0x00FFU; + + if (parity & ~(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS | PCH_UART_LCR_SP)) { + pr_err("Invalid parity(0x%x)\n", parity); + return -EINVAL; + } + + if (bits & ~PCH_UART_LCR_WLS) { + pr_err("Invalid bits(0x%x)\n", bits); + return -EINVAL; + } + + if (stb & ~PCH_UART_LCR_STB) { + pr_err("Invalid STB(0x%x)\n", stb); + return -EINVAL; + } + + lcr = parity; + lcr |= bits; + lcr |= stb; + + pr_debug("%s:baud = %d, div = %04x, lcr = %02x (%lu)\n", + __func__, baud, div, lcr, jiffies); + iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR); + iowrite8(dll, priv->membase + PCH_UART_DLL); + iowrite8(dlm, priv->membase + PCH_UART_DLM); + iowrite8(lcr, priv->membase + UART_LCR); + + return 0; +} + +static int pch_uart_hal_fifo_reset(struct eg20t_port *priv, + unsigned int flag) +{ + if (flag & ~(PCH_UART_FCR_TFR | PCH_UART_FCR_RFR)) { + pr_err("%s:Invalid flag(0x%x)\n", __func__, flag); + return -EINVAL; + } + + iowrite8(PCH_UART_FCR_FIFOE | priv->fcr, priv->membase + UART_FCR); + iowrite8(PCH_UART_FCR_FIFOE | priv->fcr | flag, + priv->membase + UART_FCR); + iowrite8(priv->fcr, priv->membase + UART_FCR); + + return 0; +} + +static int pch_uart_hal_set_fifo(struct eg20t_port *priv, + unsigned int dmamode, + unsigned int fifo_size, unsigned int trigger) +{ + u8 fcr; + + if (dmamode & ~PCH_UART_FCR_DMS) { + pr_err("%s:Invalid DMA Mode(0x%x)\n", __func__, dmamode); + return -EINVAL; + } + + if (fifo_size & ~(PCH_UART_FCR_FIFOE | PCH_UART_FCR_FIFO256)) { + pr_err("%s:Invalid FIFO SIZE(0x%x)\n", __func__, fifo_size); + return -EINVAL; + } + + if (trigger & ~PCH_UART_FCR_RFTL) { + pr_err("%s:Invalid TRIGGER(0x%x)\n", __func__, trigger); + return -EINVAL; + } + + switch (priv->fifo_size) { + case 256: + priv->trigger_level = + trigger_level_256[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + case 64: + priv->trigger_level = + trigger_level_64[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + case 16: + priv->trigger_level = + trigger_level_16[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + default: + priv->trigger_level = + trigger_level_1[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + } + fcr = + dmamode | fifo_size | trigger | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR; + iowrite8(PCH_UART_FCR_FIFOE, priv->membase + UART_FCR); + iowrite8(PCH_UART_FCR_FIFOE | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR, + priv->membase + UART_FCR); + iowrite8(fcr, priv->membase + UART_FCR); + priv->fcr = fcr; + + return 0; +} + +static u8 pch_uart_hal_get_modem(struct eg20t_port *priv) +{ + priv->dmsr = 0; + return get_msr(priv, priv->membase); +} + +static int pch_uart_hal_write(struct eg20t_port *priv, + const unsigned char *buf, int tx_size) +{ + int i; + unsigned int thr; + + for (i = 0; i < tx_size;) { + thr = buf[i++]; + iowrite8(thr, priv->membase + PCH_UART_THR); + } + return i; +} + +static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf, + int rx_size) +{ + int i; + u8 rbr, lsr; + + lsr = ioread8(priv->membase + UART_LSR); + for (i = 0, lsr = ioread8(priv->membase + UART_LSR); + i < rx_size && lsr & UART_LSR_DR; + lsr = ioread8(priv->membase + UART_LSR)) { + rbr = ioread8(priv->membase + PCH_UART_RBR); + buf[i++] = rbr; + } + return i; +} + +static unsigned int pch_uart_hal_get_iid(struct eg20t_port *priv) +{ + unsigned int iir; + int ret; + + iir = ioread8(priv->membase + UART_IIR); + ret = (iir & (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP)); + return ret; +} + +static u8 pch_uart_hal_get_line_status(struct eg20t_port *priv) +{ + return ioread8(priv->membase + UART_LSR); +} + +static void pch_uart_hal_set_break(struct eg20t_port *priv, int on) +{ + unsigned int lcr; + + lcr = ioread8(priv->membase + UART_LCR); + if (on) + lcr |= PCH_UART_LCR_SB; + else + lcr &= ~PCH_UART_LCR_SB; + + iowrite8(lcr, priv->membase + UART_LCR); +} + +static int push_rx(struct eg20t_port *priv, const unsigned char *buf, + int size) +{ + struct uart_port *port; + struct tty_struct *tty; + + port = &priv->port; + tty = tty_port_tty_get(&port->state->port); + if (!tty) { + pr_debug("%s:tty is busy now", __func__); + return -EBUSY; + } + + tty_insert_flip_string(tty, buf, size); + tty_flip_buffer_push(tty); + tty_kref_put(tty); + + return 0; +} + +static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf) +{ + int ret; + struct uart_port *port = &priv->port; + + if (port->x_char) { + pr_debug("%s:X character send %02x (%lu)\n", __func__, + port->x_char, jiffies); + buf[0] = port->x_char; + port->x_char = 0; + ret = 1; + } else { + ret = 0; + } + + return ret; +} + +static int dma_push_rx(struct eg20t_port *priv, int size) +{ + struct tty_struct *tty; + int room; + struct uart_port *port = &priv->port; + + port = &priv->port; + tty = tty_port_tty_get(&port->state->port); + if (!tty) { + pr_debug("%s:tty is busy now", __func__); + return 0; + } + + room = tty_buffer_request_room(tty, size); + + if (room < size) + dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + size - room); + if (!room) + return room; + + tty_insert_flip_string(tty, sg_virt(&priv->sg_rx), size); + + port->icount.rx += room; + tty_kref_put(tty); + + return room; +} + +static void pch_free_dma(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + + if (priv->chan_tx) { + dma_release_channel(priv->chan_tx); + priv->chan_tx = NULL; + } + if (priv->chan_rx) { + dma_release_channel(priv->chan_rx); + priv->chan_rx = NULL; + } + if (sg_dma_address(&priv->sg_rx)) + dma_free_coherent(port->dev, port->fifosize, + sg_virt(&priv->sg_rx), + sg_dma_address(&priv->sg_rx)); + + return; +} + +static bool filter(struct dma_chan *chan, void *slave) +{ + struct pch_dma_slave *param = slave; + + if ((chan->chan_id == param->chan_id) && (param->dma_dev == + chan->device->dev)) { + chan->private = param; + return true; + } else { + return false; + } +} + +static void pch_request_dma(struct uart_port *port) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct pci_dev *dma_dev; + struct pch_dma_slave *param; + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0xa, 0)); /* Get DMA's dev + information */ + /* Set Tx DMA */ + param = &priv->param_tx; + param->dma_dev = &dma_dev->dev; + param->chan_id = priv->port.line; + param->tx_reg = port->mapbase + UART_TX; + chan = dma_request_channel(mask, filter, param); + if (!chan) { + pr_err("%s:dma_request_channel FAILS(Tx)\n", __func__); + return; + } + priv->chan_tx = chan; + + /* Set Rx DMA */ + param = &priv->param_rx; + param->dma_dev = &dma_dev->dev; + param->chan_id = priv->port.line + 1; /* Rx = Tx + 1 */ + param->rx_reg = port->mapbase + UART_RX; + chan = dma_request_channel(mask, filter, param); + if (!chan) { + pr_err("%s:dma_request_channel FAILS(Rx)\n", __func__); + dma_release_channel(priv->chan_tx); + return; + } + + /* Get Consistent memory for DMA */ + priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, + &priv->rx_buf_dma, GFP_KERNEL); + priv->chan_rx = chan; +} + +static void pch_dma_rx_complete(void *arg) +{ + struct eg20t_port *priv = arg; + struct uart_port *port = &priv->port; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + + if (!tty) { + pr_debug("%s:tty is busy now", __func__); + return; + } + + if (dma_push_rx(priv, priv->trigger_level)) + tty_flip_buffer_push(tty); + + tty_kref_put(tty); +} + +static void pch_dma_tx_complete(void *arg) +{ + struct eg20t_port *priv = arg; + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + + xmit->tail += sg_dma_len(&priv->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + port->icount.tx += sg_dma_len(&priv->sg_tx); + + async_tx_ack(priv->desc_tx); + priv->tx_dma_use = 0; +} + +static int pop_tx(struct eg20t_port *priv, unsigned char *buf, int size) +{ + int count = 0; + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + + if (uart_tx_stopped(port) || uart_circ_empty(xmit) || count >= size) + goto pop_tx_end; + + do { + int cnt_to_end = + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + int sz = min(size - count, cnt_to_end); + memcpy(&buf[count], &xmit->buf[xmit->tail], sz); + xmit->tail = (xmit->tail + sz) & (UART_XMIT_SIZE - 1); + count += sz; + } while (!uart_circ_empty(xmit) && count < size); + +pop_tx_end: + pr_debug("%d characters. Remained %d characters. (%lu)\n", + count, size - count, jiffies); + + return count; +} + +static int handle_rx_to(struct eg20t_port *priv) +{ + struct pch_uart_buffer *buf; + int rx_size; + int ret; + if (!priv->start_rx) { + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); + return 0; + } + buf = &priv->rxbuf; + do { + rx_size = pch_uart_hal_read(priv, buf->buf, buf->size); + ret = push_rx(priv, buf->buf, rx_size); + if (ret) + return 0; + } while (rx_size == buf->size); + + return PCH_UART_HANDLED_RX_INT; +} + +static int handle_rx(struct eg20t_port *priv) +{ + return handle_rx_to(priv); +} + +static int dma_handle_rx(struct eg20t_port *priv) +{ + struct uart_port *port = &priv->port; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sg; + + priv = container_of(port, struct eg20t_port, port); + sg = &priv->sg_rx; + + sg_init_table(&priv->sg_rx, 1); /* Initialize SG table */ + + sg_dma_len(sg) = priv->fifo_size; + + sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), + sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & + ~PAGE_MASK); + + sg_dma_address(sg) = priv->rx_buf_dma; + + desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, + sg, 1, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!desc) + return 0; + + priv->desc_rx = desc; + desc->callback = pch_dma_rx_complete; + desc->callback_param = priv; + desc->tx_submit(desc); + dma_async_issue_pending(priv->chan_rx); + + return PCH_UART_HANDLED_RX_INT; +} + +static unsigned int handle_tx(struct eg20t_port *priv) +{ + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + int ret; + int fifo_size; + int tx_size; + int size; + int tx_empty; + + if (!priv->start_tx) { + pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + priv->tx_empty = 1; + return 0; + } + + fifo_size = max(priv->fifo_size, 1); + tx_empty = 1; + if (pop_tx_x(priv, xmit->buf)) { + pch_uart_hal_write(priv, xmit->buf, 1); + port->icount.tx++; + tx_empty = 0; + fifo_size--; + } + size = min(xmit->head - xmit->tail, fifo_size); + tx_size = pop_tx(priv, xmit->buf, size); + if (tx_size > 0) { + ret = pch_uart_hal_write(priv, xmit->buf, tx_size); + port->icount.tx += ret; + tx_empty = 0; + } + + priv->tx_empty = tx_empty; + + if (tx_empty) + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + + return PCH_UART_HANDLED_TX_INT; +} + +static unsigned int dma_handle_tx(struct eg20t_port *priv) +{ + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + struct scatterlist *sg = &priv->sg_tx; + int nent; + int fifo_size; + int tx_empty; + struct dma_async_tx_descriptor *desc; + + if (!priv->start_tx) { + pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + priv->tx_empty = 1; + return 0; + } + + fifo_size = max(priv->fifo_size, 1); + tx_empty = 1; + if (pop_tx_x(priv, xmit->buf)) { + pch_uart_hal_write(priv, xmit->buf, 1); + port->icount.tx++; + tx_empty = 0; + fifo_size--; + } + + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + + priv->tx_dma_use = 1; + + sg_init_table(&priv->sg_tx, 1); /* Initialize SG table */ + + sg_set_page(&priv->sg_tx, virt_to_page(xmit->buf), + UART_XMIT_SIZE, (int)xmit->buf & ~PAGE_MASK); + + nent = dma_map_sg(port->dev, &priv->sg_tx, 1, DMA_TO_DEVICE); + if (!nent) { + pr_err("%s:dma_map_sg Failed\n", __func__); + return 0; + } + + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, + UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, + xmit->tail, UART_XMIT_SIZE)); + + desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, + sg, nent, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + pr_err("%s:device_prep_slave_sg Failed\n", __func__); + return 0; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + + priv->desc_tx = desc; + desc->callback = pch_dma_tx_complete; + desc->callback_param = priv; + + desc->tx_submit(desc); + + dma_async_issue_pending(priv->chan_tx); + + return PCH_UART_HANDLED_TX_INT; +} + +static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr) +{ + u8 fcr = ioread8(priv->membase + UART_FCR); + + /* Reset FIFO */ + fcr |= UART_FCR_CLEAR_RCVR; + iowrite8(fcr, priv->membase + UART_FCR); + + if (lsr & PCH_UART_LSR_ERR) + dev_err(&priv->pdev->dev, "Error data in FIFO\n"); + + if (lsr & UART_LSR_FE) + dev_err(&priv->pdev->dev, "Framing Error\n"); + + if (lsr & UART_LSR_PE) + dev_err(&priv->pdev->dev, "Parity Error\n"); + + if (lsr & UART_LSR_OE) + dev_err(&priv->pdev->dev, "Overrun Error\n"); +} + +static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) +{ + struct eg20t_port *priv = dev_id; + unsigned int handled; + u8 lsr; + int ret = 0; + unsigned int iid; + unsigned long flags; + + spin_lock_irqsave(&priv->port.lock, flags); + handled = 0; + while ((iid = pch_uart_hal_get_iid(priv)) > 1) { + switch (iid) { + case PCH_UART_IID_RLS: /* Receiver Line Status */ + lsr = pch_uart_hal_get_line_status(priv); + if (lsr & (PCH_UART_LSR_ERR | UART_LSR_FE | + UART_LSR_PE | UART_LSR_OE)) { + pch_uart_err_ir(priv, lsr); + ret = PCH_UART_HANDLED_RX_ERR_INT; + } + break; + case PCH_UART_IID_RDR: /* Received Data Ready */ + if (priv->use_dma) + ret = dma_handle_rx(priv); + else + ret = handle_rx(priv); + break; + case PCH_UART_IID_RDR_TO: /* Received Data Ready + (FIFO Timeout) */ + ret = handle_rx_to(priv); + break; + case PCH_UART_IID_THRE: /* Transmitter Holding Register + Empty */ + if (priv->use_dma) + ret = dma_handle_tx(priv); + else + ret = handle_tx(priv); + break; + case PCH_UART_IID_MS: /* Modem Status */ + ret = PCH_UART_HANDLED_MS_INT; + break; + default: /* Never junp to this label */ + pr_err("%s:iid=%d (%lu)\n", __func__, iid, jiffies); + ret = -1; + break; + } + handled |= (unsigned int)ret; + } + if (handled == 0 && iid <= 1) { + if (priv->int_dis_flag) + priv->int_dis_flag = 0; + } + + spin_unlock_irqrestore(&priv->port.lock, flags); + return IRQ_RETVAL(handled); +} + +/* This function tests whether the transmitter fifo and shifter for the port + described by 'port' is empty. */ +static unsigned int pch_uart_tx_empty(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + priv = container_of(port, struct eg20t_port, port); + if (priv->tx_empty) + ret = TIOCSER_TEMT; + else + ret = 0; + + return ret; +} + +/* Returns the current state of modem control inputs. */ +static unsigned int pch_uart_get_mctrl(struct uart_port *port) +{ + struct eg20t_port *priv; + u8 modem; + unsigned int ret = 0; + + priv = container_of(port, struct eg20t_port, port); + modem = pch_uart_hal_get_modem(priv); + + if (modem & UART_MSR_DCD) + ret |= TIOCM_CAR; + + if (modem & UART_MSR_RI) + ret |= TIOCM_RNG; + + if (modem & UART_MSR_DSR) + ret |= TIOCM_DSR; + + if (modem & UART_MSR_CTS) + ret |= TIOCM_CTS; + + return ret; +} + +static void pch_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + u32 mcr = 0; + unsigned int dat; + struct eg20t_port *priv = container_of(port, struct eg20t_port, port); + + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + if (mctrl) { + dat = pch_uart_get_mctrl(port); + dat |= mcr; + iowrite8(dat, priv->membase + UART_MCR); + } +} + +static void pch_uart_stop_tx(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + priv->start_tx = 0; + priv->tx_dma_use = 0; +} + +static void pch_uart_start_tx(struct uart_port *port) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + + if (priv->use_dma) + if (priv->tx_dma_use) + return; + + priv->start_tx = 1; + pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT); +} + +static void pch_uart_stop_rx(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + priv->start_rx = 0; + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); + priv->int_dis_flag = 1; +} + +/* Enable the modem status interrupts. */ +static void pch_uart_enable_ms(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_MS_INT); +} + +/* Control the transmission of a break signal. */ +static void pch_uart_break_ctl(struct uart_port *port, int ctl) +{ + struct eg20t_port *priv; + unsigned long flags; + + priv = container_of(port, struct eg20t_port, port); + spin_lock_irqsave(&port->lock, flags); + pch_uart_hal_set_break(priv, ctl); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Grab any interrupt resources and initialise any low level driver state. */ +static int pch_uart_startup(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + int fifo_size; + int trigger_level; + + priv = container_of(port, struct eg20t_port, port); + priv->tx_empty = 1; + port->uartclk = priv->base_baud; + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + ret = pch_uart_hal_set_line(priv, default_baud, + PCH_UART_HAL_PARITY_NONE, PCH_UART_HAL_8BIT, + PCH_UART_HAL_STB1); + if (ret) + return ret; + + switch (priv->fifo_size) { + case 256: + fifo_size = PCH_UART_HAL_FIFO256; + break; + case 64: + fifo_size = PCH_UART_HAL_FIFO64; + break; + case 16: + fifo_size = PCH_UART_HAL_FIFO16; + case 1: + default: + fifo_size = PCH_UART_HAL_FIFO_DIS; + break; + } + + switch (priv->trigger) { + case PCH_UART_HAL_TRIGGER1: + trigger_level = 1; + break; + case PCH_UART_HAL_TRIGGER_L: + trigger_level = priv->fifo_size / 4; + break; + case PCH_UART_HAL_TRIGGER_M: + trigger_level = priv->fifo_size / 2; + break; + case PCH_UART_HAL_TRIGGER_H: + default: + trigger_level = priv->fifo_size - (priv->fifo_size / 8); + break; + } + + priv->trigger_level = trigger_level; + ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0, + fifo_size, priv->trigger); + if (ret < 0) + return ret; + + ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED, + KBUILD_MODNAME, priv); + if (ret < 0) + return ret; + + if (priv->use_dma) + pch_request_dma(port); + + priv->start_rx = 1; + pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT); + uart_update_timeout(port, CS8, default_baud); + + return 0; +} + +static void pch_uart_shutdown(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + + priv = container_of(port, struct eg20t_port, port); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + pch_uart_hal_fifo_reset(priv, PCH_UART_HAL_CLR_ALL_FIFO); + ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0, + PCH_UART_HAL_FIFO_DIS, PCH_UART_HAL_TRIGGER1); + if (ret) + pr_err("pch_uart_hal_set_fifo Failed(ret=%d)\n", ret); + + if (priv->use_dma_flag) + pch_free_dma(port); + + free_irq(priv->port.irq, priv); +} + +/* Change the port parameters, including word length, parity, stop + *bits. Update read_status_mask and ignore_status_mask to indicate + *the types of events we are interested in receiving. */ +static void pch_uart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + int baud; + int rtn; + unsigned int parity, bits, stb; + struct eg20t_port *priv; + unsigned long flags; + + priv = container_of(port, struct eg20t_port, port); + switch (termios->c_cflag & CSIZE) { + case CS5: + bits = PCH_UART_HAL_5BIT; + break; + case CS6: + bits = PCH_UART_HAL_6BIT; + break; + case CS7: + bits = PCH_UART_HAL_7BIT; + break; + default: /* CS8 */ + bits = PCH_UART_HAL_8BIT; + break; + } + if (termios->c_cflag & CSTOPB) + stb = PCH_UART_HAL_STB2; + else + stb = PCH_UART_HAL_STB1; + + if (termios->c_cflag & PARENB) { + if (!(termios->c_cflag & PARODD)) + parity = PCH_UART_HAL_PARITY_ODD; + else + parity = PCH_UART_HAL_PARITY_EVEN; + + } else { + parity = PCH_UART_HAL_PARITY_NONE; + } + termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); + if (rtn) + goto out; + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + +out: + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *pch_uart_type(struct uart_port *port) +{ + return KBUILD_MODNAME; +} + +static void pch_uart_release_port(struct uart_port *port) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + pci_iounmap(priv->pdev, priv->membase); + pci_release_regions(priv->pdev); +} + +static int pch_uart_request_port(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + void __iomem *membase; + + priv = container_of(port, struct eg20t_port, port); + ret = pci_request_regions(priv->pdev, KBUILD_MODNAME); + if (ret < 0) + return -EBUSY; + + membase = pci_iomap(priv->pdev, 1, 0); + if (!membase) { + pci_release_regions(priv->pdev); + return -EBUSY; + } + priv->membase = port->membase = membase; + + return 0; +} + +static void pch_uart_config_port(struct uart_port *port, int type) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + if (type & UART_CONFIG_TYPE) { + port->type = priv->port_type; + pch_uart_request_port(port); + } +} + +static int pch_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + if (serinfo->flags & UPF_LOW_LATENCY) { + pr_info("PCH UART : Use PIO Mode (without DMA)\n"); + priv->use_dma = 0; + serinfo->flags &= ~UPF_LOW_LATENCY; + } else { +#ifndef CONFIG_PCH_DMA + pr_err("%s : PCH DMA is not Loaded.\n", __func__); + return -EOPNOTSUPP; +#endif + priv->use_dma = 1; + priv->use_dma_flag = 1; + pr_info("PCH UART : Use DMA Mode\n"); + } + + return 0; +} + +static struct uart_ops pch_uart_ops = { + .tx_empty = pch_uart_tx_empty, + .set_mctrl = pch_uart_set_mctrl, + .get_mctrl = pch_uart_get_mctrl, + .stop_tx = pch_uart_stop_tx, + .start_tx = pch_uart_start_tx, + .stop_rx = pch_uart_stop_rx, + .enable_ms = pch_uart_enable_ms, + .break_ctl = pch_uart_break_ctl, + .startup = pch_uart_startup, + .shutdown = pch_uart_shutdown, + .set_termios = pch_uart_set_termios, +/* .pm = pch_uart_pm, Not supported yet */ +/* .set_wake = pch_uart_set_wake, Not supported yet */ + .type = pch_uart_type, + .release_port = pch_uart_release_port, + .request_port = pch_uart_request_port, + .config_port = pch_uart_config_port, + .verify_port = pch_uart_verify_port +}; + +static struct uart_driver pch_uart_driver = { + .owner = THIS_MODULE, + .driver_name = KBUILD_MODNAME, + .dev_name = PCH_UART_DRIVER_DEVICE, + .major = 0, + .minor = 0, + .nr = PCH_UART_NR, +}; + +static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, + int port_type) +{ + struct eg20t_port *priv; + int ret; + unsigned int iobase; + unsigned int mapbase; + unsigned char *rxbuf; + int fifosize, base_baud; + static int num; + + priv = kzalloc(sizeof(struct eg20t_port), GFP_KERNEL); + if (priv == NULL) + goto init_port_alloc_err; + + rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!rxbuf) + goto init_port_free_txbuf; + + switch (port_type) { + case PORT_UNKNOWN: + fifosize = 256; /* UART0 */ + base_baud = 1843200; /* 1.8432MHz */ + break; + case PORT_8250: + fifosize = 64; /* UART1~3 */ + base_baud = 1843200; /* 1.8432MHz */ + break; + default: + dev_err(&pdev->dev, "Invalid Port Type(=%d)\n", port_type); + goto init_port_hal_free; + } + + iobase = pci_resource_start(pdev, 0); + mapbase = pci_resource_start(pdev, 1); + priv->mapbase = mapbase; + priv->iobase = iobase; + priv->pdev = pdev; + priv->tx_empty = 1; + priv->rxbuf.buf = rxbuf; + priv->rxbuf.size = PAGE_SIZE; + + priv->fifo_size = fifosize; + priv->base_baud = base_baud; + priv->port_type = PORT_MAX_8250 + port_type + 1; + priv->port.dev = &pdev->dev; + priv->port.iobase = iobase; + priv->port.membase = NULL; + priv->port.mapbase = mapbase; + priv->port.irq = pdev->irq; + priv->port.iotype = UPIO_PORT; + priv->port.ops = &pch_uart_ops; + priv->port.flags = UPF_BOOT_AUTOCONF; + priv->port.fifosize = fifosize; + priv->port.line = num++; + priv->trigger = PCH_UART_HAL_TRIGGER_M; + + pci_set_drvdata(pdev, priv); + pch_uart_hal_request(pdev, fifosize, base_baud); + ret = uart_add_one_port(&pch_uart_driver, &priv->port); + if (ret < 0) + goto init_port_hal_free; + + return priv; + +init_port_hal_free: + free_page((unsigned long)rxbuf); +init_port_free_txbuf: + kfree(priv); +init_port_alloc_err: + + return NULL; +} + +static void pch_uart_exit_port(struct eg20t_port *priv) +{ + uart_remove_one_port(&pch_uart_driver, &priv->port); + pci_set_drvdata(priv->pdev, NULL); + free_page((unsigned long)priv->rxbuf.buf); +} + +static void pch_uart_pci_remove(struct pci_dev *pdev) +{ + struct eg20t_port *priv; + + priv = (struct eg20t_port *)pci_get_drvdata(pdev); + pch_uart_exit_port(priv); + pci_disable_device(pdev); + kfree(priv); + return; +} +#ifdef CONFIG_PM +static int pch_uart_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct eg20t_port *priv = pci_get_drvdata(pdev); + + uart_suspend_port(&pch_uart_driver, &priv->port); + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int pch_uart_pci_resume(struct pci_dev *pdev) +{ + struct eg20t_port *priv = pci_get_drvdata(pdev); + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s-pci_enable_device failed(ret=%d) ", __func__, ret); + return ret; + } + + uart_resume_port(&pch_uart_driver, &priv->port); + + return 0; +} +#else +#define pch_uart_pci_suspend NULL +#define pch_uart_pci_resume NULL +#endif + +static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811), + .driver_data = PCH_UART_8LINE}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812), + .driver_data = PCH_UART_2LINE}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8813), + .driver_data = PCH_UART_2LINE}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8814), + .driver_data = PCH_UART_2LINE}, + {0,}, +}; + +static int __devinit pch_uart_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret; + struct eg20t_port *priv; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto probe_error; + + priv = pch_uart_init_port(pdev, id->driver_data); + if (!priv) { + ret = -EBUSY; + goto probe_disable_device; + } + pci_set_drvdata(pdev, priv); + + return ret; + +probe_disable_device: + pci_disable_device(pdev); +probe_error: + return ret; +} + +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), + .suspend = pch_uart_pci_suspend, + .resume = pch_uart_pci_resume, +}; + +static int __init pch_uart_module_init(void) +{ + int ret; + + /* register as UART driver */ + ret = uart_register_driver(&pch_uart_driver); + if (ret < 0) + return ret; + + /* register as PCI driver */ + ret = pci_register_driver(&pch_uart_pci_driver); + if (ret < 0) + uart_unregister_driver(&pch_uart_driver); + + return ret; +} +module_init(pch_uart_module_init); + +static void __exit pch_uart_module_exit(void) +{ + pci_unregister_driver(&pch_uart_pci_driver); + uart_unregister_driver(&pch_uart_driver); +} +module_exit(pch_uart_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver"); +module_param(default_baud, uint, S_IRUGO); diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c new file mode 100644 index 000000000000..5b9cde79e4ea --- /dev/null +++ b/drivers/tty/serial/pmac_zilog.c @@ -0,0 +1,2208 @@ +/* + * linux/drivers/serial/pmac_zilog.c + * + * Driver for PowerMac Z85c30 based ESCC cell found in the + * "macio" ASICs of various PowerMac models + * + * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org) + * + * Derived from drivers/macintosh/macserial.c by Paul Mackerras + * and drivers/serial/sunzilog.c by David S. Miller + * + * Hrm... actually, I ripped most of sunzilog (Thanks David !) and + * adapted special tweaks needed for us. I don't think it's worth + * merging back those though. The DMA code still has to get in + * and once done, I expect that driver to remain fairly stable in + * the long term, unless we change the driver model again... + * + * 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 + * + * 2004-08-06 Harald Welte + * - Enable BREAK interrupt + * - Add support for sysreq + * + * TODO: - Add DMA support + * - Defer port shutdown to a few seconds after close + * - maybe put something right into uap->clk_divisor + */ + +#undef DEBUG +#undef DEBUG_HARD +#undef USE_CTRL_O_SYSRQ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPC_PMAC +#include +#include +#include +#include +#include +#else +#include +#define of_machine_is_compatible(x) (0) +#endif + +#if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include + +#include "pmac_zilog.h" + +/* Not yet implemented */ +#undef HAS_DBDMA + +static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt )"; +MODULE_AUTHOR("Benjamin Herrenschmidt "); +MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports."); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_SERIAL_PMACZILOG_TTYS +#define PMACZILOG_MAJOR TTY_MAJOR +#define PMACZILOG_MINOR 64 +#define PMACZILOG_NAME "ttyS" +#else +#define PMACZILOG_MAJOR 204 +#define PMACZILOG_MINOR 192 +#define PMACZILOG_NAME "ttyPZ" +#endif + + +/* + * For the sake of early serial console, we can do a pre-probe + * (optional) of the ports at rather early boot time. + */ +static struct uart_pmac_port pmz_ports[MAX_ZS_PORTS]; +static int pmz_ports_count; +static DEFINE_MUTEX(pmz_irq_mutex); + +static struct uart_driver pmz_uart_reg = { + .owner = THIS_MODULE, + .driver_name = PMACZILOG_NAME, + .dev_name = PMACZILOG_NAME, + .major = PMACZILOG_MAJOR, + .minor = PMACZILOG_MINOR, +}; + + +/* + * Load all registers to reprogram the port + * This function must only be called when the TX is not busy. The UART + * port lock must be held and local interrupts disabled. + */ +static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) +{ + int i; + + if (ZS_IS_ASLEEP(uap)) + return; + + /* Let pending transmits finish. */ + for (i = 0; i < 1000; i++) { + unsigned char stat = read_zsreg(uap, R1); + if (stat & ALL_SNT) + break; + udelay(100); + } + + ZS_CLEARERR(uap); + zssync(uap); + ZS_CLEARFIFO(uap); + zssync(uap); + ZS_CLEARERR(uap); + + /* Disable all interrupts. */ + write_zsreg(uap, R1, + regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); + + /* Set parity, sync config, stop bits, and clock divisor. */ + write_zsreg(uap, R4, regs[R4]); + + /* Set misc. TX/RX control bits. */ + write_zsreg(uap, R10, regs[R10]); + + /* Set TX/RX controls sans the enable bits. */ + write_zsreg(uap, R3, regs[R3] & ~RxENABLE); + write_zsreg(uap, R5, regs[R5] & ~TxENABLE); + + /* now set R7 "prime" on ESCC */ + write_zsreg(uap, R15, regs[R15] | EN85C30); + write_zsreg(uap, R7, regs[R7P]); + + /* make sure we use R7 "non-prime" on ESCC */ + write_zsreg(uap, R15, regs[R15] & ~EN85C30); + + /* Synchronous mode config. */ + write_zsreg(uap, R6, regs[R6]); + write_zsreg(uap, R7, regs[R7]); + + /* Disable baud generator. */ + write_zsreg(uap, R14, regs[R14] & ~BRENAB); + + /* Clock mode control. */ + write_zsreg(uap, R11, regs[R11]); + + /* Lower and upper byte of baud rate generator divisor. */ + write_zsreg(uap, R12, regs[R12]); + write_zsreg(uap, R13, regs[R13]); + + /* Now rewrite R14, with BRENAB (if set). */ + write_zsreg(uap, R14, regs[R14]); + + /* Reset external status interrupts. */ + write_zsreg(uap, R0, RES_EXT_INT); + write_zsreg(uap, R0, RES_EXT_INT); + + /* Rewrite R3/R5, this time without enables masked. */ + write_zsreg(uap, R3, regs[R3]); + write_zsreg(uap, R5, regs[R5]); + + /* Rewrite R1, this time without IRQ enabled masked. */ + write_zsreg(uap, R1, regs[R1]); + + /* Enable interrupts */ + write_zsreg(uap, R9, regs[R9]); +} + +/* + * We do like sunzilog to avoid disrupting pending Tx + * Reprogram the Zilog channel HW registers with the copies found in the + * software state struct. If the transmitter is busy, we defer this update + * until the next TX complete interrupt. Else, we do it right now. + * + * The UART port lock must be held and local interrupts disabled. + */ +static void pmz_maybe_update_regs(struct uart_pmac_port *uap) +{ + if (!ZS_REGS_HELD(uap)) { + if (ZS_TX_ACTIVE(uap)) { + uap->flags |= PMACZILOG_FLAG_REGS_HELD; + } else { + pmz_debug("pmz: maybe_update_regs: updating\n"); + pmz_load_zsregs(uap, uap->curregs); + } + } +} + +static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) +{ + struct tty_struct *tty = NULL; + unsigned char ch, r1, drop, error, flag; + int loops = 0; + + /* The interrupt can be enabled when the port isn't open, typically + * that happens when using one port is open and the other closed (stale + * interrupt) or when one port is used as a console. + */ + if (!ZS_IS_OPEN(uap)) { + pmz_debug("pmz: draining input\n"); + /* Port is closed, drain input data */ + for (;;) { + if ((++loops) > 1000) + goto flood; + (void)read_zsreg(uap, R1); + write_zsreg(uap, R0, ERR_RES); + (void)read_zsdata(uap); + ch = read_zsreg(uap, R0); + if (!(ch & Rx_CH_AV)) + break; + } + return NULL; + } + + /* Sanity check, make sure the old bug is no longer happening */ + if (uap->port.state == NULL || uap->port.state->port.tty == NULL) { + WARN_ON(1); + (void)read_zsdata(uap); + return NULL; + } + tty = uap->port.state->port.tty; + + while (1) { + error = 0; + drop = 0; + + r1 = read_zsreg(uap, R1); + ch = read_zsdata(uap); + + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + write_zsreg(uap, R0, ERR_RES); + zssync(uap); + } + + ch &= uap->parity_mask; + if (ch == 0 && uap->flags & PMACZILOG_FLAG_BREAK) { + uap->flags &= ~PMACZILOG_FLAG_BREAK; + } + +#if defined(CONFIG_MAGIC_SYSRQ) && defined(CONFIG_SERIAL_CORE_CONSOLE) +#ifdef USE_CTRL_O_SYSRQ + /* Handle the SysRq ^O Hack */ + if (ch == '\x0f') { + uap->port.sysrq = jiffies + HZ*5; + goto next_char; + } +#endif /* USE_CTRL_O_SYSRQ */ + if (uap->port.sysrq) { + int swallow; + spin_unlock(&uap->port.lock); + swallow = uart_handle_sysrq_char(&uap->port, ch); + spin_lock(&uap->port.lock); + if (swallow) + goto next_char; + } +#endif /* CONFIG_MAGIC_SYSRQ && CONFIG_SERIAL_CORE_CONSOLE */ + + /* A real serial line, record the character and status. */ + if (drop) + goto next_char; + + flag = TTY_NORMAL; + uap->port.icount.rx++; + + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) { + error = 1; + if (r1 & BRK_ABRT) { + pmz_debug("pmz: got break !\n"); + r1 &= ~(PAR_ERR | CRC_ERR); + uap->port.icount.brk++; + if (uart_handle_break(&uap->port)) + goto next_char; + } + else if (r1 & PAR_ERR) + uap->port.icount.parity++; + else if (r1 & CRC_ERR) + uap->port.icount.frame++; + if (r1 & Rx_OVR) + uap->port.icount.overrun++; + r1 &= uap->port.read_status_mask; + if (r1 & BRK_ABRT) + flag = TTY_BREAK; + else if (r1 & PAR_ERR) + flag = TTY_PARITY; + else if (r1 & CRC_ERR) + flag = TTY_FRAME; + } + + if (uap->port.ignore_status_mask == 0xff || + (r1 & uap->port.ignore_status_mask) == 0) { + tty_insert_flip_char(tty, ch, flag); + } + if (r1 & Rx_OVR) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + next_char: + /* We can get stuck in an infinite loop getting char 0 when the + * line is in a wrong HW state, we break that here. + * When that happens, I disable the receive side of the driver. + * Note that what I've been experiencing is a real irq loop where + * I'm getting flooded regardless of the actual port speed. + * Something stange is going on with the HW + */ + if ((++loops) > 1000) + goto flood; + ch = read_zsreg(uap, R0); + if (!(ch & Rx_CH_AV)) + break; + } + + return tty; + flood: + uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); + write_zsreg(uap, R1, uap->curregs[R1]); + zssync(uap); + pmz_error("pmz: rx irq flood !\n"); + return tty; +} + +static void pmz_status_handle(struct uart_pmac_port *uap) +{ + unsigned char status; + + status = read_zsreg(uap, R0); + write_zsreg(uap, R0, RES_EXT_INT); + zssync(uap); + + if (ZS_IS_OPEN(uap) && ZS_WANTS_MODEM_STATUS(uap)) { + if (status & SYNC_HUNT) + uap->port.icount.dsr++; + + /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. + * But it does not tell us which bit has changed, we have to keep + * track of this ourselves. + * The CTS input is inverted for some reason. -- paulus + */ + if ((status ^ uap->prev_status) & DCD) + uart_handle_dcd_change(&uap->port, + (status & DCD)); + if ((status ^ uap->prev_status) & CTS) + uart_handle_cts_change(&uap->port, + !(status & CTS)); + + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); + } + + if (status & BRK_ABRT) + uap->flags |= PMACZILOG_FLAG_BREAK; + + uap->prev_status = status; +} + +static void pmz_transmit_chars(struct uart_pmac_port *uap) +{ + struct circ_buf *xmit; + + if (ZS_IS_ASLEEP(uap)) + return; + if (ZS_IS_CONS(uap)) { + unsigned char status = read_zsreg(uap, R0); + + /* TX still busy? Just wait for the next TX done interrupt. + * + * It can occur because of how we do serial console writes. It would + * be nice to transmit console writes just like we normally would for + * a TTY line. (ie. buffered and TX interrupt driven). That is not + * easy because console writes cannot sleep. One solution might be + * to poll on enough port->xmit space becomming free. -DaveM + */ + if (!(status & Tx_BUF_EMP)) + return; + } + + uap->flags &= ~PMACZILOG_FLAG_TX_ACTIVE; + + if (ZS_REGS_HELD(uap)) { + pmz_load_zsregs(uap, uap->curregs); + uap->flags &= ~PMACZILOG_FLAG_REGS_HELD; + } + + if (ZS_TX_STOPPED(uap)) { + uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED; + goto ack_tx_int; + } + + /* Under some circumstances, we see interrupts reported for + * a closed channel. The interrupt mask in R1 is clear, but + * R3 still signals the interrupts and we see them when taking + * an interrupt for the other channel (this could be a qemu + * bug but since the ESCC doc doesn't specify precsiely whether + * R3 interrup status bits are masked by R1 interrupt enable + * bits, better safe than sorry). --BenH. + */ + if (!ZS_IS_OPEN(uap)) + goto ack_tx_int; + + if (uap->port.x_char) { + uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; + write_zsdata(uap, uap->port.x_char); + zssync(uap); + uap->port.icount.tx++; + uap->port.x_char = 0; + return; + } + + if (uap->port.state == NULL) + goto ack_tx_int; + xmit = &uap->port.state->xmit; + if (uart_circ_empty(xmit)) { + uart_write_wakeup(&uap->port); + goto ack_tx_int; + } + if (uart_tx_stopped(&uap->port)) + goto ack_tx_int; + + uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; + write_zsdata(uap, xmit->buf[xmit->tail]); + zssync(uap); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + uap->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + + return; + +ack_tx_int: + write_zsreg(uap, R0, RES_Tx_P); + zssync(uap); +} + +/* Hrm... we register that twice, fixme later.... */ +static irqreturn_t pmz_interrupt(int irq, void *dev_id) +{ + struct uart_pmac_port *uap = dev_id; + struct uart_pmac_port *uap_a; + struct uart_pmac_port *uap_b; + int rc = IRQ_NONE; + struct tty_struct *tty; + u8 r3; + + uap_a = pmz_get_port_A(uap); + uap_b = uap_a->mate; + + spin_lock(&uap_a->port.lock); + r3 = read_zsreg(uap_a, R3); + +#ifdef DEBUG_HARD + pmz_debug("irq, r3: %x\n", r3); +#endif + /* Channel A */ + tty = NULL; + if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { + write_zsreg(uap_a, R0, RES_H_IUS); + zssync(uap_a); + if (r3 & CHAEXT) + pmz_status_handle(uap_a); + if (r3 & CHARxIP) + tty = pmz_receive_chars(uap_a); + if (r3 & CHATxIP) + pmz_transmit_chars(uap_a); + rc = IRQ_HANDLED; + } + spin_unlock(&uap_a->port.lock); + if (tty != NULL) + tty_flip_buffer_push(tty); + + if (uap_b->node == NULL) + goto out; + + spin_lock(&uap_b->port.lock); + tty = NULL; + if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { + write_zsreg(uap_b, R0, RES_H_IUS); + zssync(uap_b); + if (r3 & CHBEXT) + pmz_status_handle(uap_b); + if (r3 & CHBRxIP) + tty = pmz_receive_chars(uap_b); + if (r3 & CHBTxIP) + pmz_transmit_chars(uap_b); + rc = IRQ_HANDLED; + } + spin_unlock(&uap_b->port.lock); + if (tty != NULL) + tty_flip_buffer_push(tty); + + out: +#ifdef DEBUG_HARD + pmz_debug("irq done.\n"); +#endif + return rc; +} + +/* + * Peek the status register, lock not held by caller + */ +static inline u8 pmz_peek_status(struct uart_pmac_port *uap) +{ + unsigned long flags; + u8 status; + + spin_lock_irqsave(&uap->port.lock, flags); + status = read_zsreg(uap, R0); + spin_unlock_irqrestore(&uap->port.lock, flags); + + return status; +} + +/* + * Check if transmitter is empty + * The port lock is not held. + */ +static unsigned int pmz_tx_empty(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned char status; + + if (ZS_IS_ASLEEP(uap) || uap->node == NULL) + return TIOCSER_TEMT; + + status = pmz_peek_status(to_pmz(port)); + if (status & Tx_BUF_EMP) + return TIOCSER_TEMT; + return 0; +} + +/* + * Set Modem Control (RTS & DTR) bits + * The port lock is held and interrupts are disabled. + * Note: Shall we really filter out RTS on external ports or + * should that be dealt at higher level only ? + */ +static void pmz_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned char set_bits, clear_bits; + + /* Do nothing for irda for now... */ + if (ZS_IS_IRDA(uap)) + return; + /* We get called during boot with a port not up yet */ + if (ZS_IS_ASLEEP(uap) || + !(ZS_IS_OPEN(uap) || ZS_IS_CONS(uap))) + return; + + set_bits = clear_bits = 0; + + if (ZS_IS_INTMODEM(uap)) { + if (mctrl & TIOCM_RTS) + set_bits |= RTS; + else + clear_bits |= RTS; + } + if (mctrl & TIOCM_DTR) + set_bits |= DTR; + else + clear_bits |= DTR; + + /* NOTE: Not subject to 'transmitter active' rule. */ + uap->curregs[R5] |= set_bits; + uap->curregs[R5] &= ~clear_bits; + if (ZS_IS_ASLEEP(uap)) + return; + write_zsreg(uap, R5, uap->curregs[R5]); + pmz_debug("pmz_set_mctrl: set bits: %x, clear bits: %x -> %x\n", + set_bits, clear_bits, uap->curregs[R5]); + zssync(uap); +} + +/* + * Get Modem Control bits (only the input ones, the core will + * or that with a cached value of the control ones) + * The port lock is held and interrupts are disabled. + */ +static unsigned int pmz_get_mctrl(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned char status; + unsigned int ret; + + if (ZS_IS_ASLEEP(uap) || uap->node == NULL) + return 0; + + status = read_zsreg(uap, R0); + + ret = 0; + if (status & DCD) + ret |= TIOCM_CAR; + if (status & SYNC_HUNT) + ret |= TIOCM_DSR; + if (!(status & CTS)) + ret |= TIOCM_CTS; + + return ret; +} + +/* + * Stop TX side. Dealt like sunzilog at next Tx interrupt, + * though for DMA, we will have to do a bit more. + * The port lock is held and interrupts are disabled. + */ +static void pmz_stop_tx(struct uart_port *port) +{ + to_pmz(port)->flags |= PMACZILOG_FLAG_TX_STOPPED; +} + +/* + * Kick the Tx side. + * The port lock is held and interrupts are disabled. + */ +static void pmz_start_tx(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned char status; + + pmz_debug("pmz: start_tx()\n"); + + uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; + uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED; + + if (ZS_IS_ASLEEP(uap) || uap->node == NULL) + return; + + status = read_zsreg(uap, R0); + + /* TX busy? Just wait for the TX done interrupt. */ + if (!(status & Tx_BUF_EMP)) + return; + + /* Send the first character to jump-start the TX done + * IRQ sending engine. + */ + if (port->x_char) { + write_zsdata(uap, port->x_char); + zssync(uap); + port->icount.tx++; + port->x_char = 0; + } else { + struct circ_buf *xmit = &port->state->xmit; + + write_zsdata(uap, xmit->buf[xmit->tail]); + zssync(uap); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + } + pmz_debug("pmz: start_tx() done.\n"); +} + +/* + * Stop Rx side, basically disable emitting of + * Rx interrupts on the port. We don't disable the rx + * side of the chip proper though + * The port lock is held. + */ +static void pmz_stop_rx(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + + if (ZS_IS_ASLEEP(uap) || uap->node == NULL) + return; + + pmz_debug("pmz: stop_rx()()\n"); + + /* Disable all RX interrupts. */ + uap->curregs[R1] &= ~RxINT_MASK; + pmz_maybe_update_regs(uap); + + pmz_debug("pmz: stop_rx() done.\n"); +} + +/* + * Enable modem status change interrupts + * The port lock is held. + */ +static void pmz_enable_ms(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned char new_reg; + + if (ZS_IS_IRDA(uap) || uap->node == NULL) + return; + new_reg = uap->curregs[R15] | (DCDIE | SYNCIE | CTSIE); + if (new_reg != uap->curregs[R15]) { + uap->curregs[R15] = new_reg; + + if (ZS_IS_ASLEEP(uap)) + return; + /* NOTE: Not subject to 'transmitter active' rule. */ + write_zsreg(uap, R15, uap->curregs[R15]); + } +} + +/* + * Control break state emission + * The port lock is not held. + */ +static void pmz_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned char set_bits, clear_bits, new_reg; + unsigned long flags; + + if (uap->node == NULL) + return; + set_bits = clear_bits = 0; + + if (break_state) + set_bits |= SND_BRK; + else + clear_bits |= SND_BRK; + + spin_lock_irqsave(&port->lock, flags); + + new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits; + if (new_reg != uap->curregs[R5]) { + uap->curregs[R5] = new_reg; + + /* NOTE: Not subject to 'transmitter active' rule. */ + if (ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); + return; + } + write_zsreg(uap, R5, uap->curregs[R5]); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +#ifdef CONFIG_PPC_PMAC + +/* + * Turn power on or off to the SCC and associated stuff + * (port drivers, modem, IR port, etc.) + * Returns the number of milliseconds we should wait before + * trying to use the port. + */ +static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) +{ + int delay = 0; + int rc; + + if (state) { + rc = pmac_call_feature( + PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 1); + pmz_debug("port power on result: %d\n", rc); + if (ZS_IS_INTMODEM(uap)) { + rc = pmac_call_feature( + PMAC_FTR_MODEM_ENABLE, uap->node, 0, 1); + delay = 2500; /* wait for 2.5s before using */ + pmz_debug("modem power result: %d\n", rc); + } + } else { + /* TODO: Make that depend on a timer, don't power down + * immediately + */ + if (ZS_IS_INTMODEM(uap)) { + rc = pmac_call_feature( + PMAC_FTR_MODEM_ENABLE, uap->node, 0, 0); + pmz_debug("port power off result: %d\n", rc); + } + pmac_call_feature(PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 0); + } + return delay; +} + +#else + +static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) +{ + return 0; +} + +#endif /* !CONFIG_PPC_PMAC */ + +/* + * FixZeroBug....Works around a bug in the SCC receving channel. + * Inspired from Darwin code, 15 Sept. 2000 -DanM + * + * The following sequence prevents a problem that is seen with O'Hare ASICs + * (most versions -- also with some Heathrow and Hydra ASICs) where a zero + * at the input to the receiver becomes 'stuck' and locks up the receiver. + * This problem can occur as a result of a zero bit at the receiver input + * coincident with any of the following events: + * + * The SCC is initialized (hardware or software). + * A framing error is detected. + * The clocking option changes from synchronous or X1 asynchronous + * clocking to X16, X32, or X64 asynchronous clocking. + * The decoding mode is changed among NRZ, NRZI, FM0, or FM1. + * + * This workaround attempts to recover from the lockup condition by placing + * the SCC in synchronous loopback mode with a fast clock before programming + * any of the asynchronous modes. + */ +static void pmz_fix_zero_bug_scc(struct uart_pmac_port *uap) +{ + write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB); + zssync(uap); + udelay(10); + write_zsreg(uap, 9, (ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB) | NV); + zssync(uap); + + write_zsreg(uap, 4, X1CLK | MONSYNC); + write_zsreg(uap, 3, Rx8); + write_zsreg(uap, 5, Tx8 | RTS); + write_zsreg(uap, 9, NV); /* Didn't we already do this? */ + write_zsreg(uap, 11, RCBR | TCBR); + write_zsreg(uap, 12, 0); + write_zsreg(uap, 13, 0); + write_zsreg(uap, 14, (LOOPBAK | BRSRC)); + write_zsreg(uap, 14, (LOOPBAK | BRSRC | BRENAB)); + write_zsreg(uap, 3, Rx8 | RxENABLE); + write_zsreg(uap, 0, RES_EXT_INT); + write_zsreg(uap, 0, RES_EXT_INT); + write_zsreg(uap, 0, RES_EXT_INT); /* to kill some time */ + + /* The channel should be OK now, but it is probably receiving + * loopback garbage. + * Switch to asynchronous mode, disable the receiver, + * and discard everything in the receive buffer. + */ + write_zsreg(uap, 9, NV); + write_zsreg(uap, 4, X16CLK | SB_MASK); + write_zsreg(uap, 3, Rx8); + + while (read_zsreg(uap, 0) & Rx_CH_AV) { + (void)read_zsreg(uap, 8); + write_zsreg(uap, 0, RES_EXT_INT); + write_zsreg(uap, 0, ERR_RES); + } +} + +/* + * Real startup routine, powers up the hardware and sets up + * the SCC. Returns a delay in ms where you need to wait before + * actually using the port, this is typically the internal modem + * powerup delay. This routine expect the lock to be taken. + */ +static int __pmz_startup(struct uart_pmac_port *uap) +{ + int pwr_delay = 0; + + memset(&uap->curregs, 0, sizeof(uap->curregs)); + + /* Power up the SCC & underlying hardware (modem/irda) */ + pwr_delay = pmz_set_scc_power(uap, 1); + + /* Nice buggy HW ... */ + pmz_fix_zero_bug_scc(uap); + + /* Reset the channel */ + uap->curregs[R9] = 0; + write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB); + zssync(uap); + udelay(10); + write_zsreg(uap, 9, 0); + zssync(uap); + + /* Clear the interrupt registers */ + write_zsreg(uap, R1, 0); + write_zsreg(uap, R0, ERR_RES); + write_zsreg(uap, R0, ERR_RES); + write_zsreg(uap, R0, RES_H_IUS); + write_zsreg(uap, R0, RES_H_IUS); + + /* Setup some valid baud rate */ + uap->curregs[R4] = X16CLK | SB1; + uap->curregs[R3] = Rx8; + uap->curregs[R5] = Tx8 | RTS; + if (!ZS_IS_IRDA(uap)) + uap->curregs[R5] |= DTR; + uap->curregs[R12] = 0; + uap->curregs[R13] = 0; + uap->curregs[R14] = BRENAB; + + /* Clear handshaking, enable BREAK interrupts */ + uap->curregs[R15] = BRKIE; + + /* Master interrupt enable */ + uap->curregs[R9] |= NV | MIE; + + pmz_load_zsregs(uap, uap->curregs); + + /* Enable receiver and transmitter. */ + write_zsreg(uap, R3, uap->curregs[R3] |= RxENABLE); + write_zsreg(uap, R5, uap->curregs[R5] |= TxENABLE); + + /* Remember status for DCD/CTS changes */ + uap->prev_status = read_zsreg(uap, R0); + + return pwr_delay; +} + +static void pmz_irda_reset(struct uart_pmac_port *uap) +{ + uap->curregs[R5] |= DTR; + write_zsreg(uap, R5, uap->curregs[R5]); + zssync(uap); + mdelay(110); + uap->curregs[R5] &= ~DTR; + write_zsreg(uap, R5, uap->curregs[R5]); + zssync(uap); + mdelay(10); +} + +/* + * This is the "normal" startup routine, using the above one + * wrapped with the lock and doing a schedule delay + */ +static int pmz_startup(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned long flags; + int pwr_delay = 0; + + pmz_debug("pmz: startup()\n"); + + if (ZS_IS_ASLEEP(uap)) + return -EAGAIN; + if (uap->node == NULL) + return -ENODEV; + + mutex_lock(&pmz_irq_mutex); + + uap->flags |= PMACZILOG_FLAG_IS_OPEN; + + /* A console is never powered down. Else, power up and + * initialize the chip + */ + if (!ZS_IS_CONS(uap)) { + spin_lock_irqsave(&port->lock, flags); + pwr_delay = __pmz_startup(uap); + spin_unlock_irqrestore(&port->lock, flags); + } + + pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; + if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, + "SCC", uap)) { + pmz_error("Unable to register zs interrupt handler.\n"); + pmz_set_scc_power(uap, 0); + mutex_unlock(&pmz_irq_mutex); + return -ENXIO; + } + + mutex_unlock(&pmz_irq_mutex); + + /* Right now, we deal with delay by blocking here, I'll be + * smarter later on + */ + if (pwr_delay != 0) { + pmz_debug("pmz: delaying %d ms\n", pwr_delay); + msleep(pwr_delay); + } + + /* IrDA reset is done now */ + if (ZS_IS_IRDA(uap)) + pmz_irda_reset(uap); + + /* Enable interrupts emission from the chip */ + spin_lock_irqsave(&port->lock, flags); + uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; + if (!ZS_IS_EXTCLK(uap)) + uap->curregs[R1] |= EXT_INT_ENAB; + write_zsreg(uap, R1, uap->curregs[R1]); + spin_unlock_irqrestore(&port->lock, flags); + + pmz_debug("pmz: startup() done.\n"); + + return 0; +} + +static void pmz_shutdown(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned long flags; + + pmz_debug("pmz: shutdown()\n"); + + if (uap->node == NULL) + return; + + mutex_lock(&pmz_irq_mutex); + + /* Release interrupt handler */ + free_irq(uap->port.irq, uap); + + spin_lock_irqsave(&port->lock, flags); + + uap->flags &= ~PMACZILOG_FLAG_IS_OPEN; + + if (!ZS_IS_OPEN(uap->mate)) + pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON; + + /* Disable interrupts */ + if (!ZS_IS_ASLEEP(uap)) { + uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); + write_zsreg(uap, R1, uap->curregs[R1]); + zssync(uap); + } + + if (ZS_IS_CONS(uap) || ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); + mutex_unlock(&pmz_irq_mutex); + return; + } + + /* Disable receiver and transmitter. */ + uap->curregs[R3] &= ~RxENABLE; + uap->curregs[R5] &= ~TxENABLE; + + /* Disable all interrupts and BRK assertion. */ + uap->curregs[R5] &= ~SND_BRK; + pmz_maybe_update_regs(uap); + + /* Shut the chip down */ + pmz_set_scc_power(uap, 0); + + spin_unlock_irqrestore(&port->lock, flags); + + mutex_unlock(&pmz_irq_mutex); + + pmz_debug("pmz: shutdown() done.\n"); +} + +/* Shared by TTY driver and serial console setup. The port lock is held + * and local interrupts are disabled. + */ +static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag, + unsigned int iflag, unsigned long baud) +{ + int brg; + + /* Switch to external clocking for IrDA high clock rates. That + * code could be re-used for Midi interfaces with different + * multipliers + */ + if (baud >= 115200 && ZS_IS_IRDA(uap)) { + uap->curregs[R4] = X1CLK; + uap->curregs[R11] = RCTRxCP | TCTRxCP; + uap->curregs[R14] = 0; /* BRG off */ + uap->curregs[R12] = 0; + uap->curregs[R13] = 0; + uap->flags |= PMACZILOG_FLAG_IS_EXTCLK; + } else { + switch (baud) { + case ZS_CLOCK/16: /* 230400 */ + uap->curregs[R4] = X16CLK; + uap->curregs[R11] = 0; + uap->curregs[R14] = 0; + break; + case ZS_CLOCK/32: /* 115200 */ + uap->curregs[R4] = X32CLK; + uap->curregs[R11] = 0; + uap->curregs[R14] = 0; + break; + default: + uap->curregs[R4] = X16CLK; + uap->curregs[R11] = TCBR | RCBR; + brg = BPS_TO_BRG(baud, ZS_CLOCK / 16); + uap->curregs[R12] = (brg & 255); + uap->curregs[R13] = ((brg >> 8) & 255); + uap->curregs[R14] = BRENAB; + } + uap->flags &= ~PMACZILOG_FLAG_IS_EXTCLK; + } + + /* Character size, stop bits, and parity. */ + uap->curregs[3] &= ~RxN_MASK; + uap->curregs[5] &= ~TxN_MASK; + + switch (cflag & CSIZE) { + case CS5: + uap->curregs[3] |= Rx5; + uap->curregs[5] |= Tx5; + uap->parity_mask = 0x1f; + break; + case CS6: + uap->curregs[3] |= Rx6; + uap->curregs[5] |= Tx6; + uap->parity_mask = 0x3f; + break; + case CS7: + uap->curregs[3] |= Rx7; + uap->curregs[5] |= Tx7; + uap->parity_mask = 0x7f; + break; + case CS8: + default: + uap->curregs[3] |= Rx8; + uap->curregs[5] |= Tx8; + uap->parity_mask = 0xff; + break; + }; + uap->curregs[4] &= ~(SB_MASK); + if (cflag & CSTOPB) + uap->curregs[4] |= SB2; + else + uap->curregs[4] |= SB1; + if (cflag & PARENB) + uap->curregs[4] |= PAR_ENAB; + else + uap->curregs[4] &= ~PAR_ENAB; + if (!(cflag & PARODD)) + uap->curregs[4] |= PAR_EVEN; + else + uap->curregs[4] &= ~PAR_EVEN; + + uap->port.read_status_mask = Rx_OVR; + if (iflag & INPCK) + uap->port.read_status_mask |= CRC_ERR | PAR_ERR; + if (iflag & (BRKINT | PARMRK)) + uap->port.read_status_mask |= BRK_ABRT; + + uap->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + uap->port.ignore_status_mask |= CRC_ERR | PAR_ERR; + if (iflag & IGNBRK) { + uap->port.ignore_status_mask |= BRK_ABRT; + if (iflag & IGNPAR) + uap->port.ignore_status_mask |= Rx_OVR; + } + + if ((cflag & CREAD) == 0) + uap->port.ignore_status_mask = 0xff; +} + + +/* + * Set the irda codec on the imac to the specified baud rate. + */ +static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) +{ + u8 cmdbyte; + int t, version; + + switch (*baud) { + /* SIR modes */ + case 2400: + cmdbyte = 0x53; + break; + case 4800: + cmdbyte = 0x52; + break; + case 9600: + cmdbyte = 0x51; + break; + case 19200: + cmdbyte = 0x50; + break; + case 38400: + cmdbyte = 0x4f; + break; + case 57600: + cmdbyte = 0x4e; + break; + case 115200: + cmdbyte = 0x4d; + break; + /* The FIR modes aren't really supported at this point, how + * do we select the speed ? via the FCR on KeyLargo ? + */ + case 1152000: + cmdbyte = 0; + break; + case 4000000: + cmdbyte = 0; + break; + default: /* 9600 */ + cmdbyte = 0x51; + *baud = 9600; + break; + } + + /* Wait for transmitter to drain */ + t = 10000; + while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0 + || (read_zsreg(uap, R1) & ALL_SNT) == 0) { + if (--t <= 0) { + pmz_error("transmitter didn't drain\n"); + return; + } + udelay(10); + } + + /* Drain the receiver too */ + t = 100; + (void)read_zsdata(uap); + (void)read_zsdata(uap); + (void)read_zsdata(uap); + mdelay(10); + while (read_zsreg(uap, R0) & Rx_CH_AV) { + read_zsdata(uap); + mdelay(10); + if (--t <= 0) { + pmz_error("receiver didn't drain\n"); + return; + } + } + + /* Switch to command mode */ + uap->curregs[R5] |= DTR; + write_zsreg(uap, R5, uap->curregs[R5]); + zssync(uap); + mdelay(1); + + /* Switch SCC to 19200 */ + pmz_convert_to_zs(uap, CS8, 0, 19200); + pmz_load_zsregs(uap, uap->curregs); + mdelay(1); + + /* Write get_version command byte */ + write_zsdata(uap, 1); + t = 5000; + while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { + if (--t <= 0) { + pmz_error("irda_setup timed out on get_version byte\n"); + goto out; + } + udelay(10); + } + version = read_zsdata(uap); + + if (version < 4) { + pmz_info("IrDA: dongle version %d not supported\n", version); + goto out; + } + + /* Send speed mode */ + write_zsdata(uap, cmdbyte); + t = 5000; + while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { + if (--t <= 0) { + pmz_error("irda_setup timed out on speed mode byte\n"); + goto out; + } + udelay(10); + } + t = read_zsdata(uap); + if (t != cmdbyte) + pmz_error("irda_setup speed mode byte = %x (%x)\n", t, cmdbyte); + + pmz_info("IrDA setup for %ld bps, dongle version: %d\n", + *baud, version); + + (void)read_zsdata(uap); + (void)read_zsdata(uap); + (void)read_zsdata(uap); + + out: + /* Switch back to data mode */ + uap->curregs[R5] &= ~DTR; + write_zsreg(uap, R5, uap->curregs[R5]); + zssync(uap); + + (void)read_zsdata(uap); + (void)read_zsdata(uap); + (void)read_zsdata(uap); +} + + +static void __pmz_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned long baud; + + pmz_debug("pmz: set_termios()\n"); + + if (ZS_IS_ASLEEP(uap)) + return; + + memcpy(&uap->termios_cache, termios, sizeof(struct ktermios)); + + /* XXX Check which revs of machines actually allow 1 and 4Mb speeds + * on the IR dongle. Note that the IRTTY driver currently doesn't know + * about the FIR mode and high speed modes. So these are unused. For + * implementing proper support for these, we should probably add some + * DMA as well, at least on the Rx side, which isn't a simple thing + * at this point. + */ + if (ZS_IS_IRDA(uap)) { + /* Calc baud rate */ + baud = uart_get_baud_rate(port, termios, old, 1200, 4000000); + pmz_debug("pmz: switch IRDA to %ld bauds\n", baud); + /* Cet the irda codec to the right rate */ + pmz_irda_setup(uap, &baud); + /* Set final baud rate */ + pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud); + pmz_load_zsregs(uap, uap->curregs); + zssync(uap); + } else { + baud = uart_get_baud_rate(port, termios, old, 1200, 230400); + pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud); + /* Make sure modem status interrupts are correctly configured */ + if (UART_ENABLE_MS(&uap->port, termios->c_cflag)) { + uap->curregs[R15] |= DCDIE | SYNCIE | CTSIE; + uap->flags |= PMACZILOG_FLAG_MODEM_STATUS; + } else { + uap->curregs[R15] &= ~(DCDIE | SYNCIE | CTSIE); + uap->flags &= ~PMACZILOG_FLAG_MODEM_STATUS; + } + + /* Load registers to the chip */ + pmz_maybe_update_regs(uap); + } + uart_update_timeout(port, termios->c_cflag, baud); + + pmz_debug("pmz: set_termios() done.\n"); +} + +/* The port lock is not held. */ +static void pmz_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_pmac_port *uap = to_pmz(port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable IRQs on the port */ + uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); + write_zsreg(uap, R1, uap->curregs[R1]); + + /* Setup new port configuration */ + __pmz_set_termios(port, termios, old); + + /* Re-enable IRQs on the port */ + if (ZS_IS_OPEN(uap)) { + uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; + if (!ZS_IS_EXTCLK(uap)) + uap->curregs[R1] |= EXT_INT_ENAB; + write_zsreg(uap, R1, uap->curregs[R1]); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *pmz_type(struct uart_port *port) +{ + struct uart_pmac_port *uap = to_pmz(port); + + if (ZS_IS_IRDA(uap)) + return "Z85c30 ESCC - Infrared port"; + else if (ZS_IS_INTMODEM(uap)) + return "Z85c30 ESCC - Internal modem"; + return "Z85c30 ESCC - Serial port"; +} + +/* We do not request/release mappings of the registers here, this + * happens at early serial probe time. + */ +static void pmz_release_port(struct uart_port *port) +{ +} + +static int pmz_request_port(struct uart_port *port) +{ + return 0; +} + +/* These do not need to do anything interesting either. */ +static void pmz_config_port(struct uart_port *port, int flags) +{ +} + +/* We do not support letting the user mess with the divisor, IRQ, etc. */ +static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +#ifdef CONFIG_CONSOLE_POLL + +static int pmz_poll_get_char(struct uart_port *port) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) + udelay(5); + return read_zsdata(uap); +} + +static void pmz_poll_put_char(struct uart_port *port, unsigned char c) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + /* Wait for the transmit buffer to empty. */ + while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) + udelay(5); + write_zsdata(uap, c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static struct uart_ops pmz_pops = { + .tx_empty = pmz_tx_empty, + .set_mctrl = pmz_set_mctrl, + .get_mctrl = pmz_get_mctrl, + .stop_tx = pmz_stop_tx, + .start_tx = pmz_start_tx, + .stop_rx = pmz_stop_rx, + .enable_ms = pmz_enable_ms, + .break_ctl = pmz_break_ctl, + .startup = pmz_startup, + .shutdown = pmz_shutdown, + .set_termios = pmz_set_termios, + .type = pmz_type, + .release_port = pmz_release_port, + .request_port = pmz_request_port, + .config_port = pmz_config_port, + .verify_port = pmz_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = pmz_poll_get_char, + .poll_put_char = pmz_poll_put_char, +#endif +}; + +#ifdef CONFIG_PPC_PMAC + +/* + * Setup one port structure after probing, HW is down at this point, + * Unlike sunzilog, we don't need to pre-init the spinlock as we don't + * register our console before uart_add_one_port() is called + */ +static int __init pmz_init_port(struct uart_pmac_port *uap) +{ + struct device_node *np = uap->node; + const char *conn; + const struct slot_names_prop { + int count; + char name[1]; + } *slots; + int len; + struct resource r_ports, r_rxdma, r_txdma; + + /* + * Request & map chip registers + */ + if (of_address_to_resource(np, 0, &r_ports)) + return -ENODEV; + uap->port.mapbase = r_ports.start; + uap->port.membase = ioremap(uap->port.mapbase, 0x1000); + + uap->control_reg = uap->port.membase; + uap->data_reg = uap->control_reg + 0x10; + + /* + * Request & map DBDMA registers + */ +#ifdef HAS_DBDMA + if (of_address_to_resource(np, 1, &r_txdma) == 0 && + of_address_to_resource(np, 2, &r_rxdma) == 0) + uap->flags |= PMACZILOG_FLAG_HAS_DMA; +#else + memset(&r_txdma, 0, sizeof(struct resource)); + memset(&r_rxdma, 0, sizeof(struct resource)); +#endif + if (ZS_HAS_DMA(uap)) { + uap->tx_dma_regs = ioremap(r_txdma.start, 0x100); + if (uap->tx_dma_regs == NULL) { + uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; + goto no_dma; + } + uap->rx_dma_regs = ioremap(r_rxdma.start, 0x100); + if (uap->rx_dma_regs == NULL) { + iounmap(uap->tx_dma_regs); + uap->tx_dma_regs = NULL; + uap->flags &= ~PMACZILOG_FLAG_HAS_DMA; + goto no_dma; + } + uap->tx_dma_irq = irq_of_parse_and_map(np, 1); + uap->rx_dma_irq = irq_of_parse_and_map(np, 2); + } +no_dma: + + /* + * Detect port type + */ + if (of_device_is_compatible(np, "cobalt")) + uap->flags |= PMACZILOG_FLAG_IS_INTMODEM; + conn = of_get_property(np, "AAPL,connector", &len); + if (conn && (strcmp(conn, "infrared") == 0)) + uap->flags |= PMACZILOG_FLAG_IS_IRDA; + uap->port_type = PMAC_SCC_ASYNC; + /* 1999 Powerbook G3 has slot-names property instead */ + slots = of_get_property(np, "slot-names", &len); + if (slots && slots->count > 0) { + if (strcmp(slots->name, "IrDA") == 0) + uap->flags |= PMACZILOG_FLAG_IS_IRDA; + else if (strcmp(slots->name, "Modem") == 0) + uap->flags |= PMACZILOG_FLAG_IS_INTMODEM; + } + if (ZS_IS_IRDA(uap)) + uap->port_type = PMAC_SCC_IRDA; + if (ZS_IS_INTMODEM(uap)) { + struct device_node* i2c_modem = + of_find_node_by_name(NULL, "i2c-modem"); + if (i2c_modem) { + const char* mid = + of_get_property(i2c_modem, "modem-id", NULL); + if (mid) switch(*mid) { + case 0x04 : + case 0x05 : + case 0x07 : + case 0x08 : + case 0x0b : + case 0x0c : + uap->port_type = PMAC_SCC_I2S1; + } + printk(KERN_INFO "pmac_zilog: i2c-modem detected, id: %d\n", + mid ? (*mid) : 0); + of_node_put(i2c_modem); + } else { + printk(KERN_INFO "pmac_zilog: serial modem detected\n"); + } + } + + /* + * Init remaining bits of "port" structure + */ + uap->port.iotype = UPIO_MEM; + uap->port.irq = irq_of_parse_and_map(np, 0); + uap->port.uartclk = ZS_CLOCK; + uap->port.fifosize = 1; + uap->port.ops = &pmz_pops; + uap->port.type = PORT_PMAC_ZILOG; + uap->port.flags = 0; + + /* + * Fixup for the port on Gatwick for which the device-tree has + * missing interrupts. Normally, the macio_dev would contain + * fixed up interrupt info, but we use the device-tree directly + * here due to early probing so we need the fixup too. + */ + if (uap->port.irq == NO_IRQ && + np->parent && np->parent->parent && + of_device_is_compatible(np->parent->parent, "gatwick")) { + /* IRQs on gatwick are offset by 64 */ + uap->port.irq = irq_create_mapping(NULL, 64 + 15); + uap->tx_dma_irq = irq_create_mapping(NULL, 64 + 4); + uap->rx_dma_irq = irq_create_mapping(NULL, 64 + 5); + } + + /* Setup some valid baud rate information in the register + * shadows so we don't write crap there before baud rate is + * first initialized. + */ + pmz_convert_to_zs(uap, CS8, 0, 9600); + + return 0; +} + +/* + * Get rid of a port on module removal + */ +static void pmz_dispose_port(struct uart_pmac_port *uap) +{ + struct device_node *np; + + np = uap->node; + iounmap(uap->rx_dma_regs); + iounmap(uap->tx_dma_regs); + iounmap(uap->control_reg); + uap->node = NULL; + of_node_put(np); + memset(uap, 0, sizeof(struct uart_pmac_port)); +} + +/* + * Called upon match with an escc node in the device-tree. + */ +static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) +{ + int i; + + /* Iterate the pmz_ports array to find a matching entry + */ + for (i = 0; i < MAX_ZS_PORTS; i++) + if (pmz_ports[i].node == mdev->ofdev.dev.of_node) { + struct uart_pmac_port *uap = &pmz_ports[i]; + + uap->dev = mdev; + dev_set_drvdata(&mdev->ofdev.dev, uap); + if (macio_request_resources(uap->dev, "pmac_zilog")) + printk(KERN_WARNING "%s: Failed to request resource" + ", port still active\n", + uap->node->name); + else + uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED; + return 0; + } + return -ENODEV; +} + +/* + * That one should not be called, macio isn't really a hotswap device, + * we don't expect one of those serial ports to go away... + */ +static int pmz_detach(struct macio_dev *mdev) +{ + struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); + + if (!uap) + return -ENODEV; + + if (uap->flags & PMACZILOG_FLAG_RSRC_REQUESTED) { + macio_release_resources(uap->dev); + uap->flags &= ~PMACZILOG_FLAG_RSRC_REQUESTED; + } + dev_set_drvdata(&mdev->ofdev.dev, NULL); + uap->dev = NULL; + + return 0; +} + + +static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) +{ + struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); + struct uart_state *state; + unsigned long flags; + + if (uap == NULL) { + printk("HRM... pmz_suspend with NULL uap\n"); + return 0; + } + + if (pm_state.event == mdev->ofdev.dev.power.power_state.event) + return 0; + + pmz_debug("suspend, switching to state %d\n", pm_state.event); + + state = pmz_uart_reg.state + uap->port.line; + + mutex_lock(&pmz_irq_mutex); + mutex_lock(&state->port.mutex); + + spin_lock_irqsave(&uap->port.lock, flags); + + if (ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)) { + /* Disable receiver and transmitter. */ + uap->curregs[R3] &= ~RxENABLE; + uap->curregs[R5] &= ~TxENABLE; + + /* Disable all interrupts and BRK assertion. */ + uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); + uap->curregs[R5] &= ~SND_BRK; + pmz_load_zsregs(uap, uap->curregs); + uap->flags |= PMACZILOG_FLAG_IS_ASLEEP; + mb(); + } + + spin_unlock_irqrestore(&uap->port.lock, flags); + + if (ZS_IS_OPEN(uap) || ZS_IS_OPEN(uap->mate)) + if (ZS_IS_ASLEEP(uap->mate) && ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { + pmz_get_port_A(uap)->flags &= ~PMACZILOG_FLAG_IS_IRQ_ON; + disable_irq(uap->port.irq); + } + + if (ZS_IS_CONS(uap)) + uap->port.cons->flags &= ~CON_ENABLED; + + /* Shut the chip down */ + pmz_set_scc_power(uap, 0); + + mutex_unlock(&state->port.mutex); + mutex_unlock(&pmz_irq_mutex); + + pmz_debug("suspend, switching complete\n"); + + mdev->ofdev.dev.power.power_state = pm_state; + + return 0; +} + + +static int pmz_resume(struct macio_dev *mdev) +{ + struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); + struct uart_state *state; + unsigned long flags; + int pwr_delay = 0; + + if (uap == NULL) + return 0; + + if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON) + return 0; + + pmz_debug("resume, switching to state 0\n"); + + state = pmz_uart_reg.state + uap->port.line; + + mutex_lock(&pmz_irq_mutex); + mutex_lock(&state->port.mutex); + + spin_lock_irqsave(&uap->port.lock, flags); + if (!ZS_IS_OPEN(uap) && !ZS_IS_CONS(uap)) { + spin_unlock_irqrestore(&uap->port.lock, flags); + goto bail; + } + pwr_delay = __pmz_startup(uap); + + /* Take care of config that may have changed while asleep */ + __pmz_set_termios(&uap->port, &uap->termios_cache, NULL); + + if (ZS_IS_OPEN(uap)) { + /* Enable interrupts */ + uap->curregs[R1] |= INT_ALL_Rx | TxINT_ENAB; + if (!ZS_IS_EXTCLK(uap)) + uap->curregs[R1] |= EXT_INT_ENAB; + write_zsreg(uap, R1, uap->curregs[R1]); + } + + spin_unlock_irqrestore(&uap->port.lock, flags); + + if (ZS_IS_CONS(uap)) + uap->port.cons->flags |= CON_ENABLED; + + /* Re-enable IRQ on the controller */ + if (ZS_IS_OPEN(uap) && !ZS_IS_IRQ_ON(pmz_get_port_A(uap))) { + pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; + enable_irq(uap->port.irq); + } + + bail: + mutex_unlock(&state->port.mutex); + mutex_unlock(&pmz_irq_mutex); + + /* Right now, we deal with delay by blocking here, I'll be + * smarter later on + */ + if (pwr_delay != 0) { + pmz_debug("pmz: delaying %d ms\n", pwr_delay); + msleep(pwr_delay); + } + + pmz_debug("resume, switching complete\n"); + + mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON; + + return 0; +} + +/* + * Probe all ports in the system and build the ports array, we register + * with the serial layer at this point, the macio-type probing is only + * used later to "attach" to the sysfs tree so we get power management + * events + */ +static int __init pmz_probe(void) +{ + struct device_node *node_p, *node_a, *node_b, *np; + int count = 0; + int rc; + + /* + * Find all escc chips in the system + */ + node_p = of_find_node_by_name(NULL, "escc"); + while (node_p) { + /* + * First get channel A/B node pointers + * + * TODO: Add routines with proper locking to do that... + */ + node_a = node_b = NULL; + for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) { + if (strncmp(np->name, "ch-a", 4) == 0) + node_a = of_node_get(np); + else if (strncmp(np->name, "ch-b", 4) == 0) + node_b = of_node_get(np); + } + if (!node_a && !node_b) { + of_node_put(node_a); + of_node_put(node_b); + printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n", + (!node_a) ? 'a' : 'b', node_p->full_name); + goto next; + } + + /* + * Fill basic fields in the port structures + */ + pmz_ports[count].mate = &pmz_ports[count+1]; + pmz_ports[count+1].mate = &pmz_ports[count]; + pmz_ports[count].flags = PMACZILOG_FLAG_IS_CHANNEL_A; + pmz_ports[count].node = node_a; + pmz_ports[count+1].node = node_b; + pmz_ports[count].port.line = count; + pmz_ports[count+1].port.line = count+1; + + /* + * Setup the ports for real + */ + rc = pmz_init_port(&pmz_ports[count]); + if (rc == 0 && node_b != NULL) + rc = pmz_init_port(&pmz_ports[count+1]); + if (rc != 0) { + of_node_put(node_a); + of_node_put(node_b); + memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port)); + memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port)); + goto next; + } + count += 2; +next: + node_p = of_find_node_by_name(node_p, "escc"); + } + pmz_ports_count = count; + + return 0; +} + +#else + +extern struct platform_device scc_a_pdev, scc_b_pdev; + +static int __init pmz_init_port(struct uart_pmac_port *uap) +{ + struct resource *r_ports; + int irq; + + r_ports = platform_get_resource(uap->node, IORESOURCE_MEM, 0); + irq = platform_get_irq(uap->node, 0); + if (!r_ports || !irq) + return -ENODEV; + + uap->port.mapbase = r_ports->start; + uap->port.membase = (unsigned char __iomem *) r_ports->start; + uap->port.iotype = UPIO_MEM; + uap->port.irq = irq; + uap->port.uartclk = ZS_CLOCK; + uap->port.fifosize = 1; + uap->port.ops = &pmz_pops; + uap->port.type = PORT_PMAC_ZILOG; + uap->port.flags = 0; + + uap->control_reg = uap->port.membase; + uap->data_reg = uap->control_reg + 4; + uap->port_type = 0; + + pmz_convert_to_zs(uap, CS8, 0, 9600); + + return 0; +} + +static int __init pmz_probe(void) +{ + int err; + + pmz_ports_count = 0; + + pmz_ports[0].mate = &pmz_ports[1]; + pmz_ports[0].port.line = 0; + pmz_ports[0].flags = PMACZILOG_FLAG_IS_CHANNEL_A; + pmz_ports[0].node = &scc_a_pdev; + err = pmz_init_port(&pmz_ports[0]); + if (err) + return err; + pmz_ports_count++; + + pmz_ports[1].mate = &pmz_ports[0]; + pmz_ports[1].port.line = 1; + pmz_ports[1].flags = 0; + pmz_ports[1].node = &scc_b_pdev; + err = pmz_init_port(&pmz_ports[1]); + if (err) + return err; + pmz_ports_count++; + + return 0; +} + +static void pmz_dispose_port(struct uart_pmac_port *uap) +{ + memset(uap, 0, sizeof(struct uart_pmac_port)); +} + +static int __init pmz_attach(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < pmz_ports_count; i++) + if (pmz_ports[i].node == pdev) + return 0; + return -ENODEV; +} + +static int __exit pmz_detach(struct platform_device *pdev) +{ + return 0; +} + +#endif /* !CONFIG_PPC_PMAC */ + +#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE + +static void pmz_console_write(struct console *con, const char *s, unsigned int count); +static int __init pmz_console_setup(struct console *co, char *options); + +static struct console pmz_console = { + .name = PMACZILOG_NAME, + .write = pmz_console_write, + .device = uart_console_device, + .setup = pmz_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &pmz_uart_reg, +}; + +#define PMACZILOG_CONSOLE &pmz_console +#else /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ +#define PMACZILOG_CONSOLE (NULL) +#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ + +/* + * Register the driver, console driver and ports with the serial + * core + */ +static int __init pmz_register(void) +{ + int i, rc; + + pmz_uart_reg.nr = pmz_ports_count; + pmz_uart_reg.cons = PMACZILOG_CONSOLE; + + /* + * Register this driver with the serial core + */ + rc = uart_register_driver(&pmz_uart_reg); + if (rc) + return rc; + + /* + * Register each port with the serial core + */ + for (i = 0; i < pmz_ports_count; i++) { + struct uart_pmac_port *uport = &pmz_ports[i]; + /* NULL node may happen on wallstreet */ + if (uport->node != NULL) + rc = uart_add_one_port(&pmz_uart_reg, &uport->port); + if (rc) + goto err_out; + } + + return 0; +err_out: + while (i-- > 0) { + struct uart_pmac_port *uport = &pmz_ports[i]; + uart_remove_one_port(&pmz_uart_reg, &uport->port); + } + uart_unregister_driver(&pmz_uart_reg); + return rc; +} + +#ifdef CONFIG_PPC_PMAC + +static struct of_device_id pmz_match[] = +{ + { + .name = "ch-a", + }, + { + .name = "ch-b", + }, + {}, +}; +MODULE_DEVICE_TABLE (of, pmz_match); + +static struct macio_driver pmz_driver = { + .driver = { + .name = "pmac_zilog", + .owner = THIS_MODULE, + .of_match_table = pmz_match, + }, + .probe = pmz_attach, + .remove = pmz_detach, + .suspend = pmz_suspend, + .resume = pmz_resume, +}; + +#else + +static struct platform_driver pmz_driver = { + .remove = __exit_p(pmz_detach), + .driver = { + .name = "scc", + .owner = THIS_MODULE, + }, +}; + +#endif /* !CONFIG_PPC_PMAC */ + +static int __init init_pmz(void) +{ + int rc, i; + printk(KERN_INFO "%s\n", version); + + /* + * First, we need to do a direct OF-based probe pass. We + * do that because we want serial console up before the + * macio stuffs calls us back, and since that makes it + * easier to pass the proper number of channels to + * uart_register_driver() + */ + if (pmz_ports_count == 0) + pmz_probe(); + + /* + * Bail early if no port found + */ + if (pmz_ports_count == 0) + return -ENODEV; + + /* + * Now we register with the serial layer + */ + rc = pmz_register(); + if (rc) { + printk(KERN_ERR + "pmac_zilog: Error registering serial device, disabling pmac_zilog.\n" + "pmac_zilog: Did another serial driver already claim the minors?\n"); + /* effectively "pmz_unprobe()" */ + for (i=0; i < pmz_ports_count; i++) + pmz_dispose_port(&pmz_ports[i]); + return rc; + } + + /* + * Then we register the macio driver itself + */ +#ifdef CONFIG_PPC_PMAC + return macio_register_driver(&pmz_driver); +#else + return platform_driver_probe(&pmz_driver, pmz_attach); +#endif +} + +static void __exit exit_pmz(void) +{ + int i; + +#ifdef CONFIG_PPC_PMAC + /* Get rid of macio-driver (detach from macio) */ + macio_unregister_driver(&pmz_driver); +#else + platform_driver_unregister(&pmz_driver); +#endif + + for (i = 0; i < pmz_ports_count; i++) { + struct uart_pmac_port *uport = &pmz_ports[i]; + if (uport->node != NULL) { + uart_remove_one_port(&pmz_uart_reg, &uport->port); + pmz_dispose_port(uport); + } + } + /* Unregister UART driver */ + uart_unregister_driver(&pmz_uart_reg); +} + +#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE + +static void pmz_console_putchar(struct uart_port *port, int ch) +{ + struct uart_pmac_port *uap = (struct uart_pmac_port *)port; + + /* Wait for the transmit buffer to empty. */ + while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0) + udelay(5); + write_zsdata(uap, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +static void pmz_console_write(struct console *con, const char *s, unsigned int count) +{ + struct uart_pmac_port *uap = &pmz_ports[con->index]; + unsigned long flags; + + if (ZS_IS_ASLEEP(uap)) + return; + spin_lock_irqsave(&uap->port.lock, flags); + + /* Turn of interrupts and enable the transmitter. */ + write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB); + write_zsreg(uap, R5, uap->curregs[5] | TxENABLE | RTS | DTR); + + uart_console_write(&uap->port, s, count, pmz_console_putchar); + + /* Restore the values in the registers. */ + write_zsreg(uap, R1, uap->curregs[1]); + /* Don't disable the transmitter. */ + + spin_unlock_irqrestore(&uap->port.lock, flags); +} + +/* + * Setup the serial console + */ +static int __init pmz_console_setup(struct console *co, char *options) +{ + struct uart_pmac_port *uap; + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + unsigned long pwr_delay; + + /* + * XServe's default to 57600 bps + */ + if (of_machine_is_compatible("RackMac1,1") + || of_machine_is_compatible("RackMac1,2") + || of_machine_is_compatible("MacRISC4")) + baud = 57600; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= pmz_ports_count) + co->index = 0; + uap = &pmz_ports[co->index]; + if (uap->node == NULL) + return -ENODEV; + port = &uap->port; + + /* + * Mark port as beeing a console + */ + uap->flags |= PMACZILOG_FLAG_IS_CONS; + + /* + * Temporary fix for uart layer who didn't setup the spinlock yet + */ + spin_lock_init(&port->lock); + + /* + * Enable the hardware + */ + pwr_delay = __pmz_startup(uap); + if (pwr_delay) + mdelay(pwr_delay); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static int __init pmz_console_init(void) +{ + /* Probe ports */ + pmz_probe(); + + /* TODO: Autoprobe console based on OF */ + /* pmz_console.index = i; */ + register_console(&pmz_console); + + return 0; + +} +console_initcall(pmz_console_init); +#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */ + +module_init(init_pmz); +module_exit(exit_pmz); diff --git a/drivers/tty/serial/pmac_zilog.h b/drivers/tty/serial/pmac_zilog.h new file mode 100644 index 000000000000..cbc34fbb1b20 --- /dev/null +++ b/drivers/tty/serial/pmac_zilog.h @@ -0,0 +1,396 @@ +#ifndef __PMAC_ZILOG_H__ +#define __PMAC_ZILOG_H__ + +#ifdef CONFIG_PPC_PMAC +#define pmz_debug(fmt, arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg) +#define pmz_error(fmt, arg...) dev_err(&uap->dev->ofdev.dev, fmt, ## arg) +#define pmz_info(fmt, arg...) dev_info(&uap->dev->ofdev.dev, fmt, ## arg) +#else +#define pmz_debug(fmt, arg...) dev_dbg(&uap->node->dev, fmt, ## arg) +#define pmz_error(fmt, arg...) dev_err(&uap->node->dev, fmt, ## arg) +#define pmz_info(fmt, arg...) dev_info(&uap->node->dev, fmt, ## arg) +#endif + +/* + * At most 2 ESCCs with 2 ports each + */ +#define MAX_ZS_PORTS 4 + +/* + * We wrap our port structure around the generic uart_port. + */ +#define NUM_ZSREGS 17 + +struct uart_pmac_port { + struct uart_port port; + struct uart_pmac_port *mate; + +#ifdef CONFIG_PPC_PMAC + /* macio_dev for the escc holding this port (maybe be null on + * early inited port) + */ + struct macio_dev *dev; + /* device node to this port, this points to one of 2 childs + * of "escc" node (ie. ch-a or ch-b) + */ + struct device_node *node; +#else + struct platform_device *node; +#endif + + /* Port type as obtained from device tree (IRDA, modem, ...) */ + int port_type; + u8 curregs[NUM_ZSREGS]; + + unsigned int flags; +#define PMACZILOG_FLAG_IS_CONS 0x00000001 +#define PMACZILOG_FLAG_IS_KGDB 0x00000002 +#define PMACZILOG_FLAG_MODEM_STATUS 0x00000004 +#define PMACZILOG_FLAG_IS_CHANNEL_A 0x00000008 +#define PMACZILOG_FLAG_REGS_HELD 0x00000010 +#define PMACZILOG_FLAG_TX_STOPPED 0x00000020 +#define PMACZILOG_FLAG_TX_ACTIVE 0x00000040 +#define PMACZILOG_FLAG_ENABLED 0x00000080 +#define PMACZILOG_FLAG_IS_IRDA 0x00000100 +#define PMACZILOG_FLAG_IS_INTMODEM 0x00000200 +#define PMACZILOG_FLAG_HAS_DMA 0x00000400 +#define PMACZILOG_FLAG_RSRC_REQUESTED 0x00000800 +#define PMACZILOG_FLAG_IS_ASLEEP 0x00001000 +#define PMACZILOG_FLAG_IS_OPEN 0x00002000 +#define PMACZILOG_FLAG_IS_IRQ_ON 0x00004000 +#define PMACZILOG_FLAG_IS_EXTCLK 0x00008000 +#define PMACZILOG_FLAG_BREAK 0x00010000 + + unsigned char parity_mask; + unsigned char prev_status; + + volatile u8 __iomem *control_reg; + volatile u8 __iomem *data_reg; + +#ifdef CONFIG_PPC_PMAC + unsigned int tx_dma_irq; + unsigned int rx_dma_irq; + volatile struct dbdma_regs __iomem *tx_dma_regs; + volatile struct dbdma_regs __iomem *rx_dma_regs; +#endif + + struct ktermios termios_cache; +}; + +#define to_pmz(p) ((struct uart_pmac_port *)(p)) + +static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap) +{ + if (uap->flags & PMACZILOG_FLAG_IS_CHANNEL_A) + return uap; + return uap->mate; +} + +/* + * Register accessors. Note that we don't need to enforce a recovery + * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip, + * though if we try to use this driver on older machines, we might have + * to add it back + */ +static inline u8 read_zsreg(struct uart_pmac_port *port, u8 reg) +{ + if (reg != 0) + writeb(reg, port->control_reg); + return readb(port->control_reg); +} + +static inline void write_zsreg(struct uart_pmac_port *port, u8 reg, u8 value) +{ + if (reg != 0) + writeb(reg, port->control_reg); + writeb(value, port->control_reg); +} + +static inline u8 read_zsdata(struct uart_pmac_port *port) +{ + return readb(port->data_reg); +} + +static inline void write_zsdata(struct uart_pmac_port *port, u8 data) +{ + writeb(data, port->data_reg); +} + +static inline void zssync(struct uart_pmac_port *port) +{ + (void)readb(port->control_reg); +} + +/* Conversion routines to/from brg time constants from/to bits + * per second. + */ +#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) +#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) + +#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ + +/* The Zilog register set */ + +#define FLAG 0x7e + +/* Write Register 0 */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 +#define R7P 16 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 */ + +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ +#define INT_ERR_Rx 0x18 /* Int on error only */ +#define RxINT_MASK 0x18 + +#define WT_RDY_RT 0x20 /* W/Req reflects recv if 1, xmit if 0 */ +#define WT_FN_RDYFN 0x40 /* W/Req pin is DMA request if 1, wait if 0 */ +#define WT_RDY_ENAB 0x80 /* Enable W/Req pin */ + +/* Write Register #2 (Interrupt Vector) */ + +/* Write Register 3 */ + +#define RxENABLE 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxN_MASK 0xc0 + +/* Write Register 4 */ + +#define PAR_ENAB 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ +#define SB_MASK 0xc + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xC0 /* x64 clock mode */ +#define XCLK_MASK 0xC0 + +/* Write Register 5 */ + +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENABLE 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define TxN_MASK 0x60 +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 7' (Some enhanced feature control) */ +#define ENEXREAD 0x40 /* Enable read of some write registers */ + +/* Write Register 8 (transmit buffer) */ + +/* Write Register 9 (Master interrupt control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (misc control bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (lower byte of baud rate generator time constant) */ + +/* Write Register 13 (upper byte of baud rate generator time constant) */ + +/* Write Register 14 (Misc control bits) */ +#define BRENAB 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (external/status interrupt control) */ +#define EN85C30 1 /* Enable some 85c30-enhanced registers */ +#define ZCIE 2 /* Zero count IE */ +#define ENSTFIFO 4 /* Enable status FIFO (SDLC) */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC_HUNT 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define CRC_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x06 + +/* Read Register 3 (interrupt pending register) ch a only */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 8 (receive data register) */ + +/* Read Register 10 (misc status bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (lower byte of baud rate generator constant) */ + +/* Read Register 13 (upper byte of baud rate generator constant) */ + +/* Read Register 15 (value of WR 15) */ + +/* Misc macros */ +#define ZS_CLEARERR(port) (write_zsreg(port, 0, ERR_RES)) +#define ZS_CLEARFIFO(port) do { volatile unsigned char garbage; \ + garbage = read_zsdata(port); \ + garbage = read_zsdata(port); \ + garbage = read_zsdata(port); \ + } while(0) + +#define ZS_IS_CONS(UP) ((UP)->flags & PMACZILOG_FLAG_IS_CONS) +#define ZS_IS_KGDB(UP) ((UP)->flags & PMACZILOG_FLAG_IS_KGDB) +#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & PMACZILOG_FLAG_IS_CHANNEL_A) +#define ZS_REGS_HELD(UP) ((UP)->flags & PMACZILOG_FLAG_REGS_HELD) +#define ZS_TX_STOPPED(UP) ((UP)->flags & PMACZILOG_FLAG_TX_STOPPED) +#define ZS_TX_ACTIVE(UP) ((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE) +#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS) +#define ZS_IS_IRDA(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA) +#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM) +#define ZS_HAS_DMA(UP) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA) +#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) +#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) +#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) +#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK) + +#endif /* __PMAC_ZILOG_H__ */ diff --git a/drivers/tty/serial/pnx8xxx_uart.c b/drivers/tty/serial/pnx8xxx_uart.c new file mode 100644 index 000000000000..0aa75a97531c --- /dev/null +++ b/drivers/tty/serial/pnx8xxx_uart.c @@ -0,0 +1,854 @@ +/* + * UART driver for PNX8XXX SoCs + * + * Author: Per Hallsmark per.hallsmark@mvista.com + * Ported to 2.6 kernel by EmbeddedAlley + * Reworked by Vitaly Wool + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of + * any kind, whether express or implied. + * + */ + +#if defined(CONFIG_SERIAL_PNX8XXX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* We'll be using StrongARM sa1100 serial port major/minor */ +#define SERIAL_PNX8XXX_MAJOR 204 +#define MINOR_START 5 + +#define NR_PORTS 2 + +#define PNX8XXX_ISR_PASS_LIMIT 256 + +/* + * Convert from ignore_status_mask or read_status_mask to FIFO + * and interrupt status bits + */ +#define SM_TO_FIFO(x) ((x) >> 10) +#define SM_TO_ISTAT(x) ((x) & 0x000001ff) +#define FIFO_TO_SM(x) ((x) << 10) +#define ISTAT_TO_SM(x) ((x) & 0x000001ff) + +/* + * This is the size of our serial port register set. + */ +#define UART_PORT_SIZE 0x1000 + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +extern struct pnx8xxx_port pnx8xxx_ports[]; + +static inline int serial_in(struct pnx8xxx_port *sport, int offset) +{ + return (__raw_readl(sport->port.membase + offset)); +} + +static inline void serial_out(struct pnx8xxx_port *sport, int offset, int value) +{ + __raw_writel(value, sport->port.membase + offset); +} + +/* + * Handle any change of modem status signal since we were last called. + */ +static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void pnx8xxx_timeout(unsigned long data) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data; + unsigned long flags; + + if (sport->port.state) { + spin_lock_irqsave(&sport->port.lock, flags); + pnx8xxx_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +/* + * interrupts disabled on entry + */ +static void pnx8xxx_stop_tx(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + u32 ien; + + /* Disable TX intr */ + ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLTX); + + /* Clear all pending TX intr */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); +} + +/* + * interrupts may not be disabled on entry + */ +static void pnx8xxx_start_tx(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + u32 ien; + + /* Clear all pending TX intr */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX); + + /* Enable TX intr */ + ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, ien | PNX8XXX_UART_INT_ALLTX); +} + +/* + * Interrupts enabled + */ +static void pnx8xxx_stop_rx(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + u32 ien; + + /* Disable RX intr */ + ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLRX); + + /* Clear all pending RX intr */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void pnx8xxx_enable_ms(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) +{ + struct tty_struct *tty = sport->port.state->port.tty; + unsigned int status, ch, flg; + + status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | + ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); + while (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFIFO)) { + ch = serial_in(sport, PNX8XXX_FIFO) & 0xff; + + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (status & (FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE | + PNX8XXX_UART_FIFO_RXPAR | + PNX8XXX_UART_FIFO_RXBRK) | + ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))) { + if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXBRK)) { + status &= ~(FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)); + sport->port.icount.brk++; + if (uart_handle_break(&sport->port)) + goto ignore_char; + } else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) + sport->port.icount.parity++; + else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) + sport->port.icount.frame++; + if (status & ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN)) + sport->port.icount.overrun++; + + status &= sport->port.read_status_mask; + + if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR)) + flg = TTY_PARITY; + else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE)) + flg = TTY_FRAME; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char; + + uart_insert_char(&sport->port, status, + ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN), ch, flg); + + ignore_char: + serial_out(sport, PNX8XXX_LCR, serial_in(sport, PNX8XXX_LCR) | + PNX8XXX_UART_LCR_RX_NEXT); + status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | + ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT)); + } + tty_flip_buffer_push(tty); +} + +static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + if (sport->port.x_char) { + serial_out(sport, PNX8XXX_FIFO, sport->port.x_char); + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /* + * Check the modem control lines before + * transmitting anything. + */ + pnx8xxx_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + pnx8xxx_stop_tx(&sport->port); + return; + } + + /* + * TX while bytes available + */ + while (((serial_in(sport, PNX8XXX_FIFO) & + PNX8XXX_UART_FIFO_TXFIFO) >> 16) < 16) { + serial_out(sport, PNX8XXX_FIFO, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + pnx8xxx_stop_tx(&sport->port); +} + +static irqreturn_t pnx8xxx_int(int irq, void *dev_id) +{ + struct pnx8xxx_port *sport = dev_id; + unsigned int status; + + spin_lock(&sport->port.lock); + /* Get the interrupts */ + status = serial_in(sport, PNX8XXX_ISTAT) & serial_in(sport, PNX8XXX_IEN); + + /* Byte or break signal received */ + if (status & (PNX8XXX_UART_INT_RX | PNX8XXX_UART_INT_BREAK)) + pnx8xxx_rx_chars(sport); + + /* TX holding register empty - transmit a byte */ + if (status & PNX8XXX_UART_INT_TX) + pnx8xxx_tx_chars(sport); + + /* Clear the ISTAT register */ + serial_out(sport, PNX8XXX_ICLR, status); + + spin_unlock(&sport->port.lock); + return IRQ_HANDLED; +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int pnx8xxx_tx_empty(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT; +} + +static unsigned int pnx8xxx_get_mctrl(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned int mctrl = TIOCM_DSR; + unsigned int msr; + + /* REVISIT */ + + msr = serial_in(sport, PNX8XXX_MCR); + + mctrl |= msr & PNX8XXX_UART_MCR_CTS ? TIOCM_CTS : 0; + mctrl |= msr & PNX8XXX_UART_MCR_DCD ? TIOCM_CAR : 0; + + return mctrl; +} + +static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +#if 0 /* FIXME */ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned int msr; +#endif +} + +/* + * Interrupts always disabled. + */ +static void pnx8xxx_break_ctl(struct uart_port *port, int break_state) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned long flags; + unsigned int lcr; + + spin_lock_irqsave(&sport->port.lock, flags); + lcr = serial_in(sport, PNX8XXX_LCR); + if (break_state == -1) + lcr |= PNX8XXX_UART_LCR_TXBREAK; + else + lcr &= ~PNX8XXX_UART_LCR_TXBREAK; + serial_out(sport, PNX8XXX_LCR, lcr); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int pnx8xxx_startup(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(sport->port.irq, pnx8xxx_int, 0, + "pnx8xxx-uart", sport); + if (retval) + return retval; + + /* + * Finally, clear and enable interrupts + */ + + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | + PNX8XXX_UART_INT_ALLTX); + + serial_out(sport, PNX8XXX_IEN, serial_in(sport, PNX8XXX_IEN) | + PNX8XXX_UART_INT_ALLRX | + PNX8XXX_UART_INT_ALLTX); + + /* + * Enable modem status interrupts + */ + spin_lock_irq(&sport->port.lock); + pnx8xxx_enable_ms(&sport->port); + spin_unlock_irq(&sport->port.lock); + + return 0; +} + +static void pnx8xxx_shutdown(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int lcr; + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Disable all interrupts + */ + serial_out(sport, PNX8XXX_IEN, 0); + + /* + * Reset the Tx and Rx FIFOS, disable the break condition + */ + lcr = serial_in(sport, PNX8XXX_LCR); + lcr &= ~PNX8XXX_UART_LCR_TXBREAK; + lcr |= PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST; + serial_out(sport, PNX8XXX_LCR, lcr); + + /* + * Clear all interrupts + */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX | + PNX8XXX_UART_INT_ALLTX); + + /* + * Free the interrupt + */ + free_irq(sport->port.irq, sport); +} + +static void +pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + unsigned long flags; + unsigned int lcr_fcr, old_ien, baud, quot; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + + /* + * We only support CS7 and CS8. + */ + while ((termios->c_cflag & CSIZE) != CS7 && + (termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8) + lcr_fcr = PNX8XXX_UART_LCR_8BIT; + else + lcr_fcr = 0; + + if (termios->c_cflag & CSTOPB) + lcr_fcr |= PNX8XXX_UART_LCR_2STOPB; + if (termios->c_cflag & PARENB) { + lcr_fcr |= PNX8XXX_UART_LCR_PAREN; + if (!(termios->c_cflag & PARODD)) + lcr_fcr |= PNX8XXX_UART_LCR_PAREVN; + } + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask = ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN) | + ISTAT_TO_SM(PNX8XXX_UART_INT_EMPTY) | + ISTAT_TO_SM(PNX8XXX_UART_INT_RX); + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) | + FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR); + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK); + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN); + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + sport->port.ignore_status_mask |= + ISTAT_TO_SM(PNX8XXX_UART_INT_RX); + + del_timer_sync(&sport->timer); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * disable interrupts and drain transmitter + */ + old_ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX | + PNX8XXX_UART_INT_ALLRX)); + + while (serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA) + barrier(); + + /* then, disable everything */ + serial_out(sport, PNX8XXX_IEN, 0); + + /* Reset the Rx and Tx FIFOs too */ + lcr_fcr |= PNX8XXX_UART_LCR_TX_RST; + lcr_fcr |= PNX8XXX_UART_LCR_RX_RST; + + /* set the parity, stop bits and data size */ + serial_out(sport, PNX8XXX_LCR, lcr_fcr); + + /* set the baud rate */ + quot -= 1; + serial_out(sport, PNX8XXX_BAUD, quot); + + serial_out(sport, PNX8XXX_ICLR, -1); + + serial_out(sport, PNX8XXX_IEN, old_ien); + + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + pnx8xxx_enable_ms(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *pnx8xxx_type(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + return sport->port.type == PORT_PNX8XXX ? "PNX8XXX" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void pnx8xxx_release_port(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + release_mem_region(sport->port.mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int pnx8xxx_request_port(struct uart_port *port) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, + "pnx8xxx-uart") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void pnx8xxx_config_port(struct uart_port *port, int flags) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + + if (flags & UART_CONFIG_TYPE && + pnx8xxx_request_port(&sport->port) == 0) + sport->port.type = PORT_PNX8XXX; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_PNX8XXX and PORT_UNKNOWN + */ +static int +pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_PNX8XXX) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops pnx8xxx_pops = { + .tx_empty = pnx8xxx_tx_empty, + .set_mctrl = pnx8xxx_set_mctrl, + .get_mctrl = pnx8xxx_get_mctrl, + .stop_tx = pnx8xxx_stop_tx, + .start_tx = pnx8xxx_start_tx, + .stop_rx = pnx8xxx_stop_rx, + .enable_ms = pnx8xxx_enable_ms, + .break_ctl = pnx8xxx_break_ctl, + .startup = pnx8xxx_startup, + .shutdown = pnx8xxx_shutdown, + .set_termios = pnx8xxx_set_termios, + .type = pnx8xxx_type, + .release_port = pnx8xxx_release_port, + .request_port = pnx8xxx_request_port, + .config_port = pnx8xxx_config_port, + .verify_port = pnx8xxx_verify_port, +}; + + +/* + * Setup the PNX8XXX serial ports. + * + * Note also that we support "console=ttySx" where "x" is either 0 or 1. + */ +static void __init pnx8xxx_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < NR_PORTS; i++) { + init_timer(&pnx8xxx_ports[i].timer); + pnx8xxx_ports[i].timer.function = pnx8xxx_timeout; + pnx8xxx_ports[i].timer.data = (unsigned long)&pnx8xxx_ports[i]; + pnx8xxx_ports[i].port.ops = &pnx8xxx_pops; + } +} + +#ifdef CONFIG_SERIAL_PNX8XXX_CONSOLE + +static void pnx8xxx_console_putchar(struct uart_port *port, int ch) +{ + struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port; + int status; + + do { + /* Wait for UART_TX register to empty */ + status = serial_in(sport, PNX8XXX_FIFO); + } while (status & PNX8XXX_UART_FIFO_TXFIFO); + serial_out(sport, PNX8XXX_FIFO, ch); +} + +/* + * Interrupts are disabled on entering + */static void +pnx8xxx_console_write(struct console *co, const char *s, unsigned int count) +{ + struct pnx8xxx_port *sport = &pnx8xxx_ports[co->index]; + unsigned int old_ien, status; + + /* + * First, save IEN and then disable interrupts + */ + old_ien = serial_in(sport, PNX8XXX_IEN); + serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX | + PNX8XXX_UART_INT_ALLRX)); + + uart_console_write(&sport->port, s, count, pnx8xxx_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore IEN + */ + do { + /* Wait for UART_TX register to empty */ + status = serial_in(sport, PNX8XXX_FIFO); + } while (status & PNX8XXX_UART_FIFO_TXFIFO); + + /* Clear TX and EMPTY interrupt */ + serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_TX | + PNX8XXX_UART_INT_EMPTY); + + serial_out(sport, PNX8XXX_IEN, old_ien); +} + +static int __init +pnx8xxx_console_setup(struct console *co, char *options) +{ + struct pnx8xxx_port *sport; + 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. + */ + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + sport = &pnx8xxx_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver pnx8xxx_reg; +static struct console pnx8xxx_console = { + .name = "ttyS", + .write = pnx8xxx_console_write, + .device = uart_console_device, + .setup = pnx8xxx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &pnx8xxx_reg, +}; + +static int __init pnx8xxx_rs_console_init(void) +{ + pnx8xxx_init_ports(); + register_console(&pnx8xxx_console); + return 0; +} +console_initcall(pnx8xxx_rs_console_init); + +#define PNX8XXX_CONSOLE &pnx8xxx_console +#else +#define PNX8XXX_CONSOLE NULL +#endif + +static struct uart_driver pnx8xxx_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyS", + .dev_name = "ttyS", + .major = SERIAL_PNX8XXX_MAJOR, + .minor = MINOR_START, + .nr = NR_PORTS, + .cons = PNX8XXX_CONSOLE, +}; + +static int pnx8xxx_serial_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct pnx8xxx_port *sport = platform_get_drvdata(pdev); + + return uart_suspend_port(&pnx8xxx_reg, &sport->port); +} + +static int pnx8xxx_serial_resume(struct platform_device *pdev) +{ + struct pnx8xxx_port *sport = platform_get_drvdata(pdev); + + return uart_resume_port(&pnx8xxx_reg, &sport->port); +} + +static int pnx8xxx_serial_probe(struct platform_device *pdev) +{ + struct resource *res = pdev->resource; + int i; + + for (i = 0; i < pdev->num_resources; i++, res++) { + if (!(res->flags & IORESOURCE_MEM)) + continue; + + for (i = 0; i < NR_PORTS; i++) { + if (pnx8xxx_ports[i].port.mapbase != res->start) + continue; + + pnx8xxx_ports[i].port.dev = &pdev->dev; + uart_add_one_port(&pnx8xxx_reg, &pnx8xxx_ports[i].port); + platform_set_drvdata(pdev, &pnx8xxx_ports[i]); + break; + } + } + + return 0; +} + +static int pnx8xxx_serial_remove(struct platform_device *pdev) +{ + struct pnx8xxx_port *sport = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (sport) + uart_remove_one_port(&pnx8xxx_reg, &sport->port); + + return 0; +} + +static struct platform_driver pnx8xxx_serial_driver = { + .driver = { + .name = "pnx8xxx-uart", + .owner = THIS_MODULE, + }, + .probe = pnx8xxx_serial_probe, + .remove = pnx8xxx_serial_remove, + .suspend = pnx8xxx_serial_suspend, + .resume = pnx8xxx_serial_resume, +}; + +static int __init pnx8xxx_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: PNX8XXX driver\n"); + + pnx8xxx_init_ports(); + + ret = uart_register_driver(&pnx8xxx_reg); + if (ret == 0) { + ret = platform_driver_register(&pnx8xxx_serial_driver); + if (ret) + uart_unregister_driver(&pnx8xxx_reg); + } + return ret; +} + +static void __exit pnx8xxx_serial_exit(void) +{ + platform_driver_unregister(&pnx8xxx_serial_driver); + uart_unregister_driver(&pnx8xxx_reg); +} + +module_init(pnx8xxx_serial_init); +module_exit(pnx8xxx_serial_exit); + +MODULE_AUTHOR("Embedded Alley Solutions, Inc."); +MODULE_DESCRIPTION("PNX8XXX SoCs serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_PNX8XXX_MAJOR); +MODULE_ALIAS("platform:pnx8xxx-uart"); diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c new file mode 100644 index 000000000000..1102a39b44f5 --- /dev/null +++ b/drivers/tty/serial/pxa.c @@ -0,0 +1,879 @@ +/* + * linux/drivers/serial/pxa.c + * + * Based on drivers/serial/8250.c by Russell King. + * + * Author: Nicolas Pitre + * Created: Feb 20, 2003 + * Copyright: (C) 2003 Monta Vista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Note 1: This driver is made separate from the already too overloaded + * 8250.c because it needs some kirks of its own and that'll make it + * easier to add DMA support. + * + * Note 2: I'm too sick of device allocation policies for serial ports. + * If someone else wants to request an "official" allocation of major/minor + * for this driver please be my guest. And don't forget that new hardware + * to come from Intel might have more than 3 or 4 of those UARTs. Let's + * hope for a better port registration and dynamic device allocation scheme + * with the serial core maintainer satisfaction to appear soon. + */ + + +#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct uart_pxa_port { + struct uart_port port; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned int lsr_break_flag; + struct clk *clk; + char *name; +}; + +static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) +{ + offset <<= 2; + return readl(up->port.membase + offset); +} + +static inline void serial_out(struct uart_pxa_port *up, int offset, int value) +{ + offset <<= 2; + writel(value, up->port.membase + offset); +} + +static void serial_pxa_enable_ms(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void serial_pxa_stop_tx(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_pxa_stop_rx(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static inline void receive_chars(struct uart_pxa_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int ch, flag; + int max_count = 256; + + do { + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_PXA_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); + + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_pxa_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_pxa_stop_tx(&up->port); + return; + } + + count = up->port.fifosize / 2; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + + if (uart_circ_empty(xmit)) + serial_pxa_stop_tx(&up->port); +} + +static void serial_pxa_start_tx(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static inline void check_modem_status(struct uart_pxa_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); +} + +/* + * This handles the interrupt from one port. + */ +static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) +{ + struct uart_pxa_port *up = dev_id; + unsigned int iir, lsr; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + lsr = serial_in(up, UART_LSR); + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + check_modem_status(up); + if (lsr & UART_LSR_THRE) + transmit_chars(up); + return IRQ_HANDLED; +} + +static unsigned int serial_pxa_tx_empty(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_pxa_get_mctrl(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char status; + unsigned int ret; + + status = serial_in(up, 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 serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char mcr = 0; + + 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; + + mcr |= up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial_pxa_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +#if 0 +static void serial_pxa_dma_init(struct pxa_uart *up) +{ + up->rxdma = + pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up); + if (up->rxdma < 0) + goto out; + up->txdma = + pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up); + if (up->txdma < 0) + goto err_txdma; + up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL); + if (!up->dmadesc) + goto err_alloc; + + /* ... */ +err_alloc: + pxa_free_dma(up->txdma); +err_rxdma: + pxa_free_dma(up->rxdma); +out: + return; +} +#endif + +static int serial_pxa_startup(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + int retval; + + if (port->line == 3) /* HWUART */ + up->mcr |= UART_MCR_AFE; + else + up->mcr = 0; + + up->port.uartclk = clk_get_rate(up->clk); + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up); + if (retval) + return retval; + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl |= TIOCM_OUT2; + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; + serial_out(up, UART_IER, up->ier); + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + return 0; +} + +static void serial_pxa_shutdown(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + + free_irq(up->port.irq, up); + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +static void +serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + unsigned int dll; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1; + else if ((up->port.uartclk / quot) < (230400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Ensure the port will be enabled. + * This is required especially for serial console. + */ + up->ier |= UART_IER_UUE; + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + else + up->mcr &= ~UART_MCR_AFE; + + serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + + /* + * work around Errata #75 according to Intel(R) PXA27x Processor Family + * Specification Update (Nov 2005) + */ + dll = serial_in(up, UART_DLL); + WARN_ON(dll != (quot & 0xff)); + + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + serial_out(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + serial_pxa_set_mctrl(&up->port, up->port.mctrl); + serial_out(up, UART_FCR, fcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial_pxa_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + if (!state) + clk_enable(up->clk); + else + clk_disable(up->clk); +} + +static void serial_pxa_release_port(struct uart_port *port) +{ +} + +static int serial_pxa_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_pxa_config_port(struct uart_port *port, int flags) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + up->port.type = PORT_PXA; +} + +static int +serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + +static const char * +serial_pxa_type(struct uart_port *port) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + return up->name; +} + +static struct uart_pxa_port *serial_pxa_ports[4]; +static struct uart_driver serial_pxa_reg; + +#ifdef CONFIG_SERIAL_PXA_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_pxa_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +static void serial_pxa_console_putchar(struct uart_port *port, int ch) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +/* + * 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. + */ +static void +serial_pxa_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_pxa_port *up = serial_pxa_ports[co->index]; + unsigned int ier; + + clk_enable(up->clk); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, UART_IER_UUE); + + uart_console_write(&up->port, s, count, serial_pxa_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + + clk_disable(up->clk); +} + +static int __init +serial_pxa_console_setup(struct console *co, char *options) +{ + struct uart_pxa_port *up; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index == -1 || co->index >= serial_pxa_reg.nr) + co->index = 0; + up = serial_pxa_ports[co->index]; + if (!up) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_pxa_console = { + .name = "ttyS", + .write = serial_pxa_console_write, + .device = uart_console_device, + .setup = serial_pxa_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_pxa_reg, +}; + +#define PXA_CONSOLE &serial_pxa_console +#else +#define PXA_CONSOLE NULL +#endif + +struct uart_ops serial_pxa_pops = { + .tx_empty = serial_pxa_tx_empty, + .set_mctrl = serial_pxa_set_mctrl, + .get_mctrl = serial_pxa_get_mctrl, + .stop_tx = serial_pxa_stop_tx, + .start_tx = serial_pxa_start_tx, + .stop_rx = serial_pxa_stop_rx, + .enable_ms = serial_pxa_enable_ms, + .break_ctl = serial_pxa_break_ctl, + .startup = serial_pxa_startup, + .shutdown = serial_pxa_shutdown, + .set_termios = serial_pxa_set_termios, + .pm = serial_pxa_pm, + .type = serial_pxa_type, + .release_port = serial_pxa_release_port, + .request_port = serial_pxa_request_port, + .config_port = serial_pxa_config_port, + .verify_port = serial_pxa_verify_port, +}; + +static struct uart_driver serial_pxa_reg = { + .owner = THIS_MODULE, + .driver_name = "PXA serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = 4, + .cons = PXA_CONSOLE, +}; + +#ifdef CONFIG_PM +static int serial_pxa_suspend(struct device *dev) +{ + struct uart_pxa_port *sport = dev_get_drvdata(dev); + + if (sport) + uart_suspend_port(&serial_pxa_reg, &sport->port); + + return 0; +} + +static int serial_pxa_resume(struct device *dev) +{ + struct uart_pxa_port *sport = dev_get_drvdata(dev); + + if (sport) + uart_resume_port(&serial_pxa_reg, &sport->port); + + return 0; +} + +static const struct dev_pm_ops serial_pxa_pm_ops = { + .suspend = serial_pxa_suspend, + .resume = serial_pxa_resume, +}; +#endif + +static int serial_pxa_probe(struct platform_device *dev) +{ + struct uart_pxa_port *sport; + struct resource *mmres, *irqres; + int ret; + + mmres = platform_get_resource(dev, IORESOURCE_MEM, 0); + irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0); + if (!mmres || !irqres) + return -ENODEV; + + sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL); + if (!sport) + return -ENOMEM; + + sport->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(sport->clk)) { + ret = PTR_ERR(sport->clk); + goto err_free; + } + + sport->port.type = PORT_PXA; + sport->port.iotype = UPIO_MEM; + sport->port.mapbase = mmres->start; + sport->port.irq = irqres->start; + sport->port.fifosize = 64; + sport->port.ops = &serial_pxa_pops; + sport->port.line = dev->id; + sport->port.dev = &dev->dev; + sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; + sport->port.uartclk = clk_get_rate(sport->clk); + + switch (dev->id) { + case 0: sport->name = "FFUART"; break; + case 1: sport->name = "BTUART"; break; + case 2: sport->name = "STUART"; break; + case 3: sport->name = "HWUART"; break; + default: + sport->name = "???"; + break; + } + + sport->port.membase = ioremap(mmres->start, mmres->end - mmres->start + 1); + if (!sport->port.membase) { + ret = -ENOMEM; + goto err_clk; + } + + serial_pxa_ports[dev->id] = sport; + + uart_add_one_port(&serial_pxa_reg, &sport->port); + platform_set_drvdata(dev, sport); + + return 0; + + err_clk: + clk_put(sport->clk); + err_free: + kfree(sport); + return ret; +} + +static int serial_pxa_remove(struct platform_device *dev) +{ + struct uart_pxa_port *sport = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + + uart_remove_one_port(&serial_pxa_reg, &sport->port); + clk_put(sport->clk); + kfree(sport); + + return 0; +} + +static struct platform_driver serial_pxa_driver = { + .probe = serial_pxa_probe, + .remove = serial_pxa_remove, + + .driver = { + .name = "pxa2xx-uart", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &serial_pxa_pm_ops, +#endif + }, +}; + +int __init serial_pxa_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_pxa_reg); + if (ret != 0) + return ret; + + ret = platform_driver_register(&serial_pxa_driver); + if (ret != 0) + uart_unregister_driver(&serial_pxa_reg); + + return ret; +} + +void __exit serial_pxa_exit(void) +{ + platform_driver_unregister(&serial_pxa_driver); + uart_unregister_driver(&serial_pxa_reg); +} + +module_init(serial_pxa_init); +module_exit(serial_pxa_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-uart"); diff --git a/drivers/tty/serial/s3c2400.c b/drivers/tty/serial/s3c2400.c new file mode 100644 index 000000000000..fed1a9a1ffb4 --- /dev/null +++ b/drivers/tty/serial/s3c2400.c @@ -0,0 +1,106 @@ +/* linux/drivers/serial/s3c240.c + * + * Driver for Samsung SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2005 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "samsung.h" + +static int s3c2400_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + clk->divisor = 1; + clk->name = "pclk"; + + return 0; +} + +static int s3c2400_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + return 0; +} + +static int s3c2400_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + dbg("s3c2400_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + wr_regl(port, S3C2410_UCON, cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c2400_uart_inf = { + .name = "Samsung S3C2400 UART", + .type = PORT_S3C2400, + .fifosize = 16, + .rx_fifomask = S3C2410_UFSTAT_RXMASK, + .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2410_UFSTAT_RXFULL, + .tx_fifofull = S3C2410_UFSTAT_TXFULL, + .tx_fifomask = S3C2410_UFSTAT_TXMASK, + .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, + .get_clksrc = s3c2400_serial_getsource, + .set_clksrc = s3c2400_serial_setsource, + .reset_port = s3c2400_serial_resetport, +}; + +static int s3c2400_serial_probe(struct platform_device *dev) +{ + return s3c24xx_serial_probe(dev, &s3c2400_uart_inf); +} + +static struct platform_driver s3c2400_serial_driver = { + .probe = s3c2400_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s3c2400-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c2400_serial_driver, &s3c2400_uart_inf); + +static inline int s3c2400_serial_init(void) +{ + return s3c24xx_serial_init(&s3c2400_serial_driver, &s3c2400_uart_inf); +} + +static inline void s3c2400_serial_exit(void) +{ + platform_driver_unregister(&s3c2400_serial_driver); +} + +module_init(s3c2400_serial_init); +module_exit(s3c2400_serial_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("Samsung S3C2400 SoC Serial port driver"); +MODULE_ALIAS("platform:s3c2400-uart"); diff --git a/drivers/tty/serial/s3c2410.c b/drivers/tty/serial/s3c2410.c new file mode 100644 index 000000000000..73f089d3efd6 --- /dev/null +++ b/drivers/tty/serial/s3c2410.c @@ -0,0 +1,118 @@ +/* linux/drivers/serial/s3c2410.c + * + * Driver for Samsung S3C2410 SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "samsung.h" + +static int s3c2410_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "uclk") == 0) + ucon |= S3C2410_UCON_UCLK; + else + ucon &= ~S3C2410_UCON_UCLK; + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + +static int s3c2410_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk"; + + return 0; +} + +static int s3c2410_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + wr_regl(port, S3C2410_UCON, cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c2410_uart_inf = { + .name = "Samsung S3C2410 UART", + .type = PORT_S3C2410, + .fifosize = 16, + .rx_fifomask = S3C2410_UFSTAT_RXMASK, + .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2410_UFSTAT_RXFULL, + .tx_fifofull = S3C2410_UFSTAT_TXFULL, + .tx_fifomask = S3C2410_UFSTAT_TXMASK, + .tx_fifoshift = S3C2410_UFSTAT_TXSHIFT, + .get_clksrc = s3c2410_serial_getsource, + .set_clksrc = s3c2410_serial_setsource, + .reset_port = s3c2410_serial_resetport, +}; + +static int s3c2410_serial_probe(struct platform_device *dev) +{ + return s3c24xx_serial_probe(dev, &s3c2410_uart_inf); +} + +static struct platform_driver s3c2410_serial_driver = { + .probe = s3c2410_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s3c2410-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c2410_serial_driver, &s3c2410_uart_inf); + +static int __init s3c2410_serial_init(void) +{ + return s3c24xx_serial_init(&s3c2410_serial_driver, &s3c2410_uart_inf); +} + +static void __exit s3c2410_serial_exit(void) +{ + platform_driver_unregister(&s3c2410_serial_driver); +} + +module_init(s3c2410_serial_init); +module_exit(s3c2410_serial_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("Samsung S3C2410 SoC Serial port driver"); +MODULE_ALIAS("platform:s3c2410-uart"); diff --git a/drivers/tty/serial/s3c2412.c b/drivers/tty/serial/s3c2412.c new file mode 100644 index 000000000000..1700b1a2fb7e --- /dev/null +++ b/drivers/tty/serial/s3c2412.c @@ -0,0 +1,152 @@ +/* linux/drivers/serial/s3c2412.c + * + * Driver for Samsung S3C2412 and S3C2413 SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "samsung.h" + +static int s3c2412_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + ucon &= ~S3C2412_UCON_CLKMASK; + + if (strcmp(clk->name, "uclk") == 0) + ucon |= S3C2440_UCON_UCLK; + else if (strcmp(clk->name, "pclk") == 0) + ucon |= S3C2440_UCON_PCLK; + else if (strcmp(clk->name, "usysclk") == 0) + ucon |= S3C2412_UCON_USYSCLK; + else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s3c2412_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + switch (ucon & S3C2412_UCON_CLKMASK) { + case S3C2412_UCON_UCLK: + clk->divisor = 1; + clk->name = "uclk"; + break; + + case S3C2412_UCON_PCLK: + case S3C2412_UCON_PCLK2: + clk->divisor = 1; + clk->name = "pclk"; + break; + + case S3C2412_UCON_USYSCLK: + clk->divisor = 1; + clk->name = "usysclk"; + break; + } + + return 0; +} + +static int s3c2412_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + dbg("%s: port=%p (%08lx), cfg=%p\n", + __func__, port, port->mapbase, cfg); + + /* ensure we don't change the clock settings... */ + + ucon &= S3C2412_UCON_CLKMASK; + + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c2412_uart_inf = { + .name = "Samsung S3C2412 UART", + .type = PORT_S3C2412, + .fifosize = 64, + .has_divslot = 1, + .rx_fifomask = S3C2440_UFSTAT_RXMASK, + .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2440_UFSTAT_RXFULL, + .tx_fifofull = S3C2440_UFSTAT_TXFULL, + .tx_fifomask = S3C2440_UFSTAT_TXMASK, + .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, + .get_clksrc = s3c2412_serial_getsource, + .set_clksrc = s3c2412_serial_setsource, + .reset_port = s3c2412_serial_resetport, +}; + +/* device management */ + +static int s3c2412_serial_probe(struct platform_device *dev) +{ + dbg("s3c2440_serial_probe: dev=%p\n", dev); + return s3c24xx_serial_probe(dev, &s3c2412_uart_inf); +} + +static struct platform_driver s3c2412_serial_driver = { + .probe = s3c2412_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s3c2412-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c2412_serial_driver, &s3c2412_uart_inf); + +static inline int s3c2412_serial_init(void) +{ + return s3c24xx_serial_init(&s3c2412_serial_driver, &s3c2412_uart_inf); +} + +static inline void s3c2412_serial_exit(void) +{ + platform_driver_unregister(&s3c2412_serial_driver); +} + +module_init(s3c2412_serial_init); +module_exit(s3c2412_serial_exit); + +MODULE_DESCRIPTION("Samsung S3C2412,S3C2413 SoC Serial port driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:s3c2412-uart"); diff --git a/drivers/tty/serial/s3c2440.c b/drivers/tty/serial/s3c2440.c new file mode 100644 index 000000000000..094cc3904b13 --- /dev/null +++ b/drivers/tty/serial/s3c2440.c @@ -0,0 +1,181 @@ +/* linux/drivers/serial/s3c2440.c + * + * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "samsung.h" + + +static int s3c2440_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + /* todo - proper fclk<>nonfclk switch. */ + + ucon &= ~S3C2440_UCON_CLKMASK; + + if (strcmp(clk->name, "uclk") == 0) + ucon |= S3C2440_UCON_UCLK; + else if (strcmp(clk->name, "pclk") == 0) + ucon |= S3C2440_UCON_PCLK; + else if (strcmp(clk->name, "fclk") == 0) + ucon |= S3C2440_UCON_FCLK; + else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s3c2440_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + unsigned long ucon0, ucon1, ucon2; + + switch (ucon & S3C2440_UCON_CLKMASK) { + case S3C2440_UCON_UCLK: + clk->divisor = 1; + clk->name = "uclk"; + break; + + case S3C2440_UCON_PCLK: + case S3C2440_UCON_PCLK2: + clk->divisor = 1; + clk->name = "pclk"; + break; + + case S3C2440_UCON_FCLK: + /* the fun of calculating the uart divisors on + * the s3c2440 */ + + ucon0 = __raw_readl(S3C24XX_VA_UART0 + S3C2410_UCON); + ucon1 = __raw_readl(S3C24XX_VA_UART1 + S3C2410_UCON); + ucon2 = __raw_readl(S3C24XX_VA_UART2 + S3C2410_UCON); + + printk("ucons: %08lx, %08lx, %08lx\n", ucon0, ucon1, ucon2); + + ucon0 &= S3C2440_UCON0_DIVMASK; + ucon1 &= S3C2440_UCON1_DIVMASK; + ucon2 &= S3C2440_UCON2_DIVMASK; + + if (ucon0 != 0) { + clk->divisor = ucon0 >> S3C2440_UCON_DIVSHIFT; + clk->divisor += 6; + } else if (ucon1 != 0) { + clk->divisor = ucon1 >> S3C2440_UCON_DIVSHIFT; + clk->divisor += 21; + } else if (ucon2 != 0) { + clk->divisor = ucon2 >> S3C2440_UCON_DIVSHIFT; + clk->divisor += 36; + } else { + /* manual calims 44, seems to be 9 */ + clk->divisor = 9; + } + + clk->name = "fclk"; + break; + } + + return 0; +} + +static int s3c2440_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + dbg("s3c2440_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + /* ensure we don't change the clock settings... */ + + ucon &= (S3C2440_UCON0_DIVMASK | (3<<10)); + + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c2440_uart_inf = { + .name = "Samsung S3C2440 UART", + .type = PORT_S3C2440, + .fifosize = 64, + .rx_fifomask = S3C2440_UFSTAT_RXMASK, + .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2440_UFSTAT_RXFULL, + .tx_fifofull = S3C2440_UFSTAT_TXFULL, + .tx_fifomask = S3C2440_UFSTAT_TXMASK, + .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, + .get_clksrc = s3c2440_serial_getsource, + .set_clksrc = s3c2440_serial_setsource, + .reset_port = s3c2440_serial_resetport, +}; + +/* device management */ + +static int s3c2440_serial_probe(struct platform_device *dev) +{ + dbg("s3c2440_serial_probe: dev=%p\n", dev); + return s3c24xx_serial_probe(dev, &s3c2440_uart_inf); +} + +static struct platform_driver s3c2440_serial_driver = { + .probe = s3c2440_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s3c2440-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf); + +static int __init s3c2440_serial_init(void) +{ + return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf); +} + +static void __exit s3c2440_serial_exit(void) +{ + platform_driver_unregister(&s3c2440_serial_driver); +} + +module_init(s3c2440_serial_init); +module_exit(s3c2440_serial_exit); + +MODULE_DESCRIPTION("Samsung S3C2440,S3C2442 SoC Serial port driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:s3c2440-uart"); diff --git a/drivers/tty/serial/s3c24a0.c b/drivers/tty/serial/s3c24a0.c new file mode 100644 index 000000000000..fad6083ca427 --- /dev/null +++ b/drivers/tty/serial/s3c24a0.c @@ -0,0 +1,118 @@ +/* linux/drivers/serial/s3c24a0.c + * + * Driver for Samsung S3C24A0 SoC onboard UARTs. + * + * Based on drivers/serial/s3c2410.c + * + * Author: Sandeep Patil + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "samsung.h" + +static int s3c24a0_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "uclk") == 0) + ucon |= S3C2410_UCON_UCLK; + else + ucon &= ~S3C2410_UCON_UCLK; + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + +static int s3c24a0_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk"; + + return 0; +} + +static int s3c24a0_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + dbg("s3c24a0_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + wr_regl(port, S3C2410_UCON, cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c24a0_uart_inf = { + .name = "Samsung S3C24A0 UART", + .type = PORT_S3C2410, + .fifosize = 16, + .rx_fifomask = S3C24A0_UFSTAT_RXMASK, + .rx_fifoshift = S3C24A0_UFSTAT_RXSHIFT, + .rx_fifofull = S3C24A0_UFSTAT_RXFULL, + .tx_fifofull = S3C24A0_UFSTAT_TXFULL, + .tx_fifomask = S3C24A0_UFSTAT_TXMASK, + .tx_fifoshift = S3C24A0_UFSTAT_TXSHIFT, + .get_clksrc = s3c24a0_serial_getsource, + .set_clksrc = s3c24a0_serial_setsource, + .reset_port = s3c24a0_serial_resetport, +}; + +static int s3c24a0_serial_probe(struct platform_device *dev) +{ + return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf); +} + +static struct platform_driver s3c24a0_serial_driver = { + .probe = s3c24a0_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s3c24a0-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); + +static int __init s3c24a0_serial_init(void) +{ + return s3c24xx_serial_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); +} + +static void __exit s3c24a0_serial_exit(void) +{ + platform_driver_unregister(&s3c24a0_serial_driver); +} + +module_init(s3c24a0_serial_init); +module_exit(s3c24a0_serial_exit); + diff --git a/drivers/tty/serial/s3c6400.c b/drivers/tty/serial/s3c6400.c new file mode 100644 index 000000000000..4be92ab50058 --- /dev/null +++ b/drivers/tty/serial/s3c6400.c @@ -0,0 +1,152 @@ +/* linux/drivers/serial/s3c6400.c + * + * Driver for Samsung S3C6400 and S3C6410 SoC onboard UARTs. + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "samsung.h" + +static int s3c6400_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if (strcmp(clk->name, "uclk0") == 0) { + ucon &= ~S3C6400_UCON_CLKMASK; + ucon |= S3C6400_UCON_UCLK0; + } else if (strcmp(clk->name, "uclk1") == 0) + ucon |= S3C6400_UCON_UCLK1; + else if (strcmp(clk->name, "pclk") == 0) { + /* See notes about transitioning from UCLK to PCLK */ + ucon &= ~S3C6400_UCON_UCLK0; + } else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s3c6400_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + u32 ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + + switch (ucon & S3C6400_UCON_CLKMASK) { + case S3C6400_UCON_UCLK0: + clk->name = "uclk0"; + break; + + case S3C6400_UCON_UCLK1: + clk->name = "uclk1"; + break; + + case S3C6400_UCON_PCLK: + case S3C6400_UCON_PCLK2: + clk->name = "pclk"; + break; + } + + return 0; +} + +static int s3c6400_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + dbg("s3c6400_serial_resetport: port=%p (%08lx), cfg=%p\n", + port, port->mapbase, cfg); + + /* ensure we don't change the clock settings... */ + + ucon &= S3C6400_UCON_CLKMASK; + + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +static struct s3c24xx_uart_info s3c6400_uart_inf = { + .name = "Samsung S3C6400 UART", + .type = PORT_S3C6400, + .fifosize = 64, + .has_divslot = 1, + .rx_fifomask = S3C2440_UFSTAT_RXMASK, + .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, + .rx_fifofull = S3C2440_UFSTAT_RXFULL, + .tx_fifofull = S3C2440_UFSTAT_TXFULL, + .tx_fifomask = S3C2440_UFSTAT_TXMASK, + .tx_fifoshift = S3C2440_UFSTAT_TXSHIFT, + .get_clksrc = s3c6400_serial_getsource, + .set_clksrc = s3c6400_serial_setsource, + .reset_port = s3c6400_serial_resetport, +}; + +/* device management */ + +static int s3c6400_serial_probe(struct platform_device *dev) +{ + dbg("s3c6400_serial_probe: dev=%p\n", dev); + return s3c24xx_serial_probe(dev, &s3c6400_uart_inf); +} + +static struct platform_driver s3c6400_serial_driver = { + .probe = s3c6400_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s3c6400-uart", + .owner = THIS_MODULE, + }, +}; + +s3c24xx_console_init(&s3c6400_serial_driver, &s3c6400_uart_inf); + +static int __init s3c6400_serial_init(void) +{ + return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf); +} + +static void __exit s3c6400_serial_exit(void) +{ + platform_driver_unregister(&s3c6400_serial_driver); +} + +module_init(s3c6400_serial_init); +module_exit(s3c6400_serial_exit); + +MODULE_DESCRIPTION("Samsung S3C6400,S3C6410 SoC Serial port driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:s3c6400-uart"); diff --git a/drivers/tty/serial/s5pv210.c b/drivers/tty/serial/s5pv210.c new file mode 100644 index 000000000000..6ebccd70a707 --- /dev/null +++ b/drivers/tty/serial/s5pv210.c @@ -0,0 +1,162 @@ +/* linux/drivers/serial/s5pv210.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Based on drivers/serial/s3c6400.c + * + * Driver for Samsung S5PV210 SoC UARTs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "samsung.h" + +static int s5pv210_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + struct s3c2410_uartcfg *cfg = port->dev->platform_data; + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if ((cfg->clocks_size) == 1) + return 0; + + if (strcmp(clk->name, "pclk") == 0) + ucon &= ~S5PV210_UCON_CLKMASK; + else if (strcmp(clk->name, "uclk1") == 0) + ucon |= S5PV210_UCON_CLKMASK; + else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s5pv210_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + struct s3c2410_uartcfg *cfg = port->dev->platform_data; + u32 ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + + if ((cfg->clocks_size) == 1) + return 0; + + switch (ucon & S5PV210_UCON_CLKMASK) { + case S5PV210_UCON_PCLK: + clk->name = "pclk"; + break; + case S5PV210_UCON_UCLK: + clk->name = "uclk1"; + break; + } + + return 0; +} + +static int s5pv210_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + ucon &= S5PV210_UCON_CLKMASK; + wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); + wr_regl(port, S3C2410_ULCON, cfg->ulcon); + + /* reset both fifos */ + wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH); + wr_regl(port, S3C2410_UFCON, cfg->ufcon); + + return 0; +} + +#define S5PV210_UART_DEFAULT_INFO(fifo_size) \ + .name = "Samsung S5PV210 UART0", \ + .type = PORT_S3C6400, \ + .fifosize = fifo_size, \ + .has_divslot = 1, \ + .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ + .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ + .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ + .tx_fifofull = S5PV210_UFSTAT_TXFULL, \ + .tx_fifomask = S5PV210_UFSTAT_TXMASK, \ + .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, \ + .get_clksrc = s5pv210_serial_getsource, \ + .set_clksrc = s5pv210_serial_setsource, \ + .reset_port = s5pv210_serial_resetport + +static struct s3c24xx_uart_info s5p_port_fifo256 = { + S5PV210_UART_DEFAULT_INFO(256), +}; + +static struct s3c24xx_uart_info s5p_port_fifo64 = { + S5PV210_UART_DEFAULT_INFO(64), +}; + +static struct s3c24xx_uart_info s5p_port_fifo16 = { + S5PV210_UART_DEFAULT_INFO(16), +}; + +static struct s3c24xx_uart_info *s5p_uart_inf[] = { + [0] = &s5p_port_fifo256, + [1] = &s5p_port_fifo64, + [2] = &s5p_port_fifo16, + [3] = &s5p_port_fifo16, +}; + +/* device management */ +static int s5p_serial_probe(struct platform_device *pdev) +{ + return s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]); +} + +static struct platform_driver s5p_serial_driver = { + .probe = s5p_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s5pv210-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init s5pv210_serial_console_init(void) +{ + return s3c24xx_serial_initconsole(&s5p_serial_driver, s5p_uart_inf); +} + +console_initcall(s5pv210_serial_console_init); + +static int __init s5p_serial_init(void) +{ + return s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf); +} + +static void __exit s5p_serial_exit(void) +{ + platform_driver_unregister(&s5p_serial_driver); +} + +module_init(s5p_serial_init); +module_exit(s5p_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s5pv210-uart"); +MODULE_DESCRIPTION("Samsung S5PV210 UART Driver support"); +MODULE_AUTHOR("Thomas Abraham "); diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c new file mode 100644 index 000000000000..2199d819a987 --- /dev/null +++ b/drivers/tty/serial/sa1100.c @@ -0,0 +1,918 @@ +/* + * linux/drivers/char/sa1100.c + * + * Driver for SA11x0 serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * + * 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 + */ + +#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_SA1100_MAJOR 204 +#define MINOR_START 5 + +#define NR_PORTS 3 + +#define SA1100_ISR_PASS_LIMIT 256 + +/* + * Convert from ignore_status_mask or read_status_mask to UTSR[01] + */ +#define SM_TO_UTSR0(x) ((x) & 0xff) +#define SM_TO_UTSR1(x) ((x) >> 8) +#define UTSR0_TO_SM(x) ((x)) +#define UTSR1_TO_SM(x) ((x) << 8) + +#define UART_GET_UTCR0(sport) __raw_readl((sport)->port.membase + UTCR0) +#define UART_GET_UTCR1(sport) __raw_readl((sport)->port.membase + UTCR1) +#define UART_GET_UTCR2(sport) __raw_readl((sport)->port.membase + UTCR2) +#define UART_GET_UTCR3(sport) __raw_readl((sport)->port.membase + UTCR3) +#define UART_GET_UTSR0(sport) __raw_readl((sport)->port.membase + UTSR0) +#define UART_GET_UTSR1(sport) __raw_readl((sport)->port.membase + UTSR1) +#define UART_GET_CHAR(sport) __raw_readl((sport)->port.membase + UTDR) + +#define UART_PUT_UTCR0(sport,v) __raw_writel((v),(sport)->port.membase + UTCR0) +#define UART_PUT_UTCR1(sport,v) __raw_writel((v),(sport)->port.membase + UTCR1) +#define UART_PUT_UTCR2(sport,v) __raw_writel((v),(sport)->port.membase + UTCR2) +#define UART_PUT_UTCR3(sport,v) __raw_writel((v),(sport)->port.membase + UTCR3) +#define UART_PUT_UTSR0(sport,v) __raw_writel((v),(sport)->port.membase + UTSR0) +#define UART_PUT_UTSR1(sport,v) __raw_writel((v),(sport)->port.membase + UTSR1) +#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR) + +/* + * This is the size of our serial port register set. + */ +#define UART_PORT_SIZE 0x24 + +/* + * This determines how often we check the modem status signals + * for any change. They generally aren't connected to an IRQ + * so we have to poll them. We also check immediately before + * filling the TX fifo incase CTS has been dropped. + */ +#define MCTRL_TIMEOUT (250*HZ/1000) + +struct sa1100_port { + struct uart_port port; + struct timer_list timer; + unsigned int old_status; +}; + +/* + * Handle any change of modem status signal since we were last called. + */ +static void sa1100_mctrl_check(struct sa1100_port *sport) +{ + unsigned int status, changed; + + status = sport->port.ops->get_mctrl(&sport->port); + changed = status ^ sport->old_status; + + if (changed == 0) + return; + + sport->old_status = status; + + if (changed & TIOCM_RI) + sport->port.icount.rng++; + if (changed & TIOCM_DSR) + sport->port.icount.dsr++; + if (changed & TIOCM_CAR) + uart_handle_dcd_change(&sport->port, status & TIOCM_CAR); + if (changed & TIOCM_CTS) + uart_handle_cts_change(&sport->port, status & TIOCM_CTS); + + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); +} + +/* + * This is our per-port timeout handler, for checking the + * modem status signals. + */ +static void sa1100_timeout(unsigned long data) +{ + struct sa1100_port *sport = (struct sa1100_port *)data; + unsigned long flags; + + if (sport->port.state) { + spin_lock_irqsave(&sport->port.lock, flags); + sa1100_mctrl_check(sport); + spin_unlock_irqrestore(&sport->port.lock, flags); + + mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT); + } +} + +/* + * interrupts disabled on entry + */ +static void sa1100_stop_tx(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + u32 utcr3; + + utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE); + sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS); +} + +/* + * port locked and interrupts disabled + */ +static void sa1100_start_tx(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + u32 utcr3; + + utcr3 = UART_GET_UTCR3(sport); + sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS); + UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE); +} + +/* + * Interrupts enabled + */ +static void sa1100_stop_rx(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + u32 utcr3; + + utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE); +} + +/* + * Set the modem control timer to fire immediately. + */ +static void sa1100_enable_ms(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + mod_timer(&sport->timer, jiffies); +} + +static void +sa1100_rx_chars(struct sa1100_port *sport) +{ + struct tty_struct *tty = sport->port.state->port.tty; + unsigned int status, ch, flg; + + status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | + UTSR0_TO_SM(UART_GET_UTSR0(sport)); + while (status & UTSR1_TO_SM(UTSR1_RNE)) { + ch = UART_GET_CHAR(sport); + + sport->port.icount.rx++; + + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) { + if (status & UTSR1_TO_SM(UTSR1_PRE)) + sport->port.icount.parity++; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + sport->port.icount.frame++; + if (status & UTSR1_TO_SM(UTSR1_ROR)) + sport->port.icount.overrun++; + + status &= sport->port.read_status_mask; + + if (status & UTSR1_TO_SM(UTSR1_PRE)) + flg = TTY_PARITY; + else if (status & UTSR1_TO_SM(UTSR1_FRE)) + flg = TTY_FRAME; + +#ifdef SUPPORT_SYSRQ + sport->port.sysrq = 0; +#endif + } + + if (uart_handle_sysrq_char(&sport->port, ch)) + goto ignore_char; + + uart_insert_char(&sport->port, status, UTSR1_TO_SM(UTSR1_ROR), ch, flg); + + ignore_char: + status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | + UTSR0_TO_SM(UART_GET_UTSR0(sport)); + } + tty_flip_buffer_push(tty); +} + +static void sa1100_tx_chars(struct sa1100_port *sport) +{ + struct circ_buf *xmit = &sport->port.state->xmit; + + if (sport->port.x_char) { + UART_PUT_CHAR(sport, sport->port.x_char); + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /* + * Check the modem control lines before + * transmitting anything. + */ + sa1100_mctrl_check(sport); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { + sa1100_stop_tx(&sport->port); + return; + } + + /* + * Tried using FIFO (not checking TNF) for fifo fill: + * still had the '4 bytes repeated' problem. + */ + while (UART_GET_UTSR1(sport) & UTSR1_TNF) { + UART_PUT_CHAR(sport, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + + if (uart_circ_empty(xmit)) + sa1100_stop_tx(&sport->port); +} + +static irqreturn_t sa1100_int(int irq, void *dev_id) +{ + struct sa1100_port *sport = dev_id; + unsigned int status, pass_counter = 0; + + spin_lock(&sport->port.lock); + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS; + do { + if (status & (UTSR0_RFS | UTSR0_RID)) { + /* Clear the receiver idle bit, if set */ + if (status & UTSR0_RID) + UART_PUT_UTSR0(sport, UTSR0_RID); + sa1100_rx_chars(sport); + } + + /* Clear the relevant break bits */ + if (status & (UTSR0_RBB | UTSR0_REB)) + UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB)); + + if (status & UTSR0_RBB) + sport->port.icount.brk++; + + if (status & UTSR0_REB) + uart_handle_break(&sport->port); + + if (status & UTSR0_TFS) + sa1100_tx_chars(sport); + if (pass_counter++ > SA1100_ISR_PASS_LIMIT) + break; + status = UART_GET_UTSR0(sport); + status &= SM_TO_UTSR0(sport->port.read_status_mask) | + ~UTSR0_TFS; + } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID)); + spin_unlock(&sport->port.lock); + + return IRQ_HANDLED; +} + +/* + * Return TIOCSER_TEMT when transmitter is not busy. + */ +static unsigned int sa1100_tx_empty(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT; +} + +static unsigned int sa1100_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/* + * Interrupts always disabled. + */ +static void sa1100_break_ctl(struct uart_port *port, int break_state) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + unsigned int utcr3; + + spin_lock_irqsave(&sport->port.lock, flags); + utcr3 = UART_GET_UTCR3(sport); + if (break_state == -1) + utcr3 |= UTCR3_BRK; + else + utcr3 &= ~UTCR3_BRK; + UART_PUT_UTCR3(sport, utcr3); + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static int sa1100_startup(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(sport->port.irq, sa1100_int, 0, + "sa11x0-uart", sport); + if (retval) + return retval; + + /* + * Finally, clear and enable interrupts + */ + UART_PUT_UTSR0(sport, -1); + UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE); + + /* + * Enable modem status interrupts + */ + spin_lock_irq(&sport->port.lock); + sa1100_enable_ms(&sport->port); + spin_unlock_irq(&sport->port.lock); + + return 0; +} + +static void sa1100_shutdown(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + /* + * Stop our timer. + */ + del_timer_sync(&sport->timer); + + /* + * Free the interrupt + */ + free_irq(sport->port.irq, sport); + + /* + * Disable all interrupts, port and break condition. + */ + UART_PUT_UTCR3(sport, 0); +} + +static void +sa1100_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + unsigned long flags; + unsigned int utcr0, old_utcr3, baud, quot; + unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; + + /* + * We only support CS7 and CS8. + */ + while ((termios->c_cflag & CSIZE) != CS7 && + (termios->c_cflag & CSIZE) != CS8) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + old_csize = CS8; + } + + if ((termios->c_cflag & CSIZE) == CS8) + utcr0 = UTCR0_DSS; + else + utcr0 = 0; + + if (termios->c_cflag & CSTOPB) + utcr0 |= UTCR0_SBS; + if (termios->c_cflag & PARENB) { + utcr0 |= UTCR0_PE; + if (!(termios->c_cflag & PARODD)) + utcr0 |= UTCR0_OES; + } + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&sport->port.lock, flags); + + sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS); + sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR); + if (termios->c_iflag & INPCK) + sport->port.read_status_mask |= + UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (termios->c_iflag & (BRKINT | PARMRK)) + sport->port.read_status_mask |= + UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + + /* + * Characters to ignore + */ + sport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= + UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE); + if (termios->c_iflag & IGNBRK) { + sport->port.ignore_status_mask |= + UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB); + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + sport->port.ignore_status_mask |= + UTSR1_TO_SM(UTSR1_ROR); + } + + del_timer_sync(&sport->timer); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * disable interrupts and drain transmitter + */ + old_utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)); + + while (UART_GET_UTSR1(sport) & UTSR1_TBY) + barrier(); + + /* then, disable everything */ + UART_PUT_UTCR3(sport, 0); + + /* set the parity, stop bits and data size */ + UART_PUT_UTCR0(sport, utcr0); + + /* set the baud rate */ + quot -= 1; + UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8)); + UART_PUT_UTCR2(sport, (quot & 0xff)); + + UART_PUT_UTSR0(sport, -1); + + UART_PUT_UTCR3(sport, old_utcr3); + + if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) + sa1100_enable_ms(&sport->port); + + spin_unlock_irqrestore(&sport->port.lock, flags); +} + +static const char *sa1100_type(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return sport->port.type == PORT_SA1100 ? "SA1100" : NULL; +} + +/* + * Release the memory region(s) being used by 'port'. + */ +static void sa1100_release_port(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + release_mem_region(sport->port.mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port'. + */ +static int sa1100_request_port(struct uart_port *port) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, + "sa11x0-uart") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void sa1100_config_port(struct uart_port *port, int flags) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + if (flags & UART_CONFIG_TYPE && + sa1100_request_port(&sport->port) == 0) + sport->port.type = PORT_SA1100; +} + +/* + * Verify the new serial_struct (for TIOCSSERIAL). + * The only change we allow are to the flags and type, and + * even then only between PORT_SA1100 and PORT_UNKNOWN + */ +static int +sa1100_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100) + ret = -EINVAL; + if (sport->port.irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (sport->port.uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)sport->port.mapbase != ser->iomem_base) + ret = -EINVAL; + if (sport->port.iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; +} + +static struct uart_ops sa1100_pops = { + .tx_empty = sa1100_tx_empty, + .set_mctrl = sa1100_set_mctrl, + .get_mctrl = sa1100_get_mctrl, + .stop_tx = sa1100_stop_tx, + .start_tx = sa1100_start_tx, + .stop_rx = sa1100_stop_rx, + .enable_ms = sa1100_enable_ms, + .break_ctl = sa1100_break_ctl, + .startup = sa1100_startup, + .shutdown = sa1100_shutdown, + .set_termios = sa1100_set_termios, + .type = sa1100_type, + .release_port = sa1100_release_port, + .request_port = sa1100_request_port, + .config_port = sa1100_config_port, + .verify_port = sa1100_verify_port, +}; + +static struct sa1100_port sa1100_ports[NR_PORTS]; + +/* + * Setup the SA1100 serial ports. Note that we don't include the IrDA + * port here since we have our own SIR/FIR driver (see drivers/net/irda) + * + * Note also that we support "console=ttySAx" where "x" is either 0 or 1. + * Which serial port this ends up being depends on the machine you're + * running this kernel on. I'm not convinced that this is a good idea, + * but that's the way it traditionally works. + * + * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer + * used here. + */ +static void __init sa1100_init_ports(void) +{ + static int first = 1; + int i; + + if (!first) + return; + first = 0; + + for (i = 0; i < NR_PORTS; i++) { + sa1100_ports[i].port.uartclk = 3686400; + sa1100_ports[i].port.ops = &sa1100_pops; + sa1100_ports[i].port.fifosize = 8; + sa1100_ports[i].port.line = i; + sa1100_ports[i].port.iotype = UPIO_MEM; + init_timer(&sa1100_ports[i].timer); + sa1100_ports[i].timer.function = sa1100_timeout; + sa1100_ports[i].timer.data = (unsigned long)&sa1100_ports[i]; + } + + /* + * make transmit lines outputs, so that when the port + * is closed, the output is in the MARK state. + */ + PPDR |= PPC_TXD1 | PPC_TXD3; + PPSR |= PPC_TXD1 | PPC_TXD3; +} + +void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns) +{ + if (fns->get_mctrl) + sa1100_pops.get_mctrl = fns->get_mctrl; + if (fns->set_mctrl) + sa1100_pops.set_mctrl = fns->set_mctrl; + + sa1100_pops.pm = fns->pm; + sa1100_pops.set_wake = fns->set_wake; +} + +void __init sa1100_register_uart(int idx, int port) +{ + if (idx >= NR_PORTS) { + printk(KERN_ERR "%s: bad index number %d\n", __func__, idx); + return; + } + + switch (port) { + case 1: + sa1100_ports[idx].port.membase = (void __iomem *)&Ser1UTCR0; + sa1100_ports[idx].port.mapbase = _Ser1UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser1UART; + sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF; + break; + + case 2: + sa1100_ports[idx].port.membase = (void __iomem *)&Ser2UTCR0; + sa1100_ports[idx].port.mapbase = _Ser2UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser2ICP; + sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF; + break; + + case 3: + sa1100_ports[idx].port.membase = (void __iomem *)&Ser3UTCR0; + sa1100_ports[idx].port.mapbase = _Ser3UTCR0; + sa1100_ports[idx].port.irq = IRQ_Ser3UART; + sa1100_ports[idx].port.flags = UPF_BOOT_AUTOCONF; + break; + + default: + printk(KERN_ERR "%s: bad port number %d\n", __func__, port); + } +} + + +#ifdef CONFIG_SERIAL_SA1100_CONSOLE +static void sa1100_console_putchar(struct uart_port *port, int ch) +{ + struct sa1100_port *sport = (struct sa1100_port *)port; + + while (!(UART_GET_UTSR1(sport) & UTSR1_TNF)) + barrier(); + UART_PUT_CHAR(sport, ch); +} + +/* + * Interrupts are disabled on entering + */ +static void +sa1100_console_write(struct console *co, const char *s, unsigned int count) +{ + struct sa1100_port *sport = &sa1100_ports[co->index]; + unsigned int old_utcr3, status; + + /* + * First, save UTCR3 and then disable interrupts + */ + old_utcr3 = UART_GET_UTCR3(sport); + UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) | + UTCR3_TXE); + + uart_console_write(&sport->port, s, count, sa1100_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore UTCR3 + */ + do { + status = UART_GET_UTSR1(sport); + } while (status & UTSR1_TBY); + UART_PUT_UTCR3(sport, old_utcr3); +} + +/* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ +static void __init +sa1100_console_get_options(struct sa1100_port *sport, int *baud, + int *parity, int *bits) +{ + unsigned int utcr3; + + utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE); + if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) { + /* ok, the port was enabled */ + unsigned int utcr0, quot; + + utcr0 = UART_GET_UTCR0(sport); + + *parity = 'n'; + if (utcr0 & UTCR0_PE) { + if (utcr0 & UTCR0_OES) + *parity = 'e'; + else + *parity = 'o'; + } + + if (utcr0 & UTCR0_DSS) + *bits = 8; + else + *bits = 7; + + quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8; + quot &= 0xfff; + *baud = sport->port.uartclk / (16 * (quot + 1)); + } +} + +static int __init +sa1100_console_setup(struct console *co, char *options) +{ + struct sa1100_port *sport; + int baud = 9600; + 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. + */ + if (co->index == -1 || co->index >= NR_PORTS) + co->index = 0; + sport = &sa1100_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + sa1100_console_get_options(sport, &baud, &parity, &bits); + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver sa1100_reg; +static struct console sa1100_console = { + .name = "ttySA", + .write = sa1100_console_write, + .device = uart_console_device, + .setup = sa1100_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sa1100_reg, +}; + +static int __init sa1100_rs_console_init(void) +{ + sa1100_init_ports(); + register_console(&sa1100_console); + return 0; +} +console_initcall(sa1100_rs_console_init); + +#define SA1100_CONSOLE &sa1100_console +#else +#define SA1100_CONSOLE NULL +#endif + +static struct uart_driver sa1100_reg = { + .owner = THIS_MODULE, + .driver_name = "ttySA", + .dev_name = "ttySA", + .major = SERIAL_SA1100_MAJOR, + .minor = MINOR_START, + .nr = NR_PORTS, + .cons = SA1100_CONSOLE, +}; + +static int sa1100_serial_suspend(struct platform_device *dev, pm_message_t state) +{ + struct sa1100_port *sport = platform_get_drvdata(dev); + + if (sport) + uart_suspend_port(&sa1100_reg, &sport->port); + + return 0; +} + +static int sa1100_serial_resume(struct platform_device *dev) +{ + struct sa1100_port *sport = platform_get_drvdata(dev); + + if (sport) + uart_resume_port(&sa1100_reg, &sport->port); + + return 0; +} + +static int sa1100_serial_probe(struct platform_device *dev) +{ + struct resource *res = dev->resource; + int i; + + for (i = 0; i < dev->num_resources; i++, res++) + if (res->flags & IORESOURCE_MEM) + break; + + if (i < dev->num_resources) { + for (i = 0; i < NR_PORTS; i++) { + if (sa1100_ports[i].port.mapbase != res->start) + continue; + + sa1100_ports[i].port.dev = &dev->dev; + uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); + platform_set_drvdata(dev, &sa1100_ports[i]); + break; + } + } + + return 0; +} + +static int sa1100_serial_remove(struct platform_device *pdev) +{ + struct sa1100_port *sport = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (sport) + uart_remove_one_port(&sa1100_reg, &sport->port); + + return 0; +} + +static struct platform_driver sa11x0_serial_driver = { + .probe = sa1100_serial_probe, + .remove = sa1100_serial_remove, + .suspend = sa1100_serial_suspend, + .resume = sa1100_serial_resume, + .driver = { + .name = "sa11x0-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init sa1100_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: SA11x0 driver\n"); + + sa1100_init_ports(); + + ret = uart_register_driver(&sa1100_reg); + if (ret == 0) { + ret = platform_driver_register(&sa11x0_serial_driver); + if (ret) + uart_unregister_driver(&sa1100_reg); + } + return ret; +} + +static void __exit sa1100_serial_exit(void) +{ + platform_driver_unregister(&sa11x0_serial_driver); + uart_unregister_driver(&sa1100_reg); +} + +module_init(sa1100_serial_init); +module_exit(sa1100_serial_exit); + +MODULE_AUTHOR("Deep Blue Solutions Ltd"); +MODULE_DESCRIPTION("SA1100 generic serial port driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_SA1100_MAJOR); +MODULE_ALIAS("platform:sa11x0-uart"); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c new file mode 100644 index 000000000000..7ac2bf5167cd --- /dev/null +++ b/drivers/tty/serial/samsung.c @@ -0,0 +1,1487 @@ +/* linux/drivers/serial/samsuing.c + * + * Driver core for Samsung SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * 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. +*/ + +/* Hote on 2410 error handling + * + * The s3c2410 manual has a love/hate affair with the contents of the + * UERSTAT register in the UART blocks, and keeps marking some of the + * error bits as reserved. Having checked with the s3c2410x01, + * it copes with BREAKs properly, so I am happy to ignore the RESERVED + * feature from the latter versions of the manual. + * + * If it becomes aparrent that latter versions of the 2410 remove these + * bits, then action will have to be taken to differentiate the versions + * and change the policy on BREAK + * + * BJD, 04-Nov-2004 +*/ + +#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "samsung.h" + +/* UART name and device definitions */ + +#define S3C24XX_SERIAL_NAME "ttySAC" +#define S3C24XX_SERIAL_MAJOR 204 +#define S3C24XX_SERIAL_MINOR 64 + +/* macros to change one thing to another */ + +#define tx_enabled(port) ((port)->unused[0]) +#define rx_enabled(port) ((port)->unused[1]) + +/* flag to ignore all characters comming in */ +#define RXSTAT_DUMMY_READ (0x10000000) + +static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port) +{ + return container_of(port, struct s3c24xx_uart_port, port); +} + +/* translate a port to the device name */ + +static inline const char *s3c24xx_serial_portname(struct uart_port *port) +{ + return to_platform_device(port->dev)->name; +} + +static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) +{ + return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); +} + +static void s3c24xx_serial_rx_enable(struct uart_port *port) +{ + unsigned long flags; + unsigned int ucon, ufcon; + int count = 10000; + + spin_lock_irqsave(&port->lock, flags); + + while (--count && !s3c24xx_serial_txempty_nofifo(port)) + udelay(100); + + ufcon = rd_regl(port, S3C2410_UFCON); + ufcon |= S3C2410_UFCON_RESETRX; + wr_regl(port, S3C2410_UFCON, ufcon); + + ucon = rd_regl(port, S3C2410_UCON); + ucon |= S3C2410_UCON_RXIRQMODE; + wr_regl(port, S3C2410_UCON, ucon); + + rx_enabled(port) = 1; + spin_unlock_irqrestore(&port->lock, flags); +} + +static void s3c24xx_serial_rx_disable(struct uart_port *port) +{ + unsigned long flags; + unsigned int ucon; + + spin_lock_irqsave(&port->lock, flags); + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~S3C2410_UCON_RXIRQMODE; + wr_regl(port, S3C2410_UCON, ucon); + + rx_enabled(port) = 0; + spin_unlock_irqrestore(&port->lock, flags); +} + +static void s3c24xx_serial_stop_tx(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (tx_enabled(port)) { + disable_irq_nosync(ourport->tx_irq); + tx_enabled(port) = 0; + if (port->flags & UPF_CONS_FLOW) + s3c24xx_serial_rx_enable(port); + } +} + +static void s3c24xx_serial_start_tx(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (!tx_enabled(port)) { + if (port->flags & UPF_CONS_FLOW) + s3c24xx_serial_rx_disable(port); + + enable_irq(ourport->tx_irq); + tx_enabled(port) = 1; + } +} + + +static void s3c24xx_serial_stop_rx(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (rx_enabled(port)) { + dbg("s3c24xx_serial_stop_rx: port=%p\n", port); + disable_irq_nosync(ourport->rx_irq); + rx_enabled(port) = 0; + } +} + +static void s3c24xx_serial_enable_ms(struct uart_port *port) +{ +} + +static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port) +{ + return to_ourport(port)->info; +} + +static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port) +{ + if (port->dev == NULL) + return NULL; + + return (struct s3c2410_uartcfg *)port->dev->platform_data; +} + +static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport, + unsigned long ufstat) +{ + struct s3c24xx_uart_info *info = ourport->info; + + if (ufstat & info->rx_fifofull) + return info->fifosize; + + return (ufstat & info->rx_fifomask) >> info->rx_fifoshift; +} + + +/* ? - where has parity gone?? */ +#define S3C2410_UERSTAT_PARITY (0x1000) + +static irqreturn_t +s3c24xx_serial_rx_chars(int irq, void *dev_id) +{ + struct s3c24xx_uart_port *ourport = dev_id; + struct uart_port *port = &ourport->port; + struct tty_struct *tty = port->state->port.tty; + unsigned int ufcon, ch, flag, ufstat, uerstat; + int max_count = 64; + + while (max_count-- > 0) { + ufcon = rd_regl(port, S3C2410_UFCON); + ufstat = rd_regl(port, S3C2410_UFSTAT); + + if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) + break; + + uerstat = rd_regl(port, S3C2410_UERSTAT); + ch = rd_regb(port, S3C2410_URXH); + + if (port->flags & UPF_CONS_FLOW) { + int txe = s3c24xx_serial_txempty_nofifo(port); + + if (rx_enabled(port)) { + if (!txe) { + rx_enabled(port) = 0; + continue; + } + } else { + if (txe) { + ufcon |= S3C2410_UFCON_RESETRX; + wr_regl(port, S3C2410_UFCON, ufcon); + rx_enabled(port) = 1; + goto out; + } + continue; + } + } + + /* insert the character into the buffer */ + + flag = TTY_NORMAL; + port->icount.rx++; + + if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) { + dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n", + ch, uerstat); + + /* check for break */ + if (uerstat & S3C2410_UERSTAT_BREAK) { + dbg("break!\n"); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } + + if (uerstat & S3C2410_UERSTAT_FRAME) + port->icount.frame++; + if (uerstat & S3C2410_UERSTAT_OVERRUN) + port->icount.overrun++; + + uerstat &= port->read_status_mask; + + if (uerstat & S3C2410_UERSTAT_BREAK) + flag = TTY_BREAK; + else if (uerstat & S3C2410_UERSTAT_PARITY) + flag = TTY_PARITY; + else if (uerstat & (S3C2410_UERSTAT_FRAME | + S3C2410_UERSTAT_OVERRUN)) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, + ch, flag); + + ignore_char: + continue; + } + tty_flip_buffer_push(tty); + + out: + return IRQ_HANDLED; +} + +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; + int count = 256; + + if (port->x_char) { + wr_regb(port, S3C2410_UTXH, port->x_char); + port->icount.tx++; + port->x_char = 0; + goto out; + } + + /* if there isnt anything more to transmit, or the uart is now + * stopped, disable the uart and exit + */ + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + s3c24xx_serial_stop_tx(port); + goto out; + } + + /* try and drain the buffer... */ + + while (!uart_circ_empty(xmit) && count-- > 0) { + if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) + break; + + wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + s3c24xx_serial_stop_tx(port); + + out: + return IRQ_HANDLED; +} + +static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); + unsigned long ufcon = rd_regl(port, S3C2410_UFCON); + + if (ufcon & S3C2410_UFCON_FIFOMODE) { + if ((ufstat & info->tx_fifomask) != 0 || + (ufstat & info->tx_fifofull)) + return 0; + + return 1; + } + + return s3c24xx_serial_txempty_nofifo(port); +} + +/* no modem control lines */ +static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) +{ + unsigned int umstat = rd_regb(port, S3C2410_UMSTAT); + + if (umstat & S3C2410_UMSTAT_CTS) + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; + else + return TIOCM_CAR | TIOCM_DSR; +} + +static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* todo - possibly remove AFC and do manual CTS */ +} + +static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned int ucon; + + spin_lock_irqsave(&port->lock, flags); + + ucon = rd_regl(port, S3C2410_UCON); + + if (break_state) + ucon |= S3C2410_UCON_SBREAK; + else + ucon &= ~S3C2410_UCON_SBREAK; + + wr_regl(port, S3C2410_UCON, ucon); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void s3c24xx_serial_shutdown(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (ourport->tx_claimed) { + free_irq(ourport->tx_irq, ourport); + tx_enabled(port) = 0; + ourport->tx_claimed = 0; + } + + if (ourport->rx_claimed) { + free_irq(ourport->rx_irq, ourport); + ourport->rx_claimed = 0; + rx_enabled(port) = 0; + } +} + + +static int s3c24xx_serial_startup(struct uart_port *port) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + int ret; + + dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", + port->mapbase, port->membase); + + rx_enabled(port) = 1; + + ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, + s3c24xx_serial_portname(port), ourport); + + if (ret != 0) { + printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); + return ret; + } + + ourport->rx_claimed = 1; + + dbg("requesting tx irq...\n"); + + tx_enabled(port) = 1; + + ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, + s3c24xx_serial_portname(port), ourport); + + if (ret) { + printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); + goto err; + } + + ourport->tx_claimed = 1; + + dbg("s3c24xx_serial_startup ok\n"); + + /* the port reset code should have done the correct + * register setup for the port controls */ + + return ret; + + err: + s3c24xx_serial_shutdown(port); + return ret; +} + +/* power power management control */ + +static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, + unsigned int old) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + + ourport->pm_level = level; + + switch (level) { + case 3: + if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + clk_disable(ourport->baudclk); + + clk_disable(ourport->clk); + break; + + case 0: + clk_enable(ourport->clk); + + if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + clk_enable(ourport->baudclk); + + break; + default: + printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level); + } +} + +/* baud rate calculation + * + * The UARTs on the S3C2410/S3C2440 can take their clocks from a number + * of different sources, including the peripheral clock ("pclk") and an + * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk") + * with a programmable extra divisor. + * + * The following code goes through the clock sources, and calculates the + * baud clocks (and the resultant actual baud rates) and then tries to + * pick the closest one and select that. + * +*/ + + +#define MAX_CLKS (8) + +static struct s3c24xx_uart_clksrc tmp_clksrc = { + .name = "pclk", + .min_baud = 0, + .max_baud = 0, + .divisor = 1, +}; + +static inline int +s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + return (info->get_clksrc)(port, c); +} + +static inline int +s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + return (info->set_clksrc)(port, c); +} + +struct baud_calc { + struct s3c24xx_uart_clksrc *clksrc; + unsigned int calc; + unsigned int divslot; + unsigned int quot; + struct clk *src; +}; + +static int s3c24xx_serial_calcbaud(struct baud_calc *calc, + struct uart_port *port, + struct s3c24xx_uart_clksrc *clksrc, + unsigned int baud) +{ + struct s3c24xx_uart_port *ourport = to_ourport(port); + unsigned long rate; + + calc->src = clk_get(port->dev, clksrc->name); + if (calc->src == NULL || IS_ERR(calc->src)) + return 0; + + rate = clk_get_rate(calc->src); + rate /= clksrc->divisor; + + calc->clksrc = clksrc; + + if (ourport->info->has_divslot) { + unsigned long div = rate / baud; + + /* The UDIVSLOT register on the newer UARTs allows us to + * get a divisor adjustment of 1/16th on the baud clock. + * + * We don't keep the UDIVSLOT value (the 16ths we calculated + * by not multiplying the baud by 16) as it is easy enough + * to recalculate. + */ + + calc->quot = div / 16; + calc->calc = rate / div; + } else { + calc->quot = (rate + (8 * baud)) / (16 * baud); + calc->calc = (rate / (calc->quot * 16)); + } + + calc->quot--; + return 1; +} + +static unsigned int s3c24xx_serial_getclk(struct uart_port *port, + struct s3c24xx_uart_clksrc **clksrc, + struct clk **clk, + unsigned int baud) +{ + struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); + struct s3c24xx_uart_clksrc *clkp; + struct baud_calc res[MAX_CLKS]; + struct baud_calc *resptr, *best, *sptr; + int i; + + clkp = cfg->clocks; + best = NULL; + + if (cfg->clocks_size < 2) { + if (cfg->clocks_size == 0) + clkp = &tmp_clksrc; + + /* check to see if we're sourcing fclk, and if so we're + * going to have to update the clock source + */ + + if (strcmp(clkp->name, "fclk") == 0) { + struct s3c24xx_uart_clksrc src; + + s3c24xx_serial_getsource(port, &src); + + /* check that the port already using fclk, and if + * not, then re-select fclk + */ + + if (strcmp(src.name, clkp->name) == 0) { + s3c24xx_serial_setsource(port, clkp); + s3c24xx_serial_getsource(port, &src); + } + + clkp->divisor = src.divisor; + } + + s3c24xx_serial_calcbaud(res, port, clkp, baud); + best = res; + resptr = best + 1; + } else { + resptr = res; + + for (i = 0; i < cfg->clocks_size; i++, clkp++) { + if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud)) + resptr++; + } + } + + /* ok, we now need to select the best clock we found */ + + if (!best) { + unsigned int deviation = (1<<30)|((1<<30)-1); + int calc_deviation; + + for (sptr = res; sptr < resptr; sptr++) { + calc_deviation = baud - sptr->calc; + if (calc_deviation < 0) + calc_deviation = -calc_deviation; + + if (calc_deviation < deviation) { + best = sptr; + deviation = calc_deviation; + } + } + } + + /* store results to pass back */ + + *clksrc = best->clksrc; + *clk = best->src; + + return best->quot; +} + +/* udivslot_table[] + * + * This table takes the fractional value of the baud divisor and gives + * the recommended setting for the UDIVSLOT register. + */ +static u16 udivslot_table[16] = { + [0] = 0x0000, + [1] = 0x0080, + [2] = 0x0808, + [3] = 0x0888, + [4] = 0x2222, + [5] = 0x4924, + [6] = 0x4A52, + [7] = 0x54AA, + [8] = 0x5555, + [9] = 0xD555, + [10] = 0xD5D5, + [11] = 0xDDD5, + [12] = 0xDDDD, + [13] = 0xDFDD, + [14] = 0xDFDF, + [15] = 0xFFDF, +}; + +static void s3c24xx_serial_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); + struct s3c24xx_uart_port *ourport = to_ourport(port); + struct s3c24xx_uart_clksrc *clksrc = NULL; + struct clk *clk = NULL; + unsigned long flags; + unsigned int baud, quot; + unsigned int ulcon; + unsigned int umcon; + unsigned int udivslot = 0; + + /* + * We don't support modem control lines. + */ + termios->c_cflag &= ~(HUPCL | CMSPAR); + termios->c_cflag |= CLOCAL; + + /* + * Ask the core to calculate the divisor for us. + */ + + baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); + + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + quot = port->custom_divisor; + else + quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); + + /* check to see if we need to change clock source */ + + if (ourport->clksrc != clksrc || ourport->baudclk != clk) { + dbg("selecting clock %p\n", clk); + s3c24xx_serial_setsource(port, clksrc); + + if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { + clk_disable(ourport->baudclk); + ourport->baudclk = NULL; + } + + clk_enable(clk); + + ourport->clksrc = clksrc; + ourport->baudclk = clk; + ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; + } + + if (ourport->info->has_divslot) { + unsigned int div = ourport->baudclk_rate / baud; + + if (cfg->has_fracval) { + udivslot = (div & 15); + dbg("fracval = %04x\n", udivslot); + } else { + udivslot = udivslot_table[div & 15]; + dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); + } + } + + switch (termios->c_cflag & CSIZE) { + case CS5: + dbg("config: 5bits/char\n"); + ulcon = S3C2410_LCON_CS5; + break; + case CS6: + dbg("config: 6bits/char\n"); + ulcon = S3C2410_LCON_CS6; + break; + case CS7: + dbg("config: 7bits/char\n"); + ulcon = S3C2410_LCON_CS7; + break; + case CS8: + default: + dbg("config: 8bits/char\n"); + ulcon = S3C2410_LCON_CS8; + break; + } + + /* preserve original lcon IR settings */ + ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); + + if (termios->c_cflag & CSTOPB) + ulcon |= S3C2410_LCON_STOPB; + + umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; + + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + ulcon |= S3C2410_LCON_PODD; + else + ulcon |= S3C2410_LCON_PEVEN; + } else { + ulcon |= S3C2410_LCON_PNONE; + } + + spin_lock_irqsave(&port->lock, flags); + + dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n", + ulcon, quot, udivslot); + + wr_regl(port, S3C2410_ULCON, ulcon); + wr_regl(port, S3C2410_UBRDIV, quot); + wr_regl(port, S3C2410_UMCON, umcon); + + if (ourport->info->has_divslot) + wr_regl(port, S3C2443_DIVSLOT, udivslot); + + dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", + rd_regl(port, S3C2410_ULCON), + rd_regl(port, S3C2410_UCON), + rd_regl(port, S3C2410_UFCON)); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Which character status flags are we interested in? + */ + port->read_status_mask = S3C2410_UERSTAT_OVERRUN; + if (termios->c_iflag & INPCK) + port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; + + /* + * Which character status flags should we ignore? + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN; + if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) + port->ignore_status_mask |= S3C2410_UERSTAT_FRAME; + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= RXSTAT_DUMMY_READ; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *s3c24xx_serial_type(struct uart_port *port) +{ + switch (port->type) { + case PORT_S3C2410: + return "S3C2410"; + case PORT_S3C2440: + return "S3C2440"; + case PORT_S3C2412: + return "S3C2412"; + case PORT_S3C6400: + return "S3C6400/10"; + default: + return NULL; + } +} + +#define MAP_SIZE (0x100) + +static void s3c24xx_serial_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, MAP_SIZE); +} + +static int s3c24xx_serial_request_port(struct uart_port *port) +{ + const char *name = s3c24xx_serial_portname(port); + return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; +} + +static void s3c24xx_serial_config_port(struct uart_port *port, int flags) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + if (flags & UART_CONFIG_TYPE && + s3c24xx_serial_request_port(port) == 0) + port->type = info->type; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int +s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + if (ser->type != PORT_UNKNOWN && ser->type != info->type) + return -EINVAL; + + return 0; +} + + +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + +static struct console s3c24xx_serial_console; + +#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console +#else +#define S3C24XX_SERIAL_CONSOLE NULL +#endif + +static struct uart_ops s3c24xx_serial_ops = { + .pm = s3c24xx_serial_pm, + .tx_empty = s3c24xx_serial_tx_empty, + .get_mctrl = s3c24xx_serial_get_mctrl, + .set_mctrl = s3c24xx_serial_set_mctrl, + .stop_tx = s3c24xx_serial_stop_tx, + .start_tx = s3c24xx_serial_start_tx, + .stop_rx = s3c24xx_serial_stop_rx, + .enable_ms = s3c24xx_serial_enable_ms, + .break_ctl = s3c24xx_serial_break_ctl, + .startup = s3c24xx_serial_startup, + .shutdown = s3c24xx_serial_shutdown, + .set_termios = s3c24xx_serial_set_termios, + .type = s3c24xx_serial_type, + .release_port = s3c24xx_serial_release_port, + .request_port = s3c24xx_serial_request_port, + .config_port = s3c24xx_serial_config_port, + .verify_port = s3c24xx_serial_verify_port, +}; + + +static struct uart_driver s3c24xx_uart_drv = { + .owner = THIS_MODULE, + .dev_name = "s3c2410_serial", + .nr = CONFIG_SERIAL_SAMSUNG_UARTS, + .cons = S3C24XX_SERIAL_CONSOLE, + .driver_name = S3C24XX_SERIAL_NAME, + .major = S3C24XX_SERIAL_MAJOR, + .minor = S3C24XX_SERIAL_MINOR, +}; + +static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { + [0] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX0, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + } + }, + [1] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX1, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, + } + }, +#if CONFIG_SERIAL_SAMSUNG_UARTS > 2 + + [2] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX2, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, + } + }, +#endif +#if CONFIG_SERIAL_SAMSUNG_UARTS > 3 + [3] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX3, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 3, + } + } +#endif +}; + +/* s3c24xx_serial_resetport + * + * wrapper to call the specific reset for this port (reset the fifos + * and the settings) +*/ + +static inline int s3c24xx_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + return (info->reset_port)(port, cfg); +} + + +#ifdef CONFIG_CPU_FREQ + +static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c24xx_uart_port *port; + struct uart_port *uport; + + port = container_of(nb, struct s3c24xx_uart_port, freq_transition); + uport = &port->port; + + /* check to see if port is enabled */ + + if (port->pm_level != 0) + return 0; + + /* try and work out if the baudrate is changing, we can detect + * a change in rate, but we do not have support for detecting + * a disturbance in the clock-rate over the change. + */ + + if (IS_ERR(port->clk)) + goto exit; + + if (port->baudclk_rate == clk_get_rate(port->clk)) + goto exit; + + if (val == CPUFREQ_PRECHANGE) { + /* we should really shut the port down whilst the + * frequency change is in progress. */ + + } else if (val == CPUFREQ_POSTCHANGE) { + struct ktermios *termios; + struct tty_struct *tty; + + if (uport->state == NULL) + goto exit; + + tty = uport->state->port.tty; + + if (tty == NULL) + goto exit; + + termios = tty->termios; + + if (termios == NULL) { + printk(KERN_WARNING "%s: no termios?\n", __func__); + goto exit; + } + + s3c24xx_serial_set_termios(uport, termios, NULL); + } + + exit: + return 0; +} + +static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) +{ + port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; + + return cpufreq_register_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) +{ + cpufreq_unregister_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) +{ + return 0; +} + +static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) +{ +} +#endif + +/* s3c24xx_serial_init_port + * + * initialise a single serial port from the platform device given + */ + +static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, + struct s3c24xx_uart_info *info, + struct platform_device *platdev) +{ + struct uart_port *port = &ourport->port; + struct s3c2410_uartcfg *cfg; + struct resource *res; + int ret; + + dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev); + + if (platdev == NULL) + return -ENODEV; + + cfg = s3c24xx_dev_to_cfg(&platdev->dev); + + if (port->mapbase != 0) + return 0; + + if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) { + printk(KERN_ERR "%s: port %d bigger than %d\n", __func__, + cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS); + return -ERANGE; + } + + /* setup info for port */ + port->dev = &platdev->dev; + ourport->info = info; + + /* copy the info in from provided structure */ + ourport->port.fifosize = info->fifosize; + + dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport); + + port->uartclk = 1; + + if (cfg->uart_flags & UPF_CONS_FLOW) { + dbg("s3c24xx_serial_init_port: enabling flow control\n"); + port->flags |= UPF_CONS_FLOW; + } + + /* sort our the physical and virtual addresses for each UART */ + + res = platform_get_resource(platdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk(KERN_ERR "failed to find memory resource for uart\n"); + return -EINVAL; + } + + dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); + + port->mapbase = res->start; + port->membase = S3C_VA_UART + (res->start & 0xfffff); + ret = platform_get_irq(platdev, 0); + if (ret < 0) + port->irq = 0; + else { + port->irq = ret; + ourport->rx_irq = ret; + ourport->tx_irq = ret + 1; + } + + ret = platform_get_irq(platdev, 1); + if (ret > 0) + ourport->tx_irq = ret; + + ourport->clk = clk_get(&platdev->dev, "uart"); + + dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", + port->mapbase, port->membase, port->irq, + ourport->rx_irq, ourport->tx_irq, port->uartclk); + + /* reset the fifos (and setup the uart) */ + s3c24xx_serial_resetport(port, cfg); + return 0; +} + +static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uart_port *port = s3c24xx_dev_to_port(dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); + + return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name); +} + +static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); + +/* Device driver serial port probe */ + +static int probe_index; + +int s3c24xx_serial_probe(struct platform_device *dev, + struct s3c24xx_uart_info *info) +{ + struct s3c24xx_uart_port *ourport; + int ret; + + dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); + + ourport = &s3c24xx_serial_ports[probe_index]; + probe_index++; + + dbg("%s: initialising port %p...\n", __func__, ourport); + + ret = s3c24xx_serial_init_port(ourport, info, dev); + if (ret < 0) + goto probe_err; + + dbg("%s: adding port\n", __func__); + uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); + platform_set_drvdata(dev, &ourport->port); + + ret = device_create_file(&dev->dev, &dev_attr_clock_source); + if (ret < 0) + printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); + + ret = s3c24xx_serial_cpufreq_register(ourport); + if (ret < 0) + dev_err(&dev->dev, "failed to add cpufreq notifier\n"); + + return 0; + + probe_err: + return ret; +} + +EXPORT_SYMBOL_GPL(s3c24xx_serial_probe); + +int __devexit s3c24xx_serial_remove(struct platform_device *dev) +{ + struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + + if (port) { + s3c24xx_serial_cpufreq_deregister(to_ourport(port)); + device_remove_file(&dev->dev, &dev_attr_clock_source); + uart_remove_one_port(&s3c24xx_uart_drv, port); + } + + return 0; +} + +EXPORT_SYMBOL_GPL(s3c24xx_serial_remove); + +/* UART power management code */ + +#ifdef CONFIG_PM + +static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state) +{ + struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + + if (port) + uart_suspend_port(&s3c24xx_uart_drv, port); + + return 0; +} + +static int s3c24xx_serial_resume(struct platform_device *dev) +{ + struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (port) { + clk_enable(ourport->clk); + s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); + clk_disable(ourport->clk); + + uart_resume_port(&s3c24xx_uart_drv, port); + } + + return 0; +} +#endif + +int s3c24xx_serial_init(struct platform_driver *drv, + struct s3c24xx_uart_info *info) +{ + dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); + +#ifdef CONFIG_PM + drv->suspend = s3c24xx_serial_suspend; + drv->resume = s3c24xx_serial_resume; +#endif + + return platform_driver_register(drv); +} + +EXPORT_SYMBOL_GPL(s3c24xx_serial_init); + +/* module initialisation code */ + +static int __init s3c24xx_serial_modinit(void) +{ + int ret; + + ret = uart_register_driver(&s3c24xx_uart_drv); + if (ret < 0) { + printk(KERN_ERR "failed to register UART driver\n"); + return -1; + } + + return 0; +} + +static void __exit s3c24xx_serial_modexit(void) +{ + uart_unregister_driver(&s3c24xx_uart_drv); +} + +module_init(s3c24xx_serial_modinit); +module_exit(s3c24xx_serial_modexit); + +/* Console code */ + +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + +static struct uart_port *cons_uart; + +static int +s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) +{ + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + unsigned long ufstat, utrstat; + + if (ufcon & S3C2410_UFCON_FIFOMODE) { + /* fifo mode - check amount of data in fifo registers... */ + + ufstat = rd_regl(port, S3C2410_UFSTAT); + return (ufstat & info->tx_fifofull) ? 0 : 1; + } + + /* in non-fifo mode, we go and use the tx buffer empty */ + + utrstat = rd_regl(port, S3C2410_UTRSTAT); + return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; +} + +static void +s3c24xx_serial_console_putchar(struct uart_port *port, int ch) +{ + unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + while (!s3c24xx_serial_console_txrdy(port, ufcon)) + barrier(); + wr_regb(cons_uart, S3C2410_UTXH, ch); +} + +static void +s3c24xx_serial_console_write(struct console *co, const char *s, + unsigned int count) +{ + uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); +} + +static void __init +s3c24xx_serial_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + struct s3c24xx_uart_clksrc clksrc; + struct clk *clk; + unsigned int ulcon; + unsigned int ucon; + unsigned int ubrdiv; + unsigned long rate; + + ulcon = rd_regl(port, S3C2410_ULCON); + ucon = rd_regl(port, S3C2410_UCON); + ubrdiv = rd_regl(port, S3C2410_UBRDIV); + + dbg("s3c24xx_serial_get_options: port=%p\n" + "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", + port, ulcon, ucon, ubrdiv); + + if ((ucon & 0xf) != 0) { + /* consider the serial port configured if the tx/rx mode set */ + + switch (ulcon & S3C2410_LCON_CSMASK) { + case S3C2410_LCON_CS5: + *bits = 5; + break; + case S3C2410_LCON_CS6: + *bits = 6; + break; + case S3C2410_LCON_CS7: + *bits = 7; + break; + default: + case S3C2410_LCON_CS8: + *bits = 8; + break; + } + + switch (ulcon & S3C2410_LCON_PMASK) { + case S3C2410_LCON_PEVEN: + *parity = 'e'; + break; + + case S3C2410_LCON_PODD: + *parity = 'o'; + break; + + case S3C2410_LCON_PNONE: + default: + *parity = 'n'; + } + + /* now calculate the baud rate */ + + s3c24xx_serial_getsource(port, &clksrc); + + clk = clk_get(port->dev, clksrc.name); + if (!IS_ERR(clk) && clk != NULL) + rate = clk_get_rate(clk) / clksrc.divisor; + else + rate = 1; + + + *baud = rate / (16 * (ubrdiv + 1)); + dbg("calculated baud %d\n", *baud); + } + +} + +/* s3c24xx_serial_init_ports + * + * initialise the serial ports from the machine provided initialisation + * data. +*/ + +static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) +{ + struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; + struct platform_device **platdev_ptr; + int i; + + dbg("s3c24xx_serial_init_ports: initialising ports...\n"); + + platdev_ptr = s3c24xx_uart_devs; + + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { + s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); + } + + return 0; +} + +static int __init +s3c24xx_serial_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n", + co, co->index, options); + + /* is this a valid port */ + + if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS) + co->index = 0; + + port = &s3c24xx_serial_ports[co->index].port; + + /* is the port configured? */ + + if (port->mapbase == 0x0) { + co->index = 0; + port = &s3c24xx_serial_ports[co->index].port; + } + + cons_uart = port; + + dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index); + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + s3c24xx_serial_get_options(port, &baud, &parity, &bits); + + dbg("s3c24xx_serial_console_setup: baud %d\n", baud); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +/* s3c24xx_serial_initconsole + * + * initialise the console from one of the uart drivers +*/ + +static struct console s3c24xx_serial_console = { + .name = S3C24XX_SERIAL_NAME, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .write = s3c24xx_serial_console_write, + .setup = s3c24xx_serial_console_setup +}; + +int s3c24xx_serial_initconsole(struct platform_driver *drv, + struct s3c24xx_uart_info **info) + +{ + struct platform_device *dev = s3c24xx_uart_devs[0]; + + dbg("s3c24xx_serial_initconsole\n"); + + /* select driver based on the cpu */ + + if (dev == NULL) { + printk(KERN_ERR "s3c24xx: no devices for console init\n"); + return 0; + } + + if (strcmp(dev->name, drv->driver.name) != 0) + return 0; + + s3c24xx_serial_console.data = &s3c24xx_uart_drv; + s3c24xx_serial_init_ports(info); + + register_console(&s3c24xx_serial_console); + return 0; +} + +#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */ + +MODULE_DESCRIPTION("Samsung SoC Serial port driver"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h new file mode 100644 index 000000000000..0ac06a07d25f --- /dev/null +++ b/drivers/tty/serial/samsung.h @@ -0,0 +1,120 @@ +/* linux/drivers/serial/samsung.h + * + * Driver for Samsung SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * 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. +*/ + +struct s3c24xx_uart_info { + char *name; + unsigned int type; + unsigned int fifosize; + unsigned long rx_fifomask; + unsigned long rx_fifoshift; + unsigned long rx_fifofull; + unsigned long tx_fifomask; + unsigned long tx_fifoshift; + unsigned long tx_fifofull; + + /* uart port features */ + + unsigned int has_divslot:1; + + /* clock source control */ + + int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); + int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk); + + /* uart controls */ + int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *); +}; + +struct s3c24xx_uart_port { + unsigned char rx_claimed; + unsigned char tx_claimed; + unsigned int pm_level; + unsigned long baudclk_rate; + + unsigned int rx_irq; + unsigned int tx_irq; + + struct s3c24xx_uart_info *info; + struct s3c24xx_uart_clksrc *clksrc; + struct clk *clk; + struct clk *baudclk; + struct uart_port port; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif +}; + +/* conversion functions */ + +#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev) +#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data) + +/* register access controls */ + +#define portaddr(port, reg) ((port)->membase + (reg)) + +#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg))) +#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) + +#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg)) +#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg)) + +extern int s3c24xx_serial_probe(struct platform_device *dev, + struct s3c24xx_uart_info *uart); + +extern int __devexit s3c24xx_serial_remove(struct platform_device *dev); + +extern int s3c24xx_serial_initconsole(struct platform_driver *drv, + struct s3c24xx_uart_info **uart); + +extern int s3c24xx_serial_init(struct platform_driver *drv, + struct s3c24xx_uart_info *info); + +#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + +#define s3c24xx_console_init(__drv, __inf) \ +static int __init s3c_serial_console_init(void) \ +{ \ + struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \ + int i; \ + \ + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \ + uinfo[i] = __inf; \ + return s3c24xx_serial_initconsole(__drv, uinfo); \ +} \ + \ +console_initcall(s3c_serial_console_init) + +#else +#define s3c24xx_console_init(drv, inf) extern void no_console(void) +#endif + +#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG + +extern void printascii(const char *); + +static void dbg(const char *fmt, ...) +{ + va_list va; + char buff[256]; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + printascii(buff); +} + +#else +#define dbg(x...) do { } while (0) +#endif diff --git a/drivers/tty/serial/sb1250-duart.c b/drivers/tty/serial/sb1250-duart.c new file mode 100644 index 000000000000..a2f2b3254499 --- /dev/null +++ b/drivers/tty/serial/sb1250-duart.c @@ -0,0 +1,976 @@ +/* + * drivers/serial/sb1250-duart.c + * + * Support for the asynchronous serial interface (DUART) included + * in the BCM1250 and derived System-On-a-Chip (SOC) devices. + * + * Copyright (c) 2007 Maciej W. Rozycki + * + * Derived from drivers/char/sb1250_duart.c for which the following + * copyright applies: + * + * Copyright (c) 2000, 2001, 2002, 2003, 2004 Broadcom Corporation + * + * 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. + * + * References: + * + * "BCM1250/BCM1125/BCM1125H User Manual", Broadcom Corporation + */ + +#if defined(CONFIG_SERIAL_SB1250_DUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) +#include +#include + +#define SBD_CHANREGS(line) A_BCM1480_DUART_CHANREG((line), 0) +#define SBD_CTRLREGS(line) A_BCM1480_DUART_CTRLREG((line), 0) +#define SBD_INT(line) (K_BCM1480_INT_UART_0 + (line)) + +#define DUART_CHANREG_SPACING BCM1480_DUART_CHANREG_SPACING + +#define R_DUART_IMRREG(line) R_BCM1480_DUART_IMRREG(line) +#define R_DUART_INCHREG(line) R_BCM1480_DUART_INCHREG(line) +#define R_DUART_ISRREG(line) R_BCM1480_DUART_ISRREG(line) + +#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X) +#include +#include + +#define SBD_CHANREGS(line) A_DUART_CHANREG((line), 0) +#define SBD_CTRLREGS(line) A_DUART_CTRLREG(0) +#define SBD_INT(line) (K_INT_UART_0 + (line)) + +#else +#error invalid SB1250 UART configuration + +#endif + + +MODULE_AUTHOR("Maciej W. Rozycki "); +MODULE_DESCRIPTION("BCM1xxx on-chip DUART serial driver"); +MODULE_LICENSE("GPL"); + + +#define DUART_MAX_CHIP 2 +#define DUART_MAX_SIDE 2 + +/* + * Per-port state. + */ +struct sbd_port { + struct sbd_duart *duart; + struct uart_port port; + unsigned char __iomem *memctrl; + int tx_stopped; + int initialised; +}; + +/* + * Per-DUART state for the shared register space. + */ +struct sbd_duart { + struct sbd_port sport[2]; + unsigned long mapctrl; + atomic_t map_guard; +}; + +#define to_sport(uport) container_of(uport, struct sbd_port, port) + +static struct sbd_duart sbd_duarts[DUART_MAX_CHIP]; + + +/* + * Reading and writing SB1250 DUART registers. + * + * There are three register spaces: two per-channel ones and + * a shared one. We have to define accessors appropriately. + * All registers are 64-bit and all but the Baud Rate Clock + * registers only define 8 least significant bits. There is + * also a workaround to take into account. Raw accessors use + * the full register width, but cooked ones truncate it + * intentionally so that the rest of the driver does not care. + */ +static u64 __read_sbdchn(struct sbd_port *sport, int reg) +{ + void __iomem *csr = sport->port.membase + reg; + + return __raw_readq(csr); +} + +static u64 __read_sbdshr(struct sbd_port *sport, int reg) +{ + void __iomem *csr = sport->memctrl + reg; + + return __raw_readq(csr); +} + +static void __write_sbdchn(struct sbd_port *sport, int reg, u64 value) +{ + void __iomem *csr = sport->port.membase + reg; + + __raw_writeq(value, csr); +} + +static void __write_sbdshr(struct sbd_port *sport, int reg, u64 value) +{ + void __iomem *csr = sport->memctrl + reg; + + __raw_writeq(value, csr); +} + +/* + * In bug 1956, we get glitches that can mess up uart registers. This + * "read-mode-reg after any register access" is an accepted workaround. + */ +static void __war_sbd1956(struct sbd_port *sport) +{ + __read_sbdchn(sport, R_DUART_MODE_REG_1); + __read_sbdchn(sport, R_DUART_MODE_REG_2); +} + +static unsigned char read_sbdchn(struct sbd_port *sport, int reg) +{ + unsigned char retval; + + retval = __read_sbdchn(sport, reg); + if (SIBYTE_1956_WAR) + __war_sbd1956(sport); + return retval; +} + +static unsigned char read_sbdshr(struct sbd_port *sport, int reg) +{ + unsigned char retval; + + retval = __read_sbdshr(sport, reg); + if (SIBYTE_1956_WAR) + __war_sbd1956(sport); + return retval; +} + +static void write_sbdchn(struct sbd_port *sport, int reg, unsigned int value) +{ + __write_sbdchn(sport, reg, value); + if (SIBYTE_1956_WAR) + __war_sbd1956(sport); +} + +static void write_sbdshr(struct sbd_port *sport, int reg, unsigned int value) +{ + __write_sbdshr(sport, reg, value); + if (SIBYTE_1956_WAR) + __war_sbd1956(sport); +} + + +static int sbd_receive_ready(struct sbd_port *sport) +{ + return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_RX_RDY; +} + +static int sbd_receive_drain(struct sbd_port *sport) +{ + int loops = 10000; + + while (sbd_receive_ready(sport) && --loops) + read_sbdchn(sport, R_DUART_RX_HOLD); + return loops; +} + +static int __maybe_unused sbd_transmit_ready(struct sbd_port *sport) +{ + return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_RDY; +} + +static int __maybe_unused sbd_transmit_drain(struct sbd_port *sport) +{ + int loops = 10000; + + while (!sbd_transmit_ready(sport) && --loops) + udelay(2); + return loops; +} + +static int sbd_transmit_empty(struct sbd_port *sport) +{ + return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_EMT; +} + +static int sbd_line_drain(struct sbd_port *sport) +{ + int loops = 10000; + + while (!sbd_transmit_empty(sport) && --loops) + udelay(2); + return loops; +} + + +static unsigned int sbd_tx_empty(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + + return sbd_transmit_empty(sport) ? TIOCSER_TEMT : 0; +} + +static unsigned int sbd_get_mctrl(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + unsigned int mctrl, status; + + status = read_sbdshr(sport, R_DUART_IN_PORT); + status >>= (uport->line) % 2; + mctrl = (!(status & M_DUART_IN_PIN0_VAL) ? TIOCM_CTS : 0) | + (!(status & M_DUART_IN_PIN4_VAL) ? TIOCM_CAR : 0) | + (!(status & M_DUART_RIN0_PIN) ? TIOCM_RNG : 0) | + (!(status & M_DUART_IN_PIN2_VAL) ? TIOCM_DSR : 0); + return mctrl; +} + +static void sbd_set_mctrl(struct uart_port *uport, unsigned int mctrl) +{ + struct sbd_port *sport = to_sport(uport); + unsigned int clr = 0, set = 0, mode2; + + if (mctrl & TIOCM_DTR) + set |= M_DUART_SET_OPR2; + else + clr |= M_DUART_CLR_OPR2; + if (mctrl & TIOCM_RTS) + set |= M_DUART_SET_OPR0; + else + clr |= M_DUART_CLR_OPR0; + clr <<= (uport->line) % 2; + set <<= (uport->line) % 2; + + mode2 = read_sbdchn(sport, R_DUART_MODE_REG_2); + mode2 &= ~M_DUART_CHAN_MODE; + if (mctrl & TIOCM_LOOP) + mode2 |= V_DUART_CHAN_MODE_LCL_LOOP; + else + mode2 |= V_DUART_CHAN_MODE_NORMAL; + + write_sbdshr(sport, R_DUART_CLEAR_OPR, clr); + write_sbdshr(sport, R_DUART_SET_OPR, set); + write_sbdchn(sport, R_DUART_MODE_REG_2, mode2); +} + +static void sbd_stop_tx(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS); + sport->tx_stopped = 1; +}; + +static void sbd_start_tx(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + unsigned int mask; + + /* Enable tx interrupts. */ + mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); + mask |= M_DUART_IMR_TX; + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); + + /* Go!, go!, go!... */ + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN); + sport->tx_stopped = 0; +}; + +static void sbd_stop_rx(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0); +}; + +static void sbd_enable_ms(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + + write_sbdchn(sport, R_DUART_AUXCTL_X, + M_DUART_CIN_CHNG_ENA | M_DUART_CTS_CHNG_ENA); +} + +static void sbd_break_ctl(struct uart_port *uport, int break_state) +{ + struct sbd_port *sport = to_sport(uport); + + if (break_state == -1) + write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_START_BREAK); + else + write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_STOP_BREAK); +} + + +static void sbd_receive_chars(struct sbd_port *sport) +{ + struct uart_port *uport = &sport->port; + struct uart_icount *icount; + unsigned int status, ch, flag; + int count; + + for (count = 16; count; count--) { + status = read_sbdchn(sport, R_DUART_STATUS); + if (!(status & M_DUART_RX_RDY)) + break; + + ch = read_sbdchn(sport, R_DUART_RX_HOLD); + + flag = TTY_NORMAL; + + icount = &uport->icount; + icount->rx++; + + if (unlikely(status & + (M_DUART_RCVD_BRK | M_DUART_FRM_ERR | + M_DUART_PARITY_ERR | M_DUART_OVRUN_ERR))) { + if (status & M_DUART_RCVD_BRK) { + icount->brk++; + if (uart_handle_break(uport)) + continue; + } else if (status & M_DUART_FRM_ERR) + icount->frame++; + else if (status & M_DUART_PARITY_ERR) + icount->parity++; + if (status & M_DUART_OVRUN_ERR) + icount->overrun++; + + status &= uport->read_status_mask; + if (status & M_DUART_RCVD_BRK) + flag = TTY_BREAK; + else if (status & M_DUART_FRM_ERR) + flag = TTY_FRAME; + else if (status & M_DUART_PARITY_ERR) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(uport, ch)) + continue; + + uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag); + } + + tty_flip_buffer_push(uport->state->port.tty); +} + +static void sbd_transmit_chars(struct sbd_port *sport) +{ + struct uart_port *uport = &sport->port; + struct circ_buf *xmit = &sport->port.state->xmit; + unsigned int mask; + int stop_tx; + + /* XON/XOFF chars. */ + if (sport->port.x_char) { + write_sbdchn(sport, R_DUART_TX_HOLD, sport->port.x_char); + sport->port.icount.tx++; + sport->port.x_char = 0; + return; + } + + /* If nothing to do or stopped or hardware stopped. */ + stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)); + + /* Send char. */ + if (!stop_tx) { + write_sbdchn(sport, R_DUART_TX_HOLD, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + sport->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&sport->port); + } + + /* Are we are done? */ + if (stop_tx || uart_circ_empty(xmit)) { + /* Disable tx interrupts. */ + mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); + mask &= ~M_DUART_IMR_TX; + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); + } +} + +static void sbd_status_handle(struct sbd_port *sport) +{ + struct uart_port *uport = &sport->port; + unsigned int delta; + + delta = read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2)); + delta >>= (uport->line) % 2; + + if (delta & (M_DUART_IN_PIN0_VAL << S_DUART_IN_PIN_CHNG)) + uart_handle_cts_change(uport, !(delta & M_DUART_IN_PIN0_VAL)); + + if (delta & (M_DUART_IN_PIN2_VAL << S_DUART_IN_PIN_CHNG)) + uport->icount.dsr++; + + if (delta & ((M_DUART_IN_PIN2_VAL | M_DUART_IN_PIN0_VAL) << + S_DUART_IN_PIN_CHNG)) + wake_up_interruptible(&uport->state->port.delta_msr_wait); +} + +static irqreturn_t sbd_interrupt(int irq, void *dev_id) +{ + struct sbd_port *sport = dev_id; + struct uart_port *uport = &sport->port; + irqreturn_t status = IRQ_NONE; + unsigned int intstat; + int count; + + for (count = 16; count; count--) { + intstat = read_sbdshr(sport, + R_DUART_ISRREG((uport->line) % 2)); + intstat &= read_sbdshr(sport, + R_DUART_IMRREG((uport->line) % 2)); + intstat &= M_DUART_ISR_ALL; + if (!intstat) + break; + + if (intstat & M_DUART_ISR_RX) + sbd_receive_chars(sport); + if (intstat & M_DUART_ISR_IN) + sbd_status_handle(sport); + if (intstat & M_DUART_ISR_TX) + sbd_transmit_chars(sport); + + status = IRQ_HANDLED; + } + + return status; +} + + +static int sbd_startup(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + unsigned int mode1; + int ret; + + ret = request_irq(sport->port.irq, sbd_interrupt, + IRQF_SHARED, "sb1250-duart", sport); + if (ret) + return ret; + + /* Clear the receive FIFO. */ + sbd_receive_drain(sport); + + /* Clear the interrupt registers. */ + write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT); + read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2)); + + /* Set rx/tx interrupt to FIFO available. */ + mode1 = read_sbdchn(sport, R_DUART_MODE_REG_1); + mode1 &= ~(M_DUART_RX_IRQ_SEL_RXFULL | M_DUART_TX_IRQ_SEL_TXEMPT); + write_sbdchn(sport, R_DUART_MODE_REG_1, mode1); + + /* Disable tx, enable rx. */ + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_EN); + sport->tx_stopped = 1; + + /* Enable interrupts. */ + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), + M_DUART_IMR_IN | M_DUART_IMR_RX); + + return 0; +} + +static void sbd_shutdown(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_DIS); + sport->tx_stopped = 1; + free_irq(sport->port.irq, sport); +} + + +static void sbd_init_port(struct sbd_port *sport) +{ + struct uart_port *uport = &sport->port; + + if (sport->initialised) + return; + + /* There is no DUART reset feature, so just set some sane defaults. */ + write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_TX); + write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_RX); + write_sbdchn(sport, R_DUART_MODE_REG_1, V_DUART_BITS_PER_CHAR_8); + write_sbdchn(sport, R_DUART_MODE_REG_2, 0); + write_sbdchn(sport, R_DUART_FULL_CTL, + V_DUART_INT_TIME(0) | V_DUART_SIG_FULL(15)); + write_sbdchn(sport, R_DUART_OPCR_X, 0); + write_sbdchn(sport, R_DUART_AUXCTL_X, 0); + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0); + + sport->initialised = 1; +} + +static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios, + struct ktermios *old_termios) +{ + struct sbd_port *sport = to_sport(uport); + unsigned int mode1 = 0, mode2 = 0, aux = 0; + unsigned int mode1mask = 0, mode2mask = 0, auxmask = 0; + unsigned int oldmode1, oldmode2, oldaux; + unsigned int baud, brg; + unsigned int command; + + mode1mask |= ~(M_DUART_PARITY_MODE | M_DUART_PARITY_TYPE_ODD | + M_DUART_BITS_PER_CHAR); + mode2mask |= ~M_DUART_STOP_BIT_LEN_2; + auxmask |= ~M_DUART_CTS_CHNG_ENA; + + /* Byte size. */ + switch (termios->c_cflag & CSIZE) { + case CS5: + case CS6: + /* Unsupported, leave unchanged. */ + mode1mask |= M_DUART_PARITY_MODE; + break; + case CS7: + mode1 |= V_DUART_BITS_PER_CHAR_7; + break; + case CS8: + default: + mode1 |= V_DUART_BITS_PER_CHAR_8; + break; + } + + /* Parity and stop bits. */ + if (termios->c_cflag & CSTOPB) + mode2 |= M_DUART_STOP_BIT_LEN_2; + else + mode2 |= M_DUART_STOP_BIT_LEN_1; + if (termios->c_cflag & PARENB) + mode1 |= V_DUART_PARITY_MODE_ADD; + else + mode1 |= V_DUART_PARITY_MODE_NONE; + if (termios->c_cflag & PARODD) + mode1 |= M_DUART_PARITY_TYPE_ODD; + else + mode1 |= M_DUART_PARITY_TYPE_EVEN; + + baud = uart_get_baud_rate(uport, termios, old_termios, 1200, 5000000); + brg = V_DUART_BAUD_RATE(baud); + /* The actual lower bound is 1221bps, so compensate. */ + if (brg > M_DUART_CLK_COUNTER) + brg = M_DUART_CLK_COUNTER; + + uart_update_timeout(uport, termios->c_cflag, baud); + + uport->read_status_mask = M_DUART_OVRUN_ERR; + if (termios->c_iflag & INPCK) + uport->read_status_mask |= M_DUART_FRM_ERR | + M_DUART_PARITY_ERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + uport->read_status_mask |= M_DUART_RCVD_BRK; + + uport->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + uport->ignore_status_mask |= M_DUART_FRM_ERR | + M_DUART_PARITY_ERR; + if (termios->c_iflag & IGNBRK) { + uport->ignore_status_mask |= M_DUART_RCVD_BRK; + if (termios->c_iflag & IGNPAR) + uport->ignore_status_mask |= M_DUART_OVRUN_ERR; + } + + if (termios->c_cflag & CREAD) + command = M_DUART_RX_EN; + else + command = M_DUART_RX_DIS; + + if (termios->c_cflag & CRTSCTS) + aux |= M_DUART_CTS_CHNG_ENA; + else + aux &= ~M_DUART_CTS_CHNG_ENA; + + spin_lock(&uport->lock); + + if (sport->tx_stopped) + command |= M_DUART_TX_DIS; + else + command |= M_DUART_TX_EN; + + oldmode1 = read_sbdchn(sport, R_DUART_MODE_REG_1) & mode1mask; + oldmode2 = read_sbdchn(sport, R_DUART_MODE_REG_2) & mode2mask; + oldaux = read_sbdchn(sport, R_DUART_AUXCTL_X) & auxmask; + + if (!sport->tx_stopped) + sbd_line_drain(sport); + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_DIS); + + write_sbdchn(sport, R_DUART_MODE_REG_1, mode1 | oldmode1); + write_sbdchn(sport, R_DUART_MODE_REG_2, mode2 | oldmode2); + write_sbdchn(sport, R_DUART_CLK_SEL, brg); + write_sbdchn(sport, R_DUART_AUXCTL_X, aux | oldaux); + + write_sbdchn(sport, R_DUART_CMD, command); + + spin_unlock(&uport->lock); +} + + +static const char *sbd_type(struct uart_port *uport) +{ + return "SB1250 DUART"; +} + +static void sbd_release_port(struct uart_port *uport) +{ + struct sbd_port *sport = to_sport(uport); + struct sbd_duart *duart = sport->duart; + int map_guard; + + iounmap(sport->memctrl); + sport->memctrl = NULL; + iounmap(uport->membase); + uport->membase = NULL; + + map_guard = atomic_add_return(-1, &duart->map_guard); + if (!map_guard) + release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING); + release_mem_region(uport->mapbase, DUART_CHANREG_SPACING); +} + +static int sbd_map_port(struct uart_port *uport) +{ + const char *err = KERN_ERR "sbd: Cannot map MMIO\n"; + struct sbd_port *sport = to_sport(uport); + struct sbd_duart *duart = sport->duart; + + if (!uport->membase) + uport->membase = ioremap_nocache(uport->mapbase, + DUART_CHANREG_SPACING); + if (!uport->membase) { + printk(err); + return -ENOMEM; + } + + if (!sport->memctrl) + sport->memctrl = ioremap_nocache(duart->mapctrl, + DUART_CHANREG_SPACING); + if (!sport->memctrl) { + printk(err); + iounmap(uport->membase); + uport->membase = NULL; + return -ENOMEM; + } + + return 0; +} + +static int sbd_request_port(struct uart_port *uport) +{ + const char *err = KERN_ERR "sbd: Unable to reserve MMIO resource\n"; + struct sbd_duart *duart = to_sport(uport)->duart; + int map_guard; + int ret = 0; + + if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING, + "sb1250-duart")) { + printk(err); + return -EBUSY; + } + map_guard = atomic_add_return(1, &duart->map_guard); + if (map_guard == 1) { + if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING, + "sb1250-duart")) { + atomic_add(-1, &duart->map_guard); + printk(err); + ret = -EBUSY; + } + } + if (!ret) { + ret = sbd_map_port(uport); + if (ret) { + map_guard = atomic_add_return(-1, &duart->map_guard); + if (!map_guard) + release_mem_region(duart->mapctrl, + DUART_CHANREG_SPACING); + } + } + if (ret) { + release_mem_region(uport->mapbase, DUART_CHANREG_SPACING); + return ret; + } + return 0; +} + +static void sbd_config_port(struct uart_port *uport, int flags) +{ + struct sbd_port *sport = to_sport(uport); + + if (flags & UART_CONFIG_TYPE) { + if (sbd_request_port(uport)) + return; + + uport->type = PORT_SB1250_DUART; + + sbd_init_port(sport); + } +} + +static int sbd_verify_port(struct uart_port *uport, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_SB1250_DUART) + ret = -EINVAL; + if (ser->irq != uport->irq) + ret = -EINVAL; + if (ser->baud_base != uport->uartclk / 16) + ret = -EINVAL; + return ret; +} + + +static const struct uart_ops sbd_ops = { + .tx_empty = sbd_tx_empty, + .set_mctrl = sbd_set_mctrl, + .get_mctrl = sbd_get_mctrl, + .stop_tx = sbd_stop_tx, + .start_tx = sbd_start_tx, + .stop_rx = sbd_stop_rx, + .enable_ms = sbd_enable_ms, + .break_ctl = sbd_break_ctl, + .startup = sbd_startup, + .shutdown = sbd_shutdown, + .set_termios = sbd_set_termios, + .type = sbd_type, + .release_port = sbd_release_port, + .request_port = sbd_request_port, + .config_port = sbd_config_port, + .verify_port = sbd_verify_port, +}; + +/* Initialize SB1250 DUART port structures. */ +static void __init sbd_probe_duarts(void) +{ + static int probed; + int chip, side; + int max_lines, line; + + if (probed) + return; + + /* Set the number of available units based on the SOC type. */ + switch (soc_type) { + case K_SYS_SOC_TYPE_BCM1x55: + case K_SYS_SOC_TYPE_BCM1x80: + max_lines = 4; + break; + default: + /* Assume at least two serial ports at the normal address. */ + max_lines = 2; + break; + } + + probed = 1; + + for (chip = 0, line = 0; chip < DUART_MAX_CHIP && line < max_lines; + chip++) { + sbd_duarts[chip].mapctrl = SBD_CTRLREGS(line); + + for (side = 0; side < DUART_MAX_SIDE && line < max_lines; + side++, line++) { + struct sbd_port *sport = &sbd_duarts[chip].sport[side]; + struct uart_port *uport = &sport->port; + + sport->duart = &sbd_duarts[chip]; + + uport->irq = SBD_INT(line); + uport->uartclk = 100000000 / 20 * 16; + uport->fifosize = 16; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &sbd_ops; + uport->line = line; + uport->mapbase = SBD_CHANREGS(line); + } + } +} + + +#ifdef CONFIG_SERIAL_SB1250_DUART_CONSOLE +/* + * Serial console stuff. Very basic, polling driver for doing serial + * console output. The console_sem is held by the caller, so we + * shouldn't be interrupted for more console activity. + */ +static void sbd_console_putchar(struct uart_port *uport, int ch) +{ + struct sbd_port *sport = to_sport(uport); + + sbd_transmit_drain(sport); + write_sbdchn(sport, R_DUART_TX_HOLD, ch); +} + +static void sbd_console_write(struct console *co, const char *s, + unsigned int count) +{ + int chip = co->index / DUART_MAX_SIDE; + int side = co->index % DUART_MAX_SIDE; + struct sbd_port *sport = &sbd_duarts[chip].sport[side]; + struct uart_port *uport = &sport->port; + unsigned long flags; + unsigned int mask; + + /* Disable transmit interrupts and enable the transmitter. */ + spin_lock_irqsave(&uport->lock, flags); + mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2)); + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), + mask & ~M_DUART_IMR_TX); + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN); + spin_unlock_irqrestore(&uport->lock, flags); + + uart_console_write(&sport->port, s, count, sbd_console_putchar); + + /* Restore transmit interrupts and the transmitter enable. */ + spin_lock_irqsave(&uport->lock, flags); + sbd_line_drain(sport); + if (sport->tx_stopped) + write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS); + write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask); + spin_unlock_irqrestore(&uport->lock, flags); +} + +static int __init sbd_console_setup(struct console *co, char *options) +{ + int chip = co->index / DUART_MAX_SIDE; + int side = co->index % DUART_MAX_SIDE; + struct sbd_port *sport = &sbd_duarts[chip].sport[side]; + struct uart_port *uport = &sport->port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (!sport->duart) + return -ENXIO; + + ret = sbd_map_port(uport); + if (ret) + return ret; + + sbd_init_port(sport); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(uport, co, baud, parity, bits, flow); +} + +static struct uart_driver sbd_reg; +static struct console sbd_console = { + .name = "duart", + .write = sbd_console_write, + .device = uart_console_device, + .setup = sbd_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sbd_reg +}; + +static int __init sbd_serial_console_init(void) +{ + sbd_probe_duarts(); + register_console(&sbd_console); + + return 0; +} + +console_initcall(sbd_serial_console_init); + +#define SERIAL_SB1250_DUART_CONSOLE &sbd_console +#else +#define SERIAL_SB1250_DUART_CONSOLE NULL +#endif /* CONFIG_SERIAL_SB1250_DUART_CONSOLE */ + + +static struct uart_driver sbd_reg = { + .owner = THIS_MODULE, + .driver_name = "sb1250_duart", + .dev_name = "duart", + .major = TTY_MAJOR, + .minor = SB1250_DUART_MINOR_BASE, + .nr = DUART_MAX_CHIP * DUART_MAX_SIDE, + .cons = SERIAL_SB1250_DUART_CONSOLE, +}; + +/* Set up the driver and register it. */ +static int __init sbd_init(void) +{ + int i, ret; + + sbd_probe_duarts(); + + ret = uart_register_driver(&sbd_reg); + if (ret) + return ret; + + for (i = 0; i < DUART_MAX_CHIP * DUART_MAX_SIDE; i++) { + struct sbd_duart *duart = &sbd_duarts[i / DUART_MAX_SIDE]; + struct sbd_port *sport = &duart->sport[i % DUART_MAX_SIDE]; + struct uart_port *uport = &sport->port; + + if (sport->duart) + uart_add_one_port(&sbd_reg, uport); + } + + return 0; +} + +/* Unload the driver. Unregister stuff, get ready to go away. */ +static void __exit sbd_exit(void) +{ + int i; + + for (i = DUART_MAX_CHIP * DUART_MAX_SIDE - 1; i >= 0; i--) { + struct sbd_duart *duart = &sbd_duarts[i / DUART_MAX_SIDE]; + struct sbd_port *sport = &duart->sport[i % DUART_MAX_SIDE]; + struct uart_port *uport = &sport->port; + + if (sport->duart) + uart_remove_one_port(&sbd_reg, uport); + } + + uart_unregister_driver(&sbd_reg); +} + +module_init(sbd_init); +module_exit(sbd_exit); diff --git a/drivers/tty/serial/sc26xx.c b/drivers/tty/serial/sc26xx.c new file mode 100644 index 000000000000..75038ad2b242 --- /dev/null +++ b/drivers/tty/serial/sc26xx.c @@ -0,0 +1,757 @@ +/* + * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices. + * + * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#define SC26XX_MAJOR 204 +#define SC26XX_MINOR_START 205 +#define SC26XX_NR 2 + +struct uart_sc26xx_port { + struct uart_port port[2]; + u8 dsr_mask[2]; + u8 cts_mask[2]; + u8 dcd_mask[2]; + u8 ri_mask[2]; + u8 dtr_mask[2]; + u8 rts_mask[2]; + u8 imr; +}; + +/* register common to both ports */ +#define RD_ISR 0x14 +#define RD_IPR 0x34 + +#define WR_ACR 0x10 +#define WR_IMR 0x14 +#define WR_OPCR 0x34 +#define WR_OPR_SET 0x38 +#define WR_OPR_CLR 0x3C + +/* access common register */ +#define READ_SC(p, r) readb((p)->membase + RD_##r) +#define WRITE_SC(p, r, v) writeb((v), (p)->membase + WR_##r) + +/* register per port */ +#define RD_PORT_MRx 0x00 +#define RD_PORT_SR 0x04 +#define RD_PORT_RHR 0x0c + +#define WR_PORT_MRx 0x00 +#define WR_PORT_CSR 0x04 +#define WR_PORT_CR 0x08 +#define WR_PORT_THR 0x0c + +/* SR bits */ +#define SR_BREAK (1 << 7) +#define SR_FRAME (1 << 6) +#define SR_PARITY (1 << 5) +#define SR_OVERRUN (1 << 4) +#define SR_TXRDY (1 << 2) +#define SR_RXRDY (1 << 0) + +#define CR_RES_MR (1 << 4) +#define CR_RES_RX (2 << 4) +#define CR_RES_TX (3 << 4) +#define CR_STRT_BRK (6 << 4) +#define CR_STOP_BRK (7 << 4) +#define CR_DIS_TX (1 << 3) +#define CR_ENA_TX (1 << 2) +#define CR_DIS_RX (1 << 1) +#define CR_ENA_RX (1 << 0) + +/* ISR bits */ +#define ISR_RXRDYB (1 << 5) +#define ISR_TXRDYB (1 << 4) +#define ISR_RXRDYA (1 << 1) +#define ISR_TXRDYA (1 << 0) + +/* IMR bits */ +#define IMR_RXRDY (1 << 1) +#define IMR_TXRDY (1 << 0) + +/* access port register */ +static inline u8 read_sc_port(struct uart_port *p, u8 reg) +{ + return readb(p->membase + p->line * 0x20 + reg); +} + +static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val) +{ + writeb(val, p->membase + p->line * 0x20 + reg); +} + +#define READ_SC_PORT(p, r) read_sc_port(p, RD_PORT_##r) +#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v) + +static void sc26xx_enable_irq(struct uart_port *port, int mask) +{ + struct uart_sc26xx_port *up; + int line = port->line; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + + up->imr |= mask << (line * 4); + WRITE_SC(port, IMR, up->imr); +} + +static void sc26xx_disable_irq(struct uart_port *port, int mask) +{ + struct uart_sc26xx_port *up; + int line = port->line; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + + up->imr &= ~(mask << (line * 4)); + WRITE_SC(port, IMR, up->imr); +} + +static struct tty_struct *receive_chars(struct uart_port *port) +{ + struct tty_struct *tty = NULL; + int limit = 10000; + unsigned char ch; + char flag; + u8 status; + + if (port->state != NULL) /* Unopened serial console */ + tty = port->state->port.tty; + + while (limit-- > 0) { + status = READ_SC_PORT(port, SR); + if (!(status & SR_RXRDY)) + break; + ch = READ_SC_PORT(port, RHR); + + flag = TTY_NORMAL; + port->icount.rx++; + + if (unlikely(status & (SR_BREAK | SR_FRAME | + SR_PARITY | SR_OVERRUN))) { + if (status & SR_BREAK) { + status &= ~(SR_PARITY | SR_FRAME); + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & SR_PARITY) + port->icount.parity++; + else if (status & SR_FRAME) + port->icount.frame++; + if (status & SR_OVERRUN) + port->icount.overrun++; + + status &= port->read_status_mask; + if (status & SR_BREAK) + flag = TTY_BREAK; + else if (status & SR_PARITY) + flag = TTY_PARITY; + else if (status & SR_FRAME) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + + if (status & port->ignore_status_mask) + continue; + + tty_insert_flip_char(tty, ch, flag); + } + return tty; +} + +static void transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit; + + if (!port->state) + return; + + xmit = &port->state->xmit; + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + sc26xx_disable_irq(port, IMR_TXRDY); + return; + } + while (!uart_circ_empty(xmit)) { + if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) + break; + + WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static irqreturn_t sc26xx_interrupt(int irq, void *dev_id) +{ + struct uart_sc26xx_port *up = dev_id; + struct tty_struct *tty; + unsigned long flags; + u8 isr; + + spin_lock_irqsave(&up->port[0].lock, flags); + + tty = NULL; + isr = READ_SC(&up->port[0], ISR); + if (isr & ISR_TXRDYA) + transmit_chars(&up->port[0]); + if (isr & ISR_RXRDYA) + tty = receive_chars(&up->port[0]); + + spin_unlock(&up->port[0].lock); + + if (tty) + tty_flip_buffer_push(tty); + + spin_lock(&up->port[1].lock); + + tty = NULL; + if (isr & ISR_TXRDYB) + transmit_chars(&up->port[1]); + if (isr & ISR_RXRDYB) + tty = receive_chars(&up->port[1]); + + spin_unlock_irqrestore(&up->port[1].lock, flags); + + if (tty) + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + +/* port->lock is not held. */ +static unsigned int sc26xx_tx_empty(struct uart_port *port) +{ + return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0; +} + +/* port->lock held by caller. */ +static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_sc26xx_port *up; + int line = port->line; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + + if (up->dtr_mask[line]) { + if (mctrl & TIOCM_DTR) + WRITE_SC(port, OPR_SET, up->dtr_mask[line]); + else + WRITE_SC(port, OPR_CLR, up->dtr_mask[line]); + } + if (up->rts_mask[line]) { + if (mctrl & TIOCM_RTS) + WRITE_SC(port, OPR_SET, up->rts_mask[line]); + else + WRITE_SC(port, OPR_CLR, up->rts_mask[line]); + } +} + +/* port->lock is held by caller and interrupts are disabled. */ +static unsigned int sc26xx_get_mctrl(struct uart_port *port) +{ + struct uart_sc26xx_port *up; + int line = port->line; + unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; + u8 ipr; + + port -= line; + up = container_of(port, struct uart_sc26xx_port, port[0]); + ipr = READ_SC(port, IPR) ^ 0xff; + + if (up->dsr_mask[line]) { + mctrl &= ~TIOCM_DSR; + mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0; + } + if (up->cts_mask[line]) { + mctrl &= ~TIOCM_CTS; + mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0; + } + if (up->dcd_mask[line]) { + mctrl &= ~TIOCM_CAR; + mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0; + } + if (up->ri_mask[line]) { + mctrl &= ~TIOCM_RNG; + mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0; + } + return mctrl; +} + +/* port->lock held by caller. */ +static void sc26xx_stop_tx(struct uart_port *port) +{ + return; +} + +/* port->lock held by caller. */ +static void sc26xx_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!uart_circ_empty(xmit)) { + if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) { + sc26xx_enable_irq(port, IMR_TXRDY); + break; + } + WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } +} + +/* port->lock held by caller. */ +static void sc26xx_stop_rx(struct uart_port *port) +{ +} + +/* port->lock held by caller. */ +static void sc26xx_enable_ms(struct uart_port *port) +{ +} + +/* port->lock is not held. */ +static void sc26xx_break_ctl(struct uart_port *port, int break_state) +{ + if (break_state == -1) + WRITE_SC_PORT(port, CR, CR_STRT_BRK); + else + WRITE_SC_PORT(port, CR, CR_STOP_BRK); +} + +/* port->lock is not held. */ +static int sc26xx_startup(struct uart_port *port) +{ + sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); + WRITE_SC(port, OPCR, 0); + + /* reset tx and rx */ + WRITE_SC_PORT(port, CR, CR_RES_RX); + WRITE_SC_PORT(port, CR, CR_RES_TX); + + /* start rx/tx */ + WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); + + /* enable irqs */ + sc26xx_enable_irq(port, IMR_RXRDY); + return 0; +} + +/* port->lock is not held. */ +static void sc26xx_shutdown(struct uart_port *port) +{ + /* disable interrupst */ + sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY); + + /* stop tx/rx */ + WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); +} + +/* port->lock is not held. */ +static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + unsigned int quot = uart_get_divisor(port, baud); + unsigned int iflag, cflag; + unsigned long flags; + u8 mr1, mr2, csr; + + spin_lock_irqsave(&port->lock, flags); + + while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) + udelay(2); + + WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX); + + iflag = termios->c_iflag; + cflag = termios->c_cflag; + + port->read_status_mask = SR_OVERRUN; + if (iflag & INPCK) + port->read_status_mask |= SR_PARITY | SR_FRAME; + if (iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SR_BREAK; + + port->ignore_status_mask = 0; + if (iflag & IGNBRK) + port->ignore_status_mask |= SR_BREAK; + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= SR_BREAK | SR_FRAME | + SR_PARITY | SR_OVERRUN; + + switch (cflag & CSIZE) { + case CS5: + mr1 = 0x00; + break; + case CS6: + mr1 = 0x01; + break; + case CS7: + mr1 = 0x02; + break; + default: + case CS8: + mr1 = 0x03; + break; + } + mr2 = 0x07; + if (cflag & CSTOPB) + mr2 = 0x0f; + if (cflag & PARENB) { + if (cflag & PARODD) + mr1 |= (1 << 2); + } else + mr1 |= (2 << 3); + + switch (baud) { + case 50: + csr = 0x00; + break; + case 110: + csr = 0x11; + break; + case 134: + csr = 0x22; + break; + case 200: + csr = 0x33; + break; + case 300: + csr = 0x44; + break; + case 600: + csr = 0x55; + break; + case 1200: + csr = 0x66; + break; + case 2400: + csr = 0x88; + break; + case 4800: + csr = 0x99; + break; + default: + case 9600: + csr = 0xbb; + break; + case 19200: + csr = 0xcc; + break; + } + + WRITE_SC_PORT(port, CR, CR_RES_MR); + WRITE_SC_PORT(port, MRx, mr1); + WRITE_SC_PORT(port, MRx, mr2); + + WRITE_SC(port, ACR, 0x80); + WRITE_SC_PORT(port, CSR, csr); + + /* reset tx and rx */ + WRITE_SC_PORT(port, CR, CR_RES_RX); + WRITE_SC_PORT(port, CR, CR_RES_TX); + + WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX); + while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc) + udelay(2); + + /* XXX */ + uart_update_timeout(port, cflag, + (port->uartclk / (16 * quot))); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *sc26xx_type(struct uart_port *port) +{ + return "SC26XX"; +} + +static void sc26xx_release_port(struct uart_port *port) +{ +} + +static int sc26xx_request_port(struct uart_port *port) +{ + return 0; +} + +static void sc26xx_config_port(struct uart_port *port, int flags) +{ +} + +static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static struct uart_ops sc26xx_ops = { + .tx_empty = sc26xx_tx_empty, + .set_mctrl = sc26xx_set_mctrl, + .get_mctrl = sc26xx_get_mctrl, + .stop_tx = sc26xx_stop_tx, + .start_tx = sc26xx_start_tx, + .stop_rx = sc26xx_stop_rx, + .enable_ms = sc26xx_enable_ms, + .break_ctl = sc26xx_break_ctl, + .startup = sc26xx_startup, + .shutdown = sc26xx_shutdown, + .set_termios = sc26xx_set_termios, + .type = sc26xx_type, + .release_port = sc26xx_release_port, + .request_port = sc26xx_request_port, + .config_port = sc26xx_config_port, + .verify_port = sc26xx_verify_port, +}; + +static struct uart_port *sc26xx_port; + +#ifdef CONFIG_SERIAL_SC26XX_CONSOLE +static void sc26xx_console_putchar(struct uart_port *port, char c) +{ + unsigned long flags; + int limit = 1000000; + + spin_lock_irqsave(&port->lock, flags); + + while (limit-- > 0) { + if (READ_SC_PORT(port, SR) & SR_TXRDY) { + WRITE_SC_PORT(port, THR, c); + break; + } + udelay(2); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void sc26xx_console_write(struct console *con, const char *s, unsigned n) +{ + struct uart_port *port = sc26xx_port; + int i; + + for (i = 0; i < n; i++) { + if (*s == '\n') + sc26xx_console_putchar(port, '\r'); + sc26xx_console_putchar(port, *s++); + } +} + +static int __init sc26xx_console_setup(struct console *con, char *options) +{ + struct uart_port *port = sc26xx_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (port->type != PORT_SC26XX) + return -1; + + printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index); + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, con, baud, parity, bits, flow); +} + +static struct uart_driver sc26xx_reg; +static struct console sc26xx_console = { + .name = "ttySC", + .write = sc26xx_console_write, + .device = uart_console_device, + .setup = sc26xx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sc26xx_reg, +}; +#define SC26XX_CONSOLE &sc26xx_console +#else +#define SC26XX_CONSOLE NULL +#endif + +static struct uart_driver sc26xx_reg = { + .owner = THIS_MODULE, + .driver_name = "SC26xx", + .dev_name = "ttySC", + .major = SC26XX_MAJOR, + .minor = SC26XX_MINOR_START, + .nr = SC26XX_NR, + .cons = SC26XX_CONSOLE, +}; + +static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos) +{ + unsigned int bit = (flags >> bitpos) & 15; + + return bit ? (1 << (bit - 1)) : 0; +} + +static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up, + int line, unsigned int data) +{ + up->dtr_mask[line] = sc26xx_flags2mask(data, 0); + up->rts_mask[line] = sc26xx_flags2mask(data, 4); + up->dsr_mask[line] = sc26xx_flags2mask(data, 8); + up->cts_mask[line] = sc26xx_flags2mask(data, 12); + up->dcd_mask[line] = sc26xx_flags2mask(data, 16); + up->ri_mask[line] = sc26xx_flags2mask(data, 20); +} + +static int __devinit sc26xx_probe(struct platform_device *dev) +{ + struct resource *res; + struct uart_sc26xx_port *up; + unsigned int *sc26xx_data = dev->dev.platform_data; + int err; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + up = kzalloc(sizeof *up, GFP_KERNEL); + if (unlikely(!up)) + return -ENOMEM; + + up->port[0].line = 0; + up->port[0].ops = &sc26xx_ops; + up->port[0].type = PORT_SC26XX; + up->port[0].uartclk = (29491200 / 16); /* arbitrary */ + + up->port[0].mapbase = res->start; + up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40); + up->port[0].iotype = UPIO_MEM; + up->port[0].irq = platform_get_irq(dev, 0); + + up->port[0].dev = &dev->dev; + + sc26xx_init_masks(up, 0, sc26xx_data[0]); + + sc26xx_port = &up->port[0]; + + up->port[1].line = 1; + up->port[1].ops = &sc26xx_ops; + up->port[1].type = PORT_SC26XX; + up->port[1].uartclk = (29491200 / 16); /* arbitrary */ + + up->port[1].mapbase = up->port[0].mapbase; + up->port[1].membase = up->port[0].membase; + up->port[1].iotype = UPIO_MEM; + up->port[1].irq = up->port[0].irq; + + up->port[1].dev = &dev->dev; + + sc26xx_init_masks(up, 1, sc26xx_data[1]); + + err = uart_register_driver(&sc26xx_reg); + if (err) + goto out_free_port; + + sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor; + + err = uart_add_one_port(&sc26xx_reg, &up->port[0]); + if (err) + goto out_unregister_driver; + + err = uart_add_one_port(&sc26xx_reg, &up->port[1]); + if (err) + goto out_remove_port0; + + err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up); + if (err) + goto out_remove_ports; + + dev_set_drvdata(&dev->dev, up); + return 0; + +out_remove_ports: + uart_remove_one_port(&sc26xx_reg, &up->port[1]); +out_remove_port0: + uart_remove_one_port(&sc26xx_reg, &up->port[0]); + +out_unregister_driver: + uart_unregister_driver(&sc26xx_reg); + +out_free_port: + kfree(up); + sc26xx_port = NULL; + return err; +} + + +static int __exit sc26xx_driver_remove(struct platform_device *dev) +{ + struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev); + + free_irq(up->port[0].irq, up); + + uart_remove_one_port(&sc26xx_reg, &up->port[0]); + uart_remove_one_port(&sc26xx_reg, &up->port[1]); + + uart_unregister_driver(&sc26xx_reg); + + kfree(up); + sc26xx_port = NULL; + + dev_set_drvdata(&dev->dev, NULL); + return 0; +} + +static struct platform_driver sc26xx_driver = { + .probe = sc26xx_probe, + .remove = __devexit_p(sc26xx_driver_remove), + .driver = { + .name = "SC26xx", + .owner = THIS_MODULE, + }, +}; + +static int __init sc26xx_init(void) +{ + return platform_driver_register(&sc26xx_driver); +} + +static void __exit sc26xx_exit(void) +{ + platform_driver_unregister(&sc26xx_driver); +} + +module_init(sc26xx_init); +module_exit(sc26xx_exit); + + +MODULE_AUTHOR("Thomas Bogendörfer"); +MODULE_DESCRIPTION("SC681/SC2692 serial driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:SC26xx"); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c new file mode 100644 index 000000000000..460a72d91bb7 --- /dev/null +++ b/drivers/tty/serial/serial_core.c @@ -0,0 +1,2578 @@ +/* + * linux/drivers/char/core.c + * + * Driver core for serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright 1999 ARM Limited + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. + * + * 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 /* for serial_state and serial_icounter_struct */ +#include +#include +#include + +#include +#include + +/* + * This is used to lock changes in serial line configuration. + */ +static DEFINE_MUTEX(port_mutex); + +/* + * lockdep: port->lock is initialized in two places, but we + * want only one lock-class: + */ +static struct lock_class_key port_lock_key; + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + +static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, + struct ktermios *old_termios); +static void __uart_wait_until_sent(struct uart_port *port, int timeout); +static void uart_change_pm(struct uart_state *state, int pm_state); + +/* + * This routine is used by the interrupt handler to schedule processing in + * the software interrupt portion of the driver. + */ +void uart_write_wakeup(struct uart_port *port) +{ + struct uart_state *state = port->state; + /* + * This means you called this function _after_ the port was + * closed. No cookie for you. + */ + BUG_ON(!state); + tasklet_schedule(&state->tlet); +} + +static void uart_stop(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __uart_start(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + if (!uart_circ_empty(&state->xmit) && state->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port); +} + +static void uart_start(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + __uart_start(tty); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void uart_tasklet_action(unsigned long data) +{ + struct uart_state *state = (struct uart_state *)data; + tty_wakeup(state->port.tty); +} + +static inline void +uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned long flags; + unsigned int old; + + spin_lock_irqsave(&port->lock, flags); + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); + spin_unlock_irqrestore(&port->lock, flags); +} + +#define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) +#define uart_clear_mctrl(port, clear) uart_update_mctrl(port, 0, clear) + +/* + * Startup the port. This will be called once per open. All calls + * will be serialised by the per-port mutex. + */ +static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) +{ + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + unsigned long page; + int retval = 0; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + /* + * Set the TTY IO error marker - we will only clear this + * once we have successfully opened the port. Also set + * up the tty->alt_speed kludge + */ + set_bit(TTY_IO_ERROR, &tty->flags); + + if (uport->type == PORT_UNKNOWN) + return 0; + + /* + * Initialise and allocate the transmit and temporary + * buffer. + */ + if (!state->xmit.buf) { + /* This is protected by the per port mutex */ + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + state->xmit.buf = (unsigned char *) page; + uart_circ_clear(&state->xmit); + } + + retval = uport->ops->startup(uport); + if (retval == 0) { + if (init_hw) { + /* + * Initialise the hardware port settings. + */ + uart_change_speed(tty, state, NULL); + + /* + * Setup the RTS and DTR signals once the + * port is open and ready to respond. + */ + if (tty->termios->c_cflag & CBAUD) + uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); + } + + if (port->flags & ASYNC_CTS_FLOW) { + spin_lock_irq(&uport->lock); + if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) + tty->hw_stopped = 1; + spin_unlock_irq(&uport->lock); + } + + set_bit(ASYNCB_INITIALIZED, &port->flags); + + clear_bit(TTY_IO_ERROR, &tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. Calls to + * uart_shutdown are serialised by the per-port semaphore. + */ +static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) +{ + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + + /* + * Set the TTY IO error marker + */ + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + + if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { + /* + * Turn off DTR and RTS early. + */ + if (!tty || (tty->termios->c_cflag & HUPCL)) + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free + * the irq here so the queue might never be woken up. Note + * that we won't end up waiting on delta_msr_wait again since + * any outstanding file descriptors should be pointing at + * hung_up_tty_fops now. + */ + wake_up_interruptible(&port->delta_msr_wait); + + /* + * Free the IRQ and disable the port. + */ + uport->ops->shutdown(uport); + + /* + * Ensure that the IRQ handler isn't running on another CPU. + */ + synchronize_irq(uport->irq); + } + + /* + * kill off our tasklet + */ + tasklet_kill(&state->tlet); + + /* + * Free the transmit buffer page. + */ + if (state->xmit.buf) { + free_page((unsigned long)state->xmit.buf); + state->xmit.buf = NULL; + } +} + +/** + * uart_update_timeout - update per-port FIFO timeout. + * @port: uart_port structure describing the port + * @cflag: termios cflag value + * @baud: speed of the port + * + * Set the port FIFO timeout value. The @cflag value should + * reflect the actual hardware settings. + */ +void +uart_update_timeout(struct uart_port *port, unsigned int cflag, + unsigned int baud) +{ + unsigned int bits; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + bits = 7; + break; + case CS6: + bits = 8; + break; + case CS7: + bits = 9; + break; + default: + bits = 10; + break; /* CS8 */ + } + + if (cflag & CSTOPB) + bits++; + if (cflag & PARENB) + bits++; + + /* + * The total number of bits to be transmitted in the fifo. + */ + bits = bits * port->fifosize; + + /* + * Figure the timeout to send the above number of bits. + * Add .02 seconds of slop + */ + port->timeout = (HZ * bits) / baud + HZ/50; +} + +EXPORT_SYMBOL(uart_update_timeout); + +/** + * uart_get_baud_rate - return baud rate for a particular port + * @port: uart_port structure describing the port in question. + * @termios: desired termios settings. + * @old: old termios (or NULL) + * @min: minimum acceptable baud rate + * @max: maximum acceptable baud rate + * + * Decode the termios structure into a numeric baud rate, + * taking account of the magic 38400 baud rate (with spd_* + * flags), and mapping the %B0 rate to 9600 baud. + * + * If the new baud rate is invalid, try the old termios setting. + * If it's still invalid, we try 9600 baud. + * + * Update the @termios structure to reflect the baud rate + * we're actually going to be using. Don't do this for the case + * where B0 is requested ("hang up"). + */ +unsigned int +uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, + struct ktermios *old, unsigned int min, unsigned int max) +{ + unsigned int try, baud, altbaud = 38400; + int hung_up = 0; + upf_t flags = port->flags & UPF_SPD_MASK; + + if (flags == UPF_SPD_HI) + altbaud = 57600; + else if (flags == UPF_SPD_VHI) + altbaud = 115200; + else if (flags == UPF_SPD_SHI) + altbaud = 230400; + else if (flags == UPF_SPD_WARP) + altbaud = 460800; + + for (try = 0; try < 2; try++) { + baud = tty_termios_baud_rate(termios); + + /* + * The spd_hi, spd_vhi, spd_shi, spd_warp kludge... + * Die! Die! Die! + */ + if (baud == 38400) + baud = altbaud; + + /* + * Special case: B0 rate. + */ + if (baud == 0) { + hung_up = 1; + baud = 9600; + } + + if (baud >= min && baud <= max) + return baud; + + /* + * Oops, the quotient was zero. Try again with + * the old baud rate if possible. + */ + termios->c_cflag &= ~CBAUD; + if (old) { + baud = tty_termios_baud_rate(old); + if (!hung_up) + tty_termios_encode_baud_rate(termios, + baud, baud); + old = NULL; + continue; + } + + /* + * As a last resort, if the range cannot be met then clip to + * the nearest chip supported rate. + */ + if (!hung_up) { + if (baud <= min) + tty_termios_encode_baud_rate(termios, + min + 1, min + 1); + else + tty_termios_encode_baud_rate(termios, + max - 1, max - 1); + } + } + /* Should never happen */ + WARN_ON(1); + return 0; +} + +EXPORT_SYMBOL(uart_get_baud_rate); + +/** + * uart_get_divisor - return uart clock divisor + * @port: uart_port structure describing the port. + * @baud: desired baud rate + * + * Calculate the uart clock divisor for the port. + */ +unsigned int +uart_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int quot; + + /* + * Old custom speed handling. + */ + 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; +} + +EXPORT_SYMBOL(uart_get_divisor); + +/* FIXME: Consistent locking policy */ +static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, + struct ktermios *old_termios) +{ + struct tty_port *port = &state->port; + struct uart_port *uport = state->uart_port; + struct ktermios *termios; + + /* + * If we have no tty, termios, or the port does not exist, + * then we can't set the parameters for this port. + */ + if (!tty || !tty->termios || uport->type == PORT_UNKNOWN) + return; + + termios = tty->termios; + + /* + * Set flags based on termios cflag + */ + if (termios->c_cflag & CRTSCTS) + set_bit(ASYNCB_CTS_FLOW, &port->flags); + else + clear_bit(ASYNCB_CTS_FLOW, &port->flags); + + if (termios->c_cflag & CLOCAL) + clear_bit(ASYNCB_CHECK_CD, &port->flags); + else + set_bit(ASYNCB_CHECK_CD, &port->flags); + + uport->ops->set_termios(uport, termios, old_termios); +} + +static inline int __uart_put_char(struct 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 uart_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct uart_state *state = tty->driver_data; + + return __uart_put_char(state->uart_port, &state->xmit, ch); +} + +static void uart_flush_chars(struct tty_struct *tty) +{ + uart_start(tty); +} + +static int uart_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port; + struct circ_buf *circ; + unsigned long flags; + int c, ret = 0; + + /* + * This means you called this function _after_ the port was + * closed. No cookie for you. + */ + if (!state) { + WARN_ON(1); + return -EL3HLT; + } + + port = state->uart_port; + circ = &state->xmit; + + if (!circ->buf) + return 0; + + spin_lock_irqsave(&port->lock, flags); + 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; + } + spin_unlock_irqrestore(&port->lock, flags); + + uart_start(tty); + return ret; +} + +static int uart_write_room(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&state->uart_port->lock, flags); + ret = uart_circ_chars_free(&state->xmit); + spin_unlock_irqrestore(&state->uart_port->lock, flags); + return ret; +} + +static int uart_chars_in_buffer(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&state->uart_port->lock, flags); + ret = uart_circ_chars_pending(&state->xmit); + spin_unlock_irqrestore(&state->uart_port->lock, flags); + return ret; +} + +static void uart_flush_buffer(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port; + unsigned long flags; + + /* + * This means you called this function _after_ the port was + * closed. No cookie for you. + */ + if (!state) { + WARN_ON(1); + return; + } + + port = state->uart_port; + pr_debug("uart_flush_buffer(%d) called\n", tty->index); + + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->xmit); + if (port->ops->flush_buffer) + port->ops->flush_buffer(port); + spin_unlock_irqrestore(&port->lock, flags); + tty_wakeup(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void uart_send_xchar(struct tty_struct *tty, char ch) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_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 uart_throttle(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + + if (I_IXOFF(tty)) + uart_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + uart_clear_mctrl(state->uart_port, TIOCM_RTS); +} + +static void uart_unthrottle(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + uart_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int uart_get_info(struct uart_state *state, + struct serial_struct __user *retinfo) +{ + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + + /* Ensure the state we copy is consistent and no hardware changes + occur as we go */ + mutex_lock(&port->mutex); + + tmp.type = uport->type; + tmp.line = uport->line; + tmp.port = uport->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = (long) uport->iobase >> HIGH_BITS_OFFSET; + tmp.irq = uport->irq; + tmp.flags = uport->flags; + tmp.xmit_fifo_size = uport->fifosize; + tmp.baud_base = uport->uartclk / 16; + tmp.close_delay = port->close_delay / 10; + tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : + port->closing_wait / 10; + tmp.custom_divisor = uport->custom_divisor; + tmp.hub6 = uport->hub6; + tmp.io_type = uport->iotype; + tmp.iomem_reg_shift = uport->regshift; + tmp.iomem_base = (void *)(unsigned long)uport->mapbase; + + mutex_unlock(&port->mutex); + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int uart_set_info(struct tty_struct *tty, struct uart_state *state, + struct serial_struct __user *newinfo) +{ + struct serial_struct new_serial; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + unsigned long new_port; + unsigned int change_irq, change_port, closing_wait; + unsigned int old_custom_divisor, close_delay; + upf_t 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); + close_delay = new_serial.close_delay * 10; + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + + /* + * This semaphore protects port->count. It is also + * very useful to prevent opens. Also, take the + * port configuration semaphore to make sure that a + * module insertion/removal doesn't change anything + * under us. + */ + mutex_lock(&port->mutex); + + change_irq = !(uport->flags & UPF_FIXED_PORT) + && new_serial.irq != uport->irq; + + /* + * Since changing the 'type' of the port changes its resource + * allocations, we should treat type changes the same as + * IO port changes. + */ + change_port = !(uport->flags & UPF_FIXED_PORT) + && (new_port != uport->iobase || + (unsigned long)new_serial.iomem_base != uport->mapbase || + new_serial.hub6 != uport->hub6 || + new_serial.io_type != uport->iotype || + new_serial.iomem_reg_shift != uport->regshift || + new_serial.type != uport->type); + + old_flags = uport->flags; + new_flags = new_serial.flags; + old_custom_divisor = uport->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != uport->uartclk / 16) || + (close_delay != port->close_delay) || + (closing_wait != port->closing_wait) || + (new_serial.xmit_fifo_size && + new_serial.xmit_fifo_size != uport->fifosize) || + (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + uport->flags = ((uport->flags & ~UPF_USR_MASK) | + (new_flags & UPF_USR_MASK)); + uport->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + /* + * Ask the low level driver to verify the settings. + */ + if (uport->ops->verify_port) + retval = uport->ops->verify_port(uport, &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; + + /* + * Make sure that we are the sole user of this port. + */ + if (tty_port_users(port) > 1) + goto exit; + + /* + * We need to shutdown the serial port at the old + * port/type/irq combination. + */ + uart_shutdown(tty, state); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = uport->iobase; + old_mapbase = uport->mapbase; + old_type = uport->type; + old_hub6 = uport->hub6; + old_iotype = uport->iotype; + old_shift = uport->regshift; + + /* + * Free and release old regions + */ + if (old_type != PORT_UNKNOWN) + uport->ops->release_port(uport); + + uport->iobase = new_port; + uport->type = new_serial.type; + uport->hub6 = new_serial.hub6; + uport->iotype = new_serial.io_type; + uport->regshift = new_serial.iomem_reg_shift; + uport->mapbase = (unsigned long)new_serial.iomem_base; + + /* + * Claim and map the new regions + */ + if (uport->type != PORT_UNKNOWN) { + retval = uport->ops->request_port(uport); + } else { + /* Always success - Jean II */ + retval = 0; + } + + /* + * If we fail to request resources for the + * new port, try to restore the old settings. + */ + if (retval && old_type != PORT_UNKNOWN) { + uport->iobase = old_iobase; + uport->type = old_type; + uport->hub6 = old_hub6; + uport->iotype = old_iotype; + uport->regshift = old_shift; + uport->mapbase = old_mapbase; + retval = uport->ops->request_port(uport); + /* + * If we failed to restore the old settings, + * we fail like this. + */ + if (retval) + uport->type = PORT_UNKNOWN; + + /* + * We failed anyway. + */ + retval = -EBUSY; + /* Added to return the correct error -Ram Gupta */ + goto exit; + } + } + + if (change_irq) + uport->irq = new_serial.irq; + if (!(uport->flags & UPF_FIXED_PORT)) + uport->uartclk = new_serial.baud_base * 16; + uport->flags = (uport->flags & ~UPF_CHANGE_MASK) | + (new_flags & UPF_CHANGE_MASK); + uport->custom_divisor = new_serial.custom_divisor; + port->close_delay = close_delay; + port->closing_wait = closing_wait; + if (new_serial.xmit_fifo_size) + uport->fifosize = new_serial.xmit_fifo_size; + if (port->tty) + port->tty->low_latency = + (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; + + check_and_exit: + retval = 0; + if (uport->type == PORT_UNKNOWN) + goto exit; + if (port->flags & ASYNC_INITIALIZED) { + if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || + old_custom_divisor != uport->custom_divisor) { + /* + * If they're setting up a custom divisor or speed, + * instead of clearing it, then bitch about it. No + * need to rate-limit; it's CAP_SYS_ADMIN only. + */ + if (uport->flags & UPF_SPD_MASK) { + char buf[64]; + printk(KERN_NOTICE + "%s sets custom speed on %s. This " + "is deprecated.\n", current->comm, + tty_name(port->tty, buf)); + } + uart_change_speed(tty, state, NULL); + } + } else + retval = uart_startup(tty, state, 1); + exit: + mutex_unlock(&port->mutex); + return retval; +} + +/** + * uart_get_lsr_info - get line status register info + * @tty: tty associated with the UART + * @state: UART being queried + * @value: returned modem value + * + * Note: uart_ioctl protects us against hangups. + */ +static int uart_get_lsr_info(struct tty_struct *tty, + struct uart_state *state, unsigned int __user *value) +{ + struct uart_port *uport = state->uart_port; + unsigned int result; + + result = uport->ops->tx_empty(uport); + + /* + * If we're about to load something into the transmit + * register, we'll pretend the transmitter isn't empty to + * avoid a race condition (depending on when the transmit + * interrupt happens). + */ + if (uport->x_char || + ((uart_circ_chars_pending(&state->xmit) > 0) && + !tty->stopped && !tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int uart_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct uart_state *state = tty->driver_data; + struct tty_port *port = &state->port; + struct uart_port *uport = state->uart_port; + int result = -EIO; + + mutex_lock(&port->mutex); + if ((!file || !tty_hung_up_p(file)) && + !(tty->flags & (1 << TTY_IO_ERROR))) { + result = uport->mctrl; + + spin_lock_irq(&uport->lock); + result |= uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); + } + mutex_unlock(&port->mutex); + + return result; +} + +static int +uart_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + int ret = -EIO; + + mutex_lock(&port->mutex); + if ((!file || !tty_hung_up_p(file)) && + !(tty->flags & (1 << TTY_IO_ERROR))) { + uart_update_mctrl(uport, set, clear); + ret = 0; + } + mutex_unlock(&port->mutex); + return ret; +} + +static int uart_break_ctl(struct tty_struct *tty, int break_state) +{ + struct uart_state *state = tty->driver_data; + struct tty_port *port = &state->port; + struct uart_port *uport = state->uart_port; + + mutex_lock(&port->mutex); + + if (uport->type != PORT_UNKNOWN) + uport->ops->break_ctl(uport, break_state); + + mutex_unlock(&port->mutex); + return 0; +} + +static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) +{ + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * Take the per-port semaphore. This prevents count from + * changing, and hence any extra opens of the port while + * we're auto-configuring. + */ + if (mutex_lock_interruptible(&port->mutex)) + return -ERESTARTSYS; + + ret = -EBUSY; + if (tty_port_users(port) == 1) { + uart_shutdown(tty, state); + + /* + * If we already have a port type configured, + * we must release its resources. + */ + if (uport->type != PORT_UNKNOWN) + uport->ops->release_port(uport); + + flags = UART_CONFIG_TYPE; + if (uport->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + /* + * This will claim the ports resources if + * a port is found. + */ + uport->ops->config_port(uport, flags); + + ret = uart_startup(tty, state, 1); + } + mutex_unlock(&port->mutex); + return ret; +} + +/* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + * + * FIXME: This wants extracting into a common all driver implementation + * of TIOCMWAIT using tty_port. + */ +static int +uart_wait_modem_status(struct uart_state *state, unsigned long arg) +{ + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; + DECLARE_WAITQUEUE(wait, current); + struct uart_icount cprev, cnow; + int ret; + + /* + * note the counters on entry + */ + spin_lock_irq(&uport->lock); + memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); + + /* + * Force modem status interrupts on + */ + uport->ops->enable_ms(uport); + spin_unlock_irq(&uport->lock); + + add_wait_queue(&port->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&uport->lock); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&uport->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(); + + /* see if a signal did it */ + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&port->delta_msr_wait, &wait); + + return ret; +} + +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int uart_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct uart_state *state = tty->driver_data; + struct uart_icount cnow; + struct uart_port *uport = state->uart_port; + + spin_lock_irq(&uport->lock); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&uport->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 0; +} + +/* + * Called via sys_ioctl. We can use spin_lock_irq() here. + */ +static int +uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct uart_state *state = tty->driver_data; + struct tty_port *port = &state->port; + void __user *uarg = (void __user *)arg; + int ret = -ENOIOCTLCMD; + + + /* + * These ioctls don't rely on the hardware to be present. + */ + switch (cmd) { + case TIOCGSERIAL: + ret = uart_get_info(state, uarg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(tty, state, uarg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(tty, state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } + + /* + * The following should only be used when hardware is present. + */ + switch (cmd) { + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + mutex_lock(&port->mutex); + + if (tty_hung_up_p(filp)) { + ret = -EIO; + goto out_up; + } + + /* + * All these rely on hardware being present and need to be + * protected against the tty being hung up. + */ + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(tty, state, uarg); + break; + + default: { + struct uart_port *uport = state->uart_port; + if (uport->ops->ioctl) + ret = uport->ops->ioctl(uport, cmd, arg); + break; + } + } +out_up: + mutex_unlock(&port->mutex); +out: + return ret; +} + +static void uart_set_ldisc(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *uport = state->uart_port; + + if (uport->ops->set_ldisc) + uport->ops->set_ldisc(uport, tty->termios->c_line); +} + +static void uart_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct uart_state *state = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + + /* + * These are the bits that are used to setup various + * flags in the low level driver. We can ignore the Bfoo + * bits in c_cflag; c_[io]speed will always be set + * appropriately by set_termios() in tty_ioctl.c + */ +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + if ((cflag ^ old_termios->c_cflag) == 0 && + tty->termios->c_ospeed == old_termios->c_ospeed && + tty->termios->c_ispeed == old_termios->c_ispeed && + RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) { + return; + } + + uart_change_speed(tty, state, old_termios); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR); + /* Handle transition away from B0 status */ + else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(state->uart_port, mask); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&state->uart_port->lock, flags); + tty->hw_stopped = 0; + __uart_start(tty); + spin_unlock_irqrestore(&state->uart_port->lock, flags); + } + /* Handle turning on CRTSCTS */ + else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { + spin_lock_irqsave(&state->uart_port->lock, flags); + if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) { + tty->hw_stopped = 1; + state->uart_port->ops->stop_tx(state->uart_port); + } + spin_unlock_irqrestore(&state->uart_port->lock, flags); + } +#if 0 + /* + * No need to wake up processes in open wait, since they + * sample the CLOCAL flag once, and don't recheck it. + * XXX It's not clear whether the current behavior is correct + * or not. Hence, this may change..... + */ + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&state->uart_port.open_wait); +#endif +} + +/* + * In 2.4.5, calls to this will be serialized via the BKL in + * linux/drivers/char/tty_io.c:tty_release() + * linux/drivers/char/tty_io.c:do_tty_handup() + */ +static void uart_close(struct tty_struct *tty, struct file *filp) +{ + struct uart_state *state = tty->driver_data; + struct tty_port *port; + struct uart_port *uport; + unsigned long flags; + + BUG_ON(!tty_locked()); + + if (!state) + return; + + uport = state->uart_port; + port = &state->port; + + pr_debug("uart_close(%d) called\n", uport->line); + + mutex_lock(&port->mutex); + spin_lock_irqsave(&port->lock, flags); + + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&port->lock, flags); + goto done; + } + + if ((tty->count == 1) && (port->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. port->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, " + "port->count is %d\n", port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n", + tty->name, port->count); + port->count = 0; + } + if (port->count) { + spin_unlock_irqrestore(&port->lock, flags); + goto done; + } + + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters by + * setting tty->closing. + */ + tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); + + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + /* + * hack: open-coded tty_wait_until_sent to avoid + * recursive tty_lock + */ + long timeout = msecs_to_jiffies(port->closing_wait); + if (wait_event_interruptible_timeout(tty->write_wait, + !tty_chars_in_buffer(tty), timeout) >= 0) + __uart_wait_until_sent(uport, timeout); + } + + /* + * At this point, we stop accepting input. To do this, we + * disable the receive line status interrupts. + */ + if (port->flags & ASYNC_INITIALIZED) { + unsigned long flags; + spin_lock_irqsave(&uport->lock, flags); + uport->ops->stop_rx(uport); + spin_unlock_irqrestore(&uport->lock, flags); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + __uart_wait_until_sent(uport, uport->timeout); + } + + uart_shutdown(tty, state); + uart_flush_buffer(tty); + + tty_ldisc_flush(tty); + + tty_port_tty_set(port, NULL); + spin_lock_irqsave(&port->lock, flags); + tty->closing = 0; + + if (port->blocked_open) { + spin_unlock_irqrestore(&port->lock, flags); + if (port->close_delay) + msleep_interruptible(port->close_delay); + spin_lock_irqsave(&port->lock, flags); + } else if (!uart_console(uport)) { + spin_unlock_irqrestore(&port->lock, flags); + uart_change_pm(state, 3); + spin_lock_irqsave(&port->lock, flags); + } + + /* + * Wake up anyone trying to open this port. + */ + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&port->open_wait); + +done: + mutex_unlock(&port->mutex); +} + +static void __uart_wait_until_sent(struct uart_port *port, int timeout) +{ + unsigned long char_time, expire; + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + /* + * Set the check interval to be 1/5 of the estimated time to + * send a single character, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + 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 the transmitter hasn't cleared in twice the approximate + * amount of time to send the entire FIFO, it probably won't + * ever clear. This assumes the UART isn't doing flow + * control, which is currently the case. Hence, if it ever + * takes longer than port->timeout, this is probably due to a + * UART bug of some kind. So, we clamp the timeout parameter at + * 2*port->timeout. + */ + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + pr_debug("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", + port->line, jiffies, expire); + + /* + * Check whether the transmitter is empty every 'char_time'. + * 'timeout' / 'expire' give us the maximum amount of time + * we wait. + */ + while (!port->ops->tx_empty(port)) { + msleep_interruptible(jiffies_to_msecs(char_time)); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + tty_lock(); + __uart_wait_until_sent(port, timeout); + tty_unlock(); +} + +/* + * This is called with the BKL held in + * linux/drivers/char/tty_io.c:do_tty_hangup() + * We're called from the eventd thread, so we can sleep for + * a _short_ time only. + */ +static void uart_hangup(struct tty_struct *tty) +{ + struct uart_state *state = tty->driver_data; + struct tty_port *port = &state->port; + unsigned long flags; + + BUG_ON(!tty_locked()); + pr_debug("uart_hangup(%d)\n", state->uart_port->line); + + mutex_lock(&port->mutex); + if (port->flags & ASYNC_NORMAL_ACTIVE) { + uart_flush_buffer(tty); + uart_shutdown(tty, state); + spin_lock_irqsave(&port->lock, flags); + port->count = 0; + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); + tty_port_tty_set(port, NULL); + wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->delta_msr_wait); + } + mutex_unlock(&port->mutex); +} + +/** + * uart_update_termios - update the terminal hw settings + * @tty: tty associated with UART + * @state: UART to update + * + * Copy across the serial console cflag setting into the termios settings + * for the initial open of the port. This allows continuity between the + * kernel settings, and the settings init adopts when it opens the port + * for the first time. + */ +static void uart_update_termios(struct tty_struct *tty, + struct uart_state *state) +{ + struct uart_port *port = state->uart_port; + + if (uart_console(port) && port->cons->cflag) { + tty->termios->c_cflag = port->cons->cflag; + port->cons->cflag = 0; + } + + /* + * If the device failed to grab its irq resources, + * or some other error occurred, don't try to talk + * to the port hardware. + */ + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + /* + * Make termios settings take effect. + */ + uart_change_speed(tty, state, NULL); + + /* + * And finally enable the RTS and DTR signals. + */ + if (tty->termios->c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +} + +static int uart_carrier_raised(struct tty_port *port) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + int mctrl; + spin_lock_irq(&uport->lock); + uport->ops->enable_ms(uport); + mctrl = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); + if (mctrl & TIOCM_CAR) + return 1; + return 0; +} + +static void uart_dtr_rts(struct tty_port *port, int onoff) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + + if (onoff) { + uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); + + /* + * If this is the first open to succeed, + * adjust things to suit. + */ + if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags)) + uart_update_termios(port->tty, state); + } + else + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); +} + +static struct uart_state *uart_get(struct uart_driver *drv, int line) +{ + struct uart_state *state; + struct tty_port *port; + int ret = 0; + + state = drv->state + line; + port = &state->port; + if (mutex_lock_interruptible(&port->mutex)) { + ret = -ERESTARTSYS; + goto err; + } + + port->count++; + if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { + ret = -ENXIO; + goto err_unlock; + } + return state; + + err_unlock: + port->count--; + mutex_unlock(&port->mutex); + err: + return ERR_PTR(ret); +} + +/* + * calls to uart_open are serialised by the BKL in + * fs/char_dev.c:chrdev_open() + * Note that if this fails, then uart_close() _will_ be called. + * + * In time, we want to scrap the "opening nonpresent ports" + * behaviour and implement an alternative way for setserial + * to set base addresses/ports/types. This will allow us to + * get rid of a certain amount of extra tests. + */ +static int uart_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; + struct uart_state *state; + struct tty_port *port; + int retval, line = tty->index; + + BUG_ON(!tty_locked()); + pr_debug("uart_open(%d) called\n", line); + + /* + * tty->driver->num won't change, so we won't fail here with + * tty->driver_data set to something non-NULL (and therefore + * we won't get caught by uart_close()). + */ + retval = -ENODEV; + if (line >= tty->driver->num) + goto fail; + + /* + * We take the semaphore inside uart_get to guarantee that we won't + * be re-entered while allocating the state structure, or while we + * request any IRQs that the driver may need. This also has the nice + * side-effect that it delays the action of uart_hangup, so we can + * guarantee that state->port.tty will always contain something + * reasonable. + */ + state = uart_get(drv, line); + if (IS_ERR(state)) { + retval = PTR_ERR(state); + goto fail; + } + port = &state->port; + + /* + * Once we set tty->driver_data here, we are guaranteed that + * uart_close() will decrement the driver module use count. + * Any failures from here onwards should not touch the count. + */ + tty->driver_data = state; + state->uart_port->state = state; + tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->alt_speed = 0; + tty_port_tty_set(port, tty); + + /* + * If the port is in the middle of closing, bail out now. + */ + if (tty_hung_up_p(filp)) { + retval = -EAGAIN; + port->count--; + mutex_unlock(&port->mutex); + goto fail; + } + + /* + * Make sure the device is in D0 state. + */ + if (port->count == 1) + uart_change_pm(state, 0); + + /* + * Start up the serial port. + */ + retval = uart_startup(tty, state, 0); + + /* + * If we succeeded, wait until the port is ready. + */ + mutex_unlock(&port->mutex); + if (retval == 0) + retval = tty_port_block_til_ready(port, tty, filp); + +fail: + return retval; +} + +static const char *uart_type(struct uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +#ifdef CONFIG_PROC_FS + +static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) +{ + struct uart_state *state = drv->state + i; + struct tty_port *port = &state->port; + int pm_state; + struct uart_port *uport = state->uart_port; + char stat_buf[32]; + unsigned int status; + int mmio; + + if (!uport) + return; + + mmio = uport->iotype >= UPIO_MEM; + seq_printf(m, "%d: uart:%s %s%08llX irq:%d", + uport->line, uart_type(uport), + mmio ? "mmio:0x" : "port:", + mmio ? (unsigned long long)uport->mapbase + : (unsigned long long)uport->iobase, + uport->irq); + + if (uport->type == PORT_UNKNOWN) { + seq_putc(m, '\n'); + return; + } + + if (capable(CAP_SYS_ADMIN)) { + mutex_lock(&port->mutex); + pm_state = state->pm_state; + if (pm_state) + uart_change_pm(state, 0); + spin_lock_irq(&uport->lock); + status = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); + if (pm_state) + uart_change_pm(state, pm_state); + mutex_unlock(&port->mutex); + + seq_printf(m, " tx:%d rx:%d", + uport->icount.tx, uport->icount.rx); + if (uport->icount.frame) + seq_printf(m, " fe:%d", + uport->icount.frame); + if (uport->icount.parity) + seq_printf(m, " pe:%d", + uport->icount.parity); + if (uport->icount.brk) + seq_printf(m, " brk:%d", + uport->icount.brk); + if (uport->icount.overrun) + seq_printf(m, " oe:%d", + uport->icount.overrun); + +#define INFOBIT(bit, str) \ + if (uport->mctrl & (bit)) \ + strncat(stat_buf, (str), sizeof(stat_buf) - \ + strlen(stat_buf) - 2) +#define STATBIT(bit, str) \ + if (status & (bit)) \ + strncat(stat_buf, (str), sizeof(stat_buf) - \ + strlen(stat_buf) - 2) + + stat_buf[0] = '\0'; + stat_buf[1] = '\0'; + INFOBIT(TIOCM_RTS, "|RTS"); + STATBIT(TIOCM_CTS, "|CTS"); + INFOBIT(TIOCM_DTR, "|DTR"); + STATBIT(TIOCM_DSR, "|DSR"); + STATBIT(TIOCM_CAR, "|CD"); + STATBIT(TIOCM_RNG, "|RI"); + if (stat_buf[0]) + stat_buf[0] = ' '; + + seq_puts(m, stat_buf); + } + seq_putc(m, '\n'); +#undef STATBIT +#undef INFOBIT +} + +static int uart_proc_show(struct seq_file *m, void *v) +{ + struct tty_driver *ttydrv = m->private; + struct uart_driver *drv = ttydrv->driver_state; + int i; + + seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n", + "", "", ""); + for (i = 0; i < drv->nr; i++) + uart_line_info(m, drv, i); + return 0; +} + +static int uart_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, uart_proc_show, PDE(inode)->data); +} + +static const struct file_operations uart_proc_fops = { + .owner = THIS_MODULE, + .open = uart_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) +/* + * uart_console_write - write a console message to a serial port + * @port: the port to write the message + * @s: array of characters + * @count: number of characters in string to write + * @write: function to write character to port + */ +void uart_console_write(struct uart_port *port, const char *s, + unsigned int count, + void (*putchar)(struct uart_port *, int)) +{ + unsigned int i; + + for (i = 0; i < count; i++, s++) { + if (*s == '\n') + putchar(port, '\r'); + putchar(port, *s); + } +} +EXPORT_SYMBOL_GPL(uart_console_write); + +/* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ +struct uart_port * __init +uart_get_console(struct uart_port *ports, int nr, struct console *co) +{ + int idx = co->index; + + if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && + ports[idx].membase == NULL)) + for (idx = 0; idx < nr; idx++) + if (ports[idx].iobase != 0 || + ports[idx].membase != NULL) + break; + + co->index = idx; + + return ports + idx; +} + +/** + * uart_parse_options - Parse serial port baud/parity/bits/flow contro. + * @options: pointer to option string + * @baud: pointer to an 'int' variable for the baud rate. + * @parity: pointer to an 'int' variable for the parity. + * @bits: pointer to an 'int' variable for the number of data bits. + * @flow: pointer to an 'int' variable for the flow control character. + * + * uart_parse_options decodes a string containing the serial console + * options. The format of the string is , + * eg: 115200n8r + */ +void +uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow) +{ + char *s = options; + + *baud = simple_strtoul(s, NULL, 10); + while (*s >= '0' && *s <= '9') + s++; + if (*s) + *parity = *s++; + if (*s) + *bits = *s++ - '0'; + if (*s) + *flow = *s; +} +EXPORT_SYMBOL_GPL(uart_parse_options); + +struct baud_rates { + unsigned int rate; + unsigned int cflag; +}; + +static const struct baud_rates baud_rates[] = { + { 921600, B921600 }, + { 460800, B460800 }, + { 230400, B230400 }, + { 115200, B115200 }, + { 57600, B57600 }, + { 38400, B38400 }, + { 19200, B19200 }, + { 9600, B9600 }, + { 4800, B4800 }, + { 2400, B2400 }, + { 1200, B1200 }, + { 0, B38400 } +}; + +/** + * uart_set_options - setup the serial console parameters + * @port: pointer to the serial ports uart_port structure + * @co: console pointer + * @baud: baud rate + * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) + * @bits: number of data bits + * @flow: flow control character - 'r' (rts) + */ +int +uart_set_options(struct uart_port *port, struct console *co, + int baud, int parity, int bits, int flow) +{ + struct ktermios termios; + static struct ktermios dummy; + int i; + + /* + * Ensure that the serial console lock is initialised + * early. + */ + spin_lock_init(&port->lock); + lockdep_set_class(&port->lock, &port_lock_key); + + memset(&termios, 0, sizeof(struct ktermios)); + + termios.c_cflag = CREAD | HUPCL | CLOCAL; + + /* + * Construct a cflag setting. + */ + for (i = 0; baud_rates[i].rate; i++) + if (baud_rates[i].rate <= baud) + break; + + termios.c_cflag |= baud_rates[i].cflag; + + if (bits == 7) + termios.c_cflag |= CS7; + else + termios.c_cflag |= CS8; + + switch (parity) { + case 'o': case 'O': + termios.c_cflag |= PARODD; + /*fall through*/ + case 'e': case 'E': + termios.c_cflag |= PARENB; + break; + } + + if (flow == 'r') + termios.c_cflag |= CRTSCTS; + + /* + * some uarts on other side don't support no flow control. + * So we set * DTR in host uart to make them happy + */ + port->mctrl |= TIOCM_DTR; + + port->ops->set_termios(port, &termios, &dummy); + /* + * Allow the setting of the UART parameters with a NULL console + * too: + */ + if (co) + co->cflag = termios.c_cflag; + + return 0; +} +EXPORT_SYMBOL_GPL(uart_set_options); +#endif /* CONFIG_SERIAL_CORE_CONSOLE */ + +static void uart_change_pm(struct uart_state *state, int pm_state) +{ + struct uart_port *port = state->uart_port; + + if (state->pm_state != pm_state) { + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; + } +} + +struct uart_match { + struct uart_port *port; + struct uart_driver *driver; +}; + +static int serial_match_port(struct device *dev, void *data) +{ + struct uart_match *match = data; + struct tty_driver *tty_drv = match->driver->tty_driver; + dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) + + match->port->line; + + return dev->devt == devt; /* Actually, only one tty per port */ +} + +int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) +{ + struct uart_state *state = drv->state + uport->line; + struct tty_port *port = &state->port; + struct device *tty_dev; + struct uart_match match = {uport, drv}; + struct tty_struct *tty; + + mutex_lock(&port->mutex); + + /* Must be inside the mutex lock until we convert to tty_port */ + tty = port->tty; + + tty_dev = device_find_child(uport->dev, &match, serial_match_port); + if (device_may_wakeup(tty_dev)) { + if (!enable_irq_wake(uport->irq)) + uport->irq_wake = 1; + put_device(tty_dev); + mutex_unlock(&port->mutex); + return 0; + } + if (console_suspend_enabled || !uart_console(uport)) + uport->suspended = 1; + + if (port->flags & ASYNC_INITIALIZED) { + const struct uart_ops *ops = uport->ops; + int tries; + + if (console_suspend_enabled || !uart_console(uport)) { + set_bit(ASYNCB_SUSPENDED, &port->flags); + clear_bit(ASYNCB_INITIALIZED, &port->flags); + + spin_lock_irq(&uport->lock); + ops->stop_tx(uport); + ops->set_mctrl(uport, 0); + ops->stop_rx(uport); + spin_unlock_irq(&uport->lock); + } + + /* + * Wait for the transmitter to empty. + */ + for (tries = 3; !ops->tx_empty(uport) && tries; tries--) + msleep(10); + if (!tries) + printk(KERN_ERR "%s%s%s%d: Unable to drain " + "transmitter\n", + uport->dev ? dev_name(uport->dev) : "", + uport->dev ? ": " : "", + drv->dev_name, + drv->tty_driver->name_base + uport->line); + + if (console_suspend_enabled || !uart_console(uport)) + ops->shutdown(uport); + } + + /* + * Disable the console device before suspending. + */ + if (console_suspend_enabled && uart_console(uport)) + console_stop(uport->cons); + + if (console_suspend_enabled || !uart_console(uport)) + uart_change_pm(state, 3); + + mutex_unlock(&port->mutex); + + return 0; +} + +int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) +{ + struct uart_state *state = drv->state + uport->line; + struct tty_port *port = &state->port; + struct device *tty_dev; + struct uart_match match = {uport, drv}; + struct ktermios termios; + + mutex_lock(&port->mutex); + + tty_dev = device_find_child(uport->dev, &match, serial_match_port); + if (!uport->suspended && device_may_wakeup(tty_dev)) { + if (uport->irq_wake) { + disable_irq_wake(uport->irq); + uport->irq_wake = 0; + } + mutex_unlock(&port->mutex); + return 0; + } + uport->suspended = 0; + + /* + * Re-enable the console device after suspending. + */ + if (console_suspend_enabled && uart_console(uport)) { + /* + * First try to use the console cflag setting. + */ + memset(&termios, 0, sizeof(struct ktermios)); + termios.c_cflag = uport->cons->cflag; + + /* + * If that's unset, use the tty termios setting. + */ + if (port->tty && port->tty->termios && termios.c_cflag == 0) + termios = *(port->tty->termios); + + uart_change_pm(state, 0); + uport->ops->set_termios(uport, &termios, NULL); + console_start(uport->cons); + } + + if (port->flags & ASYNC_SUSPENDED) { + const struct uart_ops *ops = uport->ops; + int ret; + + uart_change_pm(state, 0); + spin_lock_irq(&uport->lock); + ops->set_mctrl(uport, 0); + spin_unlock_irq(&uport->lock); + if (console_suspend_enabled || !uart_console(uport)) { + /* Protected by port mutex for now */ + struct tty_struct *tty = port->tty; + ret = ops->startup(uport); + if (ret == 0) { + if (tty) + uart_change_speed(tty, state, NULL); + spin_lock_irq(&uport->lock); + ops->set_mctrl(uport, uport->mctrl); + ops->start_tx(uport); + spin_unlock_irq(&uport->lock); + set_bit(ASYNCB_INITIALIZED, &port->flags); + } else { + /* + * Failed to resume - maybe hardware went away? + * Clear the "initialized" flag so we won't try + * to call the low level drivers shutdown method. + */ + uart_shutdown(tty, state); + } + } + + clear_bit(ASYNCB_SUSPENDED, &port->flags); + } + + mutex_unlock(&port->mutex); + + return 0; +} + +static inline void +uart_report_port(struct uart_driver *drv, struct uart_port *port) +{ + char address[64]; + + switch (port->iotype) { + case UPIO_PORT: + snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase); + break; + case UPIO_HUB6: + snprintf(address, sizeof(address), + "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + case UPIO_MEM32: + case UPIO_AU: + case UPIO_TSI: + case UPIO_DWAPB: + case UPIO_DWAPB32: + snprintf(address, sizeof(address), + "MMIO 0x%llx", (unsigned long long)port->mapbase); + break; + default: + strlcpy(address, "*unknown*", sizeof(address)); + break; + } + + printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", + port->dev ? dev_name(port->dev) : "", + port->dev ? ": " : "", + drv->dev_name, + drv->tty_driver->name_base + port->line, + address, port->irq, uart_type(port)); +} + +static void +uart_configure_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) +{ + unsigned int flags; + + /* + * If there isn't a port here, don't do anything further. + */ + if (!port->iobase && !port->mapbase && !port->membase) + return; + + /* + * Now do the auto configuration stuff. Note that config_port + * is expected to claim the resources and map the port for us. + */ + flags = 0; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) { + if (!(port->flags & UPF_FIXED_TYPE)) { + port->type = PORT_UNKNOWN; + flags |= UART_CONFIG_TYPE; + } + port->ops->config_port(port, flags); + } + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + uart_report_port(drv, port); + + /* Power up port for set_mctrl() */ + uart_change_pm(state, 0); + + /* + * Ensure that the modem control lines are de-activated. + * keep the DTR setting that is set in uart_set_options() + * We probably don't need a spinlock around this, but + */ + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR); + spin_unlock_irqrestore(&port->lock, flags); + + /* + * If this driver supports console, and it hasn't been + * successfully registered yet, try to re-register it. + * It may be that the port was not available. + */ + if (port->cons && !(port->cons->flags & CON_ENABLED)) + register_console(port->cons); + + /* + * Power down all ports by default, except the + * console if we have one. + */ + if (!uart_console(port)) + uart_change_pm(state, 3); + } +} + +#ifdef CONFIG_CONSOLE_POLL + +static int uart_poll_init(struct tty_driver *driver, int line, char *options) +{ + struct uart_driver *drv = driver->driver_state; + struct uart_state *state = drv->state + line; + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (!state || !state->uart_port) + return -1; + + port = state->uart_port; + if (!(port->ops->poll_get_char && port->ops->poll_put_char)) + return -1; + + if (options) { + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(port, NULL, baud, parity, bits, flow); + } + + return 0; +} + +static int uart_poll_get_char(struct tty_driver *driver, int line) +{ + struct uart_driver *drv = driver->driver_state; + struct uart_state *state = drv->state + line; + struct uart_port *port; + + if (!state || !state->uart_port) + return -1; + + port = state->uart_port; + return port->ops->poll_get_char(port); +} + +static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) +{ + struct uart_driver *drv = driver->driver_state; + struct uart_state *state = drv->state + line; + struct uart_port *port; + + if (!state || !state->uart_port) + return; + + port = state->uart_port; + port->ops->poll_put_char(port, ch); +} +#endif + +static const struct tty_operations uart_ops = { + .open = uart_open, + .close = uart_close, + .write = uart_write, + .put_char = uart_put_char, + .flush_chars = uart_flush_chars, + .write_room = uart_write_room, + .chars_in_buffer= uart_chars_in_buffer, + .flush_buffer = uart_flush_buffer, + .ioctl = uart_ioctl, + .throttle = uart_throttle, + .unthrottle = uart_unthrottle, + .send_xchar = uart_send_xchar, + .set_termios = uart_set_termios, + .set_ldisc = uart_set_ldisc, + .stop = uart_stop, + .start = uart_start, + .hangup = uart_hangup, + .break_ctl = uart_break_ctl, + .wait_until_sent= uart_wait_until_sent, +#ifdef CONFIG_PROC_FS + .proc_fops = &uart_proc_fops, +#endif + .tiocmget = uart_tiocmget, + .tiocmset = uart_tiocmset, + .get_icount = uart_get_icount, +#ifdef CONFIG_CONSOLE_POLL + .poll_init = uart_poll_init, + .poll_get_char = uart_poll_get_char, + .poll_put_char = uart_poll_put_char, +#endif +}; + +static const struct tty_port_operations uart_port_ops = { + .carrier_raised = uart_carrier_raised, + .dtr_rts = uart_dtr_rts, +}; + +/** + * uart_register_driver - register a driver with the uart core layer + * @drv: low level driver structure + * + * Register a uart driver with the core driver. We in turn register + * with the tty layer, and initialise the core driver per-port state. + * + * We have a proc file in /proc/tty/driver which is named after the + * normal driver. + * + * drv->port should be NULL, and the per-port structures should be + * registered using uart_add_one_port after this call has succeeded. + */ +int uart_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal; + int i, retval; + + BUG_ON(drv->state); + + /* + * Maybe we should be using a slab cache for this, especially if + * we have a large number of ports to handle. + */ + drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); + if (!drv->state) + goto out; + + normal = alloc_tty_driver(drv->nr); + if (!normal) + goto out_kfree; + + drv->tty_driver = normal; + + normal->owner = drv->owner; + normal->driver_name = drv->driver_name; + normal->name = drv->dev_name; + normal->major = drv->major; + normal->minor_start = drv->minor; + 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->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + normal->driver_state = drv; + tty_set_operations(normal, &uart_ops); + + /* + * Initialise the UART state(s). + */ + for (i = 0; i < drv->nr; i++) { + struct uart_state *state = drv->state + i; + struct tty_port *port = &state->port; + + tty_port_init(port); + port->ops = &uart_port_ops; + port->close_delay = 500; /* .5 seconds */ + port->closing_wait = 30000; /* 30 seconds */ + tasklet_init(&state->tlet, uart_tasklet_action, + (unsigned long)state); + } + + retval = tty_register_driver(normal); + if (retval >= 0) + return retval; + + put_tty_driver(normal); +out_kfree: + kfree(drv->state); +out: + return -ENOMEM; +} + +/** + * uart_unregister_driver - remove a driver from the uart core layer + * @drv: low level driver structure + * + * Remove all references to a driver from the core driver. The low + * level driver must have removed all its ports via the + * uart_remove_one_port() if it registered them with uart_add_one_port(). + * (ie, drv->port == NULL) + */ +void uart_unregister_driver(struct uart_driver *drv) +{ + struct tty_driver *p = drv->tty_driver; + tty_unregister_driver(p); + put_tty_driver(p); + kfree(drv->state); + drv->tty_driver = NULL; +} + +struct tty_driver *uart_console_device(struct console *co, int *index) +{ + struct uart_driver *p = co->data; + *index = co->index; + return p->tty_driver; +} + +/** + * uart_add_one_port - attach a driver-defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @uport: uart port structure to use for this port. + * + * This allows the driver to register its own uart_port structure + * with the core driver. The main purpose is to allow the low + * level uart drivers to expand uart_port, rather than having yet + * more levels of structures. + */ +int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) +{ + struct uart_state *state; + struct tty_port *port; + int ret = 0; + struct device *tty_dev; + + BUG_ON(in_interrupt()); + + if (uport->line >= drv->nr) + return -EINVAL; + + state = drv->state + uport->line; + port = &state->port; + + mutex_lock(&port_mutex); + mutex_lock(&port->mutex); + if (state->uart_port) { + ret = -EINVAL; + goto out; + } + + state->uart_port = uport; + state->pm_state = -1; + + uport->cons = drv->cons; + uport->state = state; + + /* + * If this port is a console, then the spinlock is already + * initialised. + */ + if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) { + spin_lock_init(&uport->lock); + lockdep_set_class(&uport->lock, &port_lock_key); + } + + uart_configure_port(drv, state, uport); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); + if (likely(!IS_ERR(tty_dev))) { + device_init_wakeup(tty_dev, 1); + device_set_wakeup_enable(tty_dev, 0); + } else + printk(KERN_ERR "Cannot register tty device on line %d\n", + uport->line); + + /* + * Ensure UPF_DEAD is not set. + */ + uport->flags &= ~UPF_DEAD; + + out: + mutex_unlock(&port->mutex); + mutex_unlock(&port_mutex); + + return ret; +} + +/** + * uart_remove_one_port - detach a driver defined port structure + * @drv: pointer to the uart low level driver structure for this port + * @uport: uart port structure for this port + * + * This unhooks (and hangs up) the specified port structure from the + * core driver. No further calls will be made to the low-level code + * for this port. + */ +int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) +{ + struct uart_state *state = drv->state + uport->line; + struct tty_port *port = &state->port; + + BUG_ON(in_interrupt()); + + if (state->uart_port != uport) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->uart_port, uport); + + mutex_lock(&port_mutex); + + /* + * Mark the port "dead" - this prevents any opens from + * succeeding while we shut down the port. + */ + mutex_lock(&port->mutex); + uport->flags |= UPF_DEAD; + mutex_unlock(&port->mutex); + + /* + * Remove the devices from the tty layer + */ + tty_unregister_device(drv->tty_driver, uport->line); + + if (port->tty) + tty_vhangup(port->tty); + + /* + * Free the port IO and memory resources, if any. + */ + if (uport->type != PORT_UNKNOWN) + uport->ops->release_port(uport); + + /* + * Indicate that there isn't a port here anymore. + */ + uport->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + tasklet_kill(&state->tlet); + + state->uart_port = NULL; + mutex_unlock(&port_mutex); + + return 0; +} + +/* + * Are the two ports equivalent? + */ +int uart_match_port(struct uart_port *port1, struct uart_port *port2) +{ + if (port1->iotype != port2->iotype) + return 0; + + switch (port1->iotype) { + case UPIO_PORT: + return (port1->iobase == port2->iobase); + case UPIO_HUB6: + return (port1->iobase == port2->iobase) && + (port1->hub6 == port2->hub6); + case UPIO_MEM: + case UPIO_MEM32: + case UPIO_AU: + case UPIO_TSI: + case UPIO_DWAPB: + case UPIO_DWAPB32: + return (port1->mapbase == port2->mapbase); + } + return 0; +} +EXPORT_SYMBOL(uart_match_port); + +EXPORT_SYMBOL(uart_write_wakeup); +EXPORT_SYMBOL(uart_register_driver); +EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_suspend_port); +EXPORT_SYMBOL(uart_resume_port); +EXPORT_SYMBOL(uart_add_one_port); +EXPORT_SYMBOL(uart_remove_one_port); + +MODULE_DESCRIPTION("Serial driver core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/serial_cs.c b/drivers/tty/serial/serial_cs.c new file mode 100644 index 000000000000..93760b2ea172 --- /dev/null +++ b/drivers/tty/serial/serial_cs.c @@ -0,0 +1,869 @@ +/*====================================================================== + + A driver for PCMCIA serial devices + + serial_cs.c 1.134 2002/05/04 05:48:53 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "8250.h" + + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Enable the speaker? */ +static int do_sound = 1; +/* Skip strict UART tests? */ +static int buggy_uart; + +module_param(do_sound, int, 0444); +module_param(buggy_uart, int, 0444); + +/*====================================================================*/ + +/* Table of multi-port card ID's */ + +struct serial_quirk { + unsigned int manfid; + unsigned int prodid; + int multi; /* 1 = multifunction, > 1 = # ports */ + void (*config)(struct pcmcia_device *); + void (*setup)(struct pcmcia_device *, struct uart_port *); + void (*wakeup)(struct pcmcia_device *); + int (*post)(struct pcmcia_device *); +}; + +struct serial_info { + struct pcmcia_device *p_dev; + int ndev; + int multi; + int slave; + int manfid; + int prodid; + int c950ctrl; + int line[4]; + const struct serial_quirk *quirk; +}; + +struct serial_cfg_mem { + tuple_t tuple; + cisparse_t parse; + u_char buf[256]; +}; + +/* + * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6" + * manfid 0x0160, 0x0104 + * This card appears to have a 14.7456MHz clock. + */ +/* Generic Modem: MD55x (GPRS/EDGE) have + * Elan VPU16551 UART with 14.7456MHz oscillator + * manfid 0x015D, 0x4C45 + */ +static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) +{ + port->uartclk = 14745600; +} + +static int quirk_post_ibm(struct pcmcia_device *link) +{ + u8 val; + int ret; + + ret = pcmcia_read_config_byte(link, 0x800, &val); + if (ret) + goto failed; + + ret = pcmcia_write_config_byte(link, 0x800, val | 1); + if (ret) + goto failed; + return 0; + + failed: + return -ENODEV; +} + +/* + * Nokia cards are not really multiport cards. Shouldn't this + * be handled by setting the quirk entry .multi = 0 | 1 ? + */ +static void quirk_config_nokia(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->multi > 1) + info->multi = 1; +} + +static void quirk_wakeup_oxsemi(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->c950ctrl) + outb(12, info->c950ctrl + 1); +} + +/* request_region? oxsemi branch does no request_region too... */ +/* + * This sequence is needed to properly initialize MC45 attached to OXCF950. + * I tried decreasing these msleep()s, but it worked properly (survived + * 1000 stop/start operations) with these timeouts (or bigger). + */ +static void quirk_wakeup_possio_gcc(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + unsigned int ctrl = info->c950ctrl; + + outb(0xA, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(300); + outb(0xC, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(200); + outb(0xF, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(100); + outb(0xC, ctrl + 1); +} + +/* + * Socket Dual IO: this enables irq's for second port + */ +static void quirk_config_socket(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + if (info->multi) + link->config_flags |= CONF_ENABLE_ESR; +} + +static const struct serial_quirk quirks[] = { + { + .manfid = 0x0160, + .prodid = 0x0104, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { + .manfid = 0x015D, + .prodid = 0x4C45, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { + .manfid = MANFID_IBM, + .prodid = ~0, + .multi = -1, + .post = quirk_post_ibm, + }, { + .manfid = MANFID_INTEL, + .prodid = PRODID_INTEL_DUAL_RS232, + .multi = 2, + }, { + .manfid = MANFID_NATINST, + .prodid = PRODID_NATINST_QUAD_RS232, + .multi = 4, + }, { + .manfid = MANFID_NOKIA, + .prodid = ~0, + .multi = -1, + .config = quirk_config_nokia, + }, { + .manfid = MANFID_OMEGA, + .prodid = PRODID_OMEGA_QSP_100, + .multi = 4, + }, { + .manfid = MANFID_OXSEMI, + .prodid = ~0, + .multi = -1, + .wakeup = quirk_wakeup_oxsemi, + }, { + .manfid = MANFID_POSSIO, + .prodid = PRODID_POSSIO_GCC, + .multi = -1, + .wakeup = quirk_wakeup_possio_gcc, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_D1, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_G, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_QUAD_RS232, + .multi = 4, + }, { + .manfid = MANFID_SOCKET, + .prodid = PRODID_SOCKET_DUAL_RS232, + .multi = 2, + .config = quirk_config_socket, + }, { + .manfid = MANFID_SOCKET, + .prodid = ~0, + .multi = -1, + .config = quirk_config_socket, + } +}; + + +static int serial_config(struct pcmcia_device * link); + + +static void serial_remove(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + dev_dbg(&link->dev, "serial_release\n"); + + /* + * Recheck to see if the device is still configured. + */ + for (i = 0; i < info->ndev; i++) + serial8250_unregister_port(info->line[i]); + + if (!info->slave) + pcmcia_disable_device(link); +} + +static int serial_suspend(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + for (i = 0; i < info->ndev; i++) + serial8250_suspend_port(info->line[i]); + + return 0; +} + +static int serial_resume(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i; + + for (i = 0; i < info->ndev; i++) + serial8250_resume_port(info->line[i]); + + if (info->quirk && info->quirk->wakeup) + info->quirk->wakeup(link); + + return 0; +} + +static int serial_probe(struct pcmcia_device *link) +{ + struct serial_info *info; + + dev_dbg(&link->dev, "serial_attach()\n"); + + /* Create new serial device */ + info = kzalloc(sizeof (*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->p_dev = link; + link->priv = info; + + link->config_flags |= CONF_ENABLE_IRQ; + if (do_sound) + link->config_flags |= CONF_ENABLE_SPKR; + + return serial_config(link); +} + +static void serial_detach(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + + dev_dbg(&link->dev, "serial_detach\n"); + + /* + * Ensure that the ports have been released. + */ + serial_remove(link); + + /* free bits */ + kfree(info); +} + +/*====================================================================*/ + +static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, + unsigned int iobase, int irq) +{ + struct uart_port port; + int line; + + memset(&port, 0, sizeof (struct uart_port)); + port.iobase = iobase; + port.irq = irq; + port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; + port.uartclk = 1843200; + port.dev = &handle->dev; + if (buggy_uart) + port.flags |= UPF_BUGGY_UART; + + if (info->quirk && info->quirk->setup) + info->quirk->setup(handle, &port); + + line = serial8250_register_port(&port); + if (line < 0) { + printk(KERN_NOTICE "serial_cs: serial8250_register_port() at " + "0x%04lx, irq %d failed\n", (u_long)iobase, irq); + return -EINVAL; + } + + info->line[info->ndev] = line; + info->ndev++; + + return 0; +} + +/*====================================================================*/ + +static int pfc_config(struct pcmcia_device *p_dev) +{ + unsigned int port = 0; + struct serial_info *info = p_dev->priv; + + if ((p_dev->resource[1]->end != 0) && + (resource_size(p_dev->resource[1]) == 8)) { + port = p_dev->resource[1]->start; + info->slave = 1; + } else if ((info->manfid == MANFID_OSITECH) && + (resource_size(p_dev->resource[0]) == 0x40)) { + port = p_dev->resource[0]->start + 0x28; + info->slave = 1; + } + if (info->slave) + return setup_serial(p_dev, info, port, p_dev->irq); + + dev_warn(&p_dev->dev, "no usable port range found, giving up\n"); + return -ENODEV; +} + +static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + static const int size_table[2] = { 8, 16 }; + int *try = priv_data; + + if (p_dev->resource[0]->start == 0) + return -ENODEV; + + if ((*try & 0x1) == 0) + p_dev->io_lines = 16; + + if (p_dev->resource[0]->end != size_table[(*try >> 1)]) + return -ENODEV; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); +} + +static int simple_config_check_notpicky(struct pcmcia_device *p_dev, + void *priv_data) +{ + static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; + int j; + + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; + } + return -ENODEV; +} + +static int simple_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i = -ENODEV, try; + + /* First pass: look for a config entry that looks normal. + * Two tries: without IO aliases, then with aliases */ + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; + for (try = 0; try < 4; try++) + if (!pcmcia_loop_config(link, simple_config_check, &try)) + goto found_port; + + /* Second pass: try to find an entry that isn't picky about + its base address, then try to grab any standard serial port + address, and finally try to get any free port. */ + if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) + goto found_port; + + dev_warn(&link->dev, "no usable port range found, giving up\n"); + return -1; + +found_port: + if (info->multi && (info->manfid == MANFID_3COM)) + link->config_index &= ~(0x08); + + /* + * Apply any configuration quirks. + */ + if (info->quirk && info->quirk->config) + info->quirk->config(link); + + i = pcmcia_enable_device(link); + if (i != 0) + return -1; + return setup_serial(link, info, link->resource[0]->start, link->irq); +} + +static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + int *multi = priv_data; + + if (p_dev->resource[1]->end) + return -EINVAL; + + /* The quad port cards have bad CIS's, so just look for a + window larger than 8 ports and assume it will be right */ + if (p_dev->resource[0]->end <= 8) + return -EINVAL; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = *multi * 8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + return 0; +} + +static int multi_config_check_notpicky(struct pcmcia_device *p_dev, + void *priv_data) +{ + int *base2 = priv_data; + + if (!p_dev->resource[0]->end || !p_dev->resource[1]->end) + return -ENODEV; + + p_dev->resource[0]->end = p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + + *base2 = p_dev->resource[0]->start + 8; + return 0; +} + +static int multi_config(struct pcmcia_device *link) +{ + struct serial_info *info = link->priv; + int i, base2 = 0; + + link->config_flags |= CONF_AUTO_SET_IO; + /* First, look for a generic full-sized window */ + if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) + base2 = link->resource[0]->start + 8; + else { + /* If that didn't work, look for two windows */ + info->multi = 2; + if (pcmcia_loop_config(link, multi_config_check_notpicky, + &base2)) { + dev_warn(&link->dev, "no usable port range " + "found, giving up\n"); + return -ENODEV; + } + } + + if (!link->irq) + dev_warn(&link->dev, "no usable IRQ found, continuing...\n"); + + /* + * Apply any configuration quirks. + */ + if (info->quirk && info->quirk->config) + info->quirk->config(link); + + i = pcmcia_enable_device(link); + if (i != 0) + return -ENODEV; + + /* The Oxford Semiconductor OXCF950 cards are in fact single-port: + * 8 registers are for the UART, the others are extra registers. + * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. + */ + if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO && + info->prodid == PRODID_POSSIO_GCC)) { + int err; + + if (link->config_index == 1 || + link->config_index == 3) { + err = setup_serial(link, info, base2, + link->irq); + base2 = link->resource[0]->start; + } else { + err = setup_serial(link, info, link->resource[0]->start, + link->irq); + } + info->c950ctrl = base2; + + /* + * FIXME: We really should wake up the port prior to + * handing it over to the serial layer. + */ + if (info->quirk && info->quirk->wakeup) + info->quirk->wakeup(link); + + return 0; + } + + setup_serial(link, info, link->resource[0]->start, link->irq); + for (i = 0; i < info->multi - 1; i++) + setup_serial(link, info, base2 + (8 * i), + link->irq); + return 0; +} + +static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) +{ + struct serial_info *info = p_dev->priv; + + if (!p_dev->resource[0]->end) + return -EINVAL; + + if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) + info->multi = p_dev->resource[0]->end >> 3; + + if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) + && (p_dev->resource[1]->end == 8)) + info->multi = 2; + + return 0; /* break */ +} + + +static int serial_config(struct pcmcia_device * link) +{ + struct serial_info *info = link->priv; + int i; + + dev_dbg(&link->dev, "serial_config\n"); + + /* Is this a compliant multifunction card? */ + info->multi = (link->socket->functions > 1); + + /* Is this a multiport card? */ + info->manfid = link->manf_id; + info->prodid = link->card_id; + + for (i = 0; i < ARRAY_SIZE(quirks); i++) + if ((quirks[i].manfid == ~0 || + quirks[i].manfid == info->manfid) && + (quirks[i].prodid == ~0 || + quirks[i].prodid == info->prodid)) { + info->quirk = &quirks[i]; + break; + } + + /* Another check for dual-serial cards: look for either serial or + multifunction cards that ask for appropriate IO port ranges */ + if ((info->multi == 0) && + (link->has_func_id) && + (link->socket->pcmcia_pfc == 0) && + ((link->func_id == CISTPL_FUNCID_MULTI) || + (link->func_id == CISTPL_FUNCID_SERIAL))) + pcmcia_loop_config(link, serial_check_for_multi, info); + + /* + * Apply any multi-port quirk. + */ + if (info->quirk && info->quirk->multi != -1) + info->multi = info->quirk->multi; + + dev_info(&link->dev, + "trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n", + link->manf_id, link->card_id, + link->socket->pcmcia_pfc, info->multi, info->quirk); + if (link->socket->pcmcia_pfc) + i = pfc_config(link); + else if (info->multi > 1) + i = multi_config(link); + else + i = simple_config(link); + + if (i || info->ndev == 0) + goto failed; + + /* + * Apply any post-init quirk. FIXME: This should really happen + * before we register the port, since it might already be in use. + */ + if (info->quirk && info->quirk->post) + if (info->quirk->post(link)) + goto failed; + + return 0; + +failed: + dev_warn(&link->dev, "failed to initialize\n"); + serial_remove(link); + return -ENODEV; +} + +static struct pcmcia_device_id serial_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020), + PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), + PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), + PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), + PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), + PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */ + PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */ + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), + PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), + PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e), + PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), + PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), + PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), + PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), + PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), + PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b), + PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), + PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb), + PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447), + PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), + PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), + PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41), + PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), + PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38), + PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ + PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), + PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), + PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3), + PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676), + PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029), + PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4), + PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b), + /* too generic */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */ + PCMCIA_DEVICE_FUNC_ID(2), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, serial_ids); + +MODULE_FIRMWARE("cis/PCMLM28.cis"); +MODULE_FIRMWARE("cis/DP83903.cis"); +MODULE_FIRMWARE("cis/3CCFEM556.cis"); +MODULE_FIRMWARE("cis/3CXEM556.cis"); +MODULE_FIRMWARE("cis/SW_8xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_7xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_555_SER.cis"); +MODULE_FIRMWARE("cis/MT5634ZLX.cis"); +MODULE_FIRMWARE("cis/COMpad2.cis"); +MODULE_FIRMWARE("cis/COMpad4.cis"); +MODULE_FIRMWARE("cis/RS-COM-2P.cis"); + +static struct pcmcia_driver serial_cs_driver = { + .owner = THIS_MODULE, + .name = "serial_cs", + .probe = serial_probe, + .remove = serial_detach, + .id_table = serial_ids, + .suspend = serial_suspend, + .resume = serial_resume, +}; + +static int __init init_serial_cs(void) +{ + return pcmcia_register_driver(&serial_cs_driver); +} + +static void __exit exit_serial_cs(void) +{ + pcmcia_unregister_driver(&serial_cs_driver); +} + +module_init(init_serial_cs); +module_exit(exit_serial_cs); + +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c new file mode 100644 index 000000000000..b1962025b1aa --- /dev/null +++ b/drivers/tty/serial/serial_ks8695.c @@ -0,0 +1,705 @@ +/* + * drivers/serial/serial_ks8695.c + * + * Driver for KS8695 serial ports + * + * Based on drivers/serial/serial_amba.c, by Kam Lee. + * + * Copyright 2002-2005 Micrel Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#if defined(CONFIG_SERIAL_KS8695_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + + +#define SERIAL_KS8695_MAJOR 204 +#define SERIAL_KS8695_MINOR 16 +#define SERIAL_KS8695_DEVNAME "ttyAM" + +#define SERIAL_KS8695_NR 1 + +/* + * Access macros for the KS8695 UART + */ +#define UART_GET_CHAR(p) (__raw_readl((p)->membase + KS8695_URRB) & 0xFF) +#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + KS8695_URTH) +#define UART_GET_FCR(p) __raw_readl((p)->membase + KS8695_URFC) +#define UART_PUT_FCR(p, c) __raw_writel((c), (p)->membase + KS8695_URFC) +#define UART_GET_MSR(p) __raw_readl((p)->membase + KS8695_URMS) +#define UART_GET_LSR(p) __raw_readl((p)->membase + KS8695_URLS) +#define UART_GET_LCR(p) __raw_readl((p)->membase + KS8695_URLC) +#define UART_PUT_LCR(p, c) __raw_writel((c), (p)->membase + KS8695_URLC) +#define UART_GET_MCR(p) __raw_readl((p)->membase + KS8695_URMC) +#define UART_PUT_MCR(p, c) __raw_writel((c), (p)->membase + KS8695_URMC) +#define UART_GET_BRDR(p) __raw_readl((p)->membase + KS8695_URBD) +#define UART_PUT_BRDR(p, c) __raw_writel((c), (p)->membase + KS8695_URBD) + +#define KS8695_CLR_TX_INT() __raw_writel(1 << KS8695_IRQ_UART_TX, KS8695_IRQ_VA + KS8695_INTST) + +#define UART_DUMMY_LSR_RX 0x100 +#define UART_PORT_SIZE (KS8695_USR - KS8695_URRB + 4) + +static inline int tx_enabled(struct uart_port *port) +{ + return port->unused[0] & 1; +} + +static inline int rx_enabled(struct uart_port *port) +{ + return port->unused[0] & 2; +} + +static inline int ms_enabled(struct uart_port *port) +{ + return port->unused[0] & 4; +} + +static inline void ms_enable(struct uart_port *port, int enabled) +{ + if(enabled) + port->unused[0] |= 4; + else + port->unused[0] &= ~4; +} + +static inline void rx_enable(struct uart_port *port, int enabled) +{ + if(enabled) + port->unused[0] |= 2; + else + port->unused[0] &= ~2; +} + +static inline void tx_enable(struct uart_port *port, int enabled) +{ + if(enabled) + port->unused[0] |= 1; + else + port->unused[0] &= ~1; +} + + +#ifdef SUPPORT_SYSRQ +static struct console ks8695_console; +#endif + +static void ks8695uart_stop_tx(struct uart_port *port) +{ + if (tx_enabled(port)) { + /* use disable_irq_nosync() and not disable_irq() to avoid self + * imposed deadlock by not waiting for irq handler to end, + * since this ks8695uart_stop_tx() is called from interrupt context. + */ + disable_irq_nosync(KS8695_IRQ_UART_TX); + tx_enable(port, 0); + } +} + +static void ks8695uart_start_tx(struct uart_port *port) +{ + if (!tx_enabled(port)) { + enable_irq(KS8695_IRQ_UART_TX); + tx_enable(port, 1); + } +} + +static void ks8695uart_stop_rx(struct uart_port *port) +{ + if (rx_enabled(port)) { + disable_irq(KS8695_IRQ_UART_RX); + rx_enable(port, 0); + } +} + +static void ks8695uart_enable_ms(struct uart_port *port) +{ + if (!ms_enabled(port)) { + enable_irq(KS8695_IRQ_UART_MODEM_STATUS); + ms_enable(port,1); + } +} + +static void ks8695uart_disable_ms(struct uart_port *port) +{ + if (ms_enabled(port)) { + disable_irq(KS8695_IRQ_UART_MODEM_STATUS); + ms_enable(port,0); + } +} + +static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty = port->state->port.tty; + unsigned int status, ch, lsr, flg, max_count = 256; + + status = UART_GET_LSR(port); /* clears pending LSR interrupts */ + while ((status & URLS_URDR) && max_count--) { + ch = UART_GET_CHAR(port); + flg = TTY_NORMAL; + + port->icount.rx++; + + /* + * Note that the error handling code is + * out of the main execution path + */ + lsr = UART_GET_LSR(port) | UART_DUMMY_LSR_RX; + if (unlikely(lsr & (URLS_URBI | URLS_URPE | URLS_URFE | URLS_URROE))) { + if (lsr & URLS_URBI) { + lsr &= ~(URLS_URFE | URLS_URPE); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } + if (lsr & URLS_URPE) + port->icount.parity++; + if (lsr & URLS_URFE) + port->icount.frame++; + if (lsr & URLS_URROE) + port->icount.overrun++; + + lsr &= port->read_status_mask; + + if (lsr & URLS_URBI) + flg = TTY_BREAK; + else if (lsr & URLS_URPE) + flg = TTY_PARITY; + else if (lsr & URLS_URFE) + flg = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, lsr, URLS_URROE, ch, flg); + +ignore_char: + status = UART_GET_LSR(port); + } + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + + +static irqreturn_t ks8695uart_tx_chars(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct circ_buf *xmit = &port->state->xmit; + unsigned int count; + + if (port->x_char) { + KS8695_CLR_TX_INT(); + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return IRQ_HANDLED; + } + + if (uart_tx_stopped(port) || uart_circ_empty(xmit)) { + ks8695uart_stop_tx(port); + return IRQ_HANDLED; + } + + count = 16; /* fifo size */ + while (!uart_circ_empty(xmit) && (count-- > 0)) { + KS8695_CLR_TX_INT(); + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + ks8695uart_stop_tx(port); + + return IRQ_HANDLED; +} + +static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int status; + + /* + * clear modem interrupt by reading MSR + */ + status = UART_GET_MSR(port); + + if (status & URMS_URDDCD) + uart_handle_dcd_change(port, status & URMS_URDDCD); + + if (status & URMS_URDDST) + port->icount.dsr++; + + if (status & URMS_URDCTS) + uart_handle_cts_change(port, status & URMS_URDCTS); + + if (status & URMS_URTERI) + port->icount.rng++; + + wake_up_interruptible(&port->state->port.delta_msr_wait); + + return IRQ_HANDLED; +} + +static unsigned int ks8695uart_tx_empty(struct uart_port *port) +{ + return (UART_GET_LSR(port) & URLS_URTE) ? TIOCSER_TEMT : 0; +} + +static unsigned int ks8695uart_get_mctrl(struct uart_port *port) +{ + unsigned int result = 0; + unsigned int status; + + status = UART_GET_MSR(port); + if (status & URMS_URDCD) + result |= TIOCM_CAR; + if (status & URMS_URDSR) + result |= TIOCM_DSR; + if (status & URMS_URCTS) + result |= TIOCM_CTS; + if (status & URMS_URRI) + result |= TIOCM_RI; + + return result; +} + +static void ks8695uart_set_mctrl(struct uart_port *port, u_int mctrl) +{ + unsigned int mcr; + + mcr = UART_GET_MCR(port); + if (mctrl & TIOCM_RTS) + mcr |= URMC_URRTS; + else + mcr &= ~URMC_URRTS; + + if (mctrl & TIOCM_DTR) + mcr |= URMC_URDTR; + else + mcr &= ~URMC_URDTR; + + UART_PUT_MCR(port, mcr); +} + +static void ks8695uart_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int lcr; + + lcr = UART_GET_LCR(port); + + if (break_state == -1) + lcr |= URLC_URSBC; + else + lcr &= ~URLC_URSBC; + + UART_PUT_LCR(port, lcr); +} + +static int ks8695uart_startup(struct uart_port *port) +{ + int retval; + + set_irq_flags(KS8695_IRQ_UART_TX, IRQF_VALID | IRQF_NOAUTOEN); + tx_enable(port, 0); + rx_enable(port, 1); + ms_enable(port, 1); + + /* + * Allocate the IRQ + */ + retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, IRQF_DISABLED, "UART TX", port); + if (retval) + goto err_tx; + + retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, IRQF_DISABLED, "UART RX", port); + if (retval) + goto err_rx; + + retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, IRQF_DISABLED, "UART LineStatus", port); + if (retval) + goto err_ls; + + retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, IRQF_DISABLED, "UART ModemStatus", port); + if (retval) + goto err_ms; + + return 0; + +err_ms: + free_irq(KS8695_IRQ_UART_LINE_STATUS, port); +err_ls: + free_irq(KS8695_IRQ_UART_RX, port); +err_rx: + free_irq(KS8695_IRQ_UART_TX, port); +err_tx: + return retval; +} + +static void ks8695uart_shutdown(struct uart_port *port) +{ + /* + * Free the interrupt + */ + free_irq(KS8695_IRQ_UART_RX, port); + free_irq(KS8695_IRQ_UART_TX, port); + free_irq(KS8695_IRQ_UART_MODEM_STATUS, port); + free_irq(KS8695_IRQ_UART_LINE_STATUS, port); + + /* disable break condition and fifos */ + UART_PUT_LCR(port, UART_GET_LCR(port) & ~URLC_URSBC); + UART_PUT_FCR(port, UART_GET_FCR(port) & ~URFC_URFE); +} + +static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) +{ + unsigned int lcr, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + switch (termios->c_cflag & CSIZE) { + case CS5: + lcr = URCL_5; + break; + case CS6: + lcr = URCL_6; + break; + case CS7: + lcr = URCL_7; + break; + default: + lcr = URCL_8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + lcr |= URLC_URSB; + + /* parity */ + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */ + if (termios->c_cflag & PARODD) + lcr |= URPE_MARK; + else + lcr |= URPE_SPACE; + } + else if (termios->c_cflag & PARODD) + lcr |= URPE_ODD; + else + lcr |= URPE_EVEN; + } + + if (port->fifosize > 1) + fcr = URFC_URFRT_8 | URFC_URTFR | URFC_URRFR | URFC_URFE; + + spin_lock_irqsave(&port->lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = URLS_URROE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= (URLS_URFE | URLS_URPE); + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= URLS_URBI; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= (URLS_URFE | URLS_URPE); + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= URLS_URBI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= URLS_URROE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_LSR_RX; + + /* first, disable everything */ + if (UART_ENABLE_MS(port, termios->c_cflag)) + ks8695uart_enable_ms(port); + else + ks8695uart_disable_ms(port); + + /* Set baud rate */ + UART_PUT_BRDR(port, quot); + + UART_PUT_LCR(port, lcr); + UART_PUT_FCR(port, fcr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *ks8695uart_type(struct uart_port *port) +{ + return port->type == PORT_KS8695 ? "KS8695" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void ks8695uart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int ks8695uart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + "serial_ks8695") != NULL ? 0 : -EBUSY; +} + +/* + * Configure/autoconfigure the port. + */ +static void ks8695uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_KS8695; + ks8695uart_request_port(port); + } +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int ks8695uart_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_KS8695) + ret = -EINVAL; + if (ser->irq != port->irq) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops ks8695uart_pops = { + .tx_empty = ks8695uart_tx_empty, + .set_mctrl = ks8695uart_set_mctrl, + .get_mctrl = ks8695uart_get_mctrl, + .stop_tx = ks8695uart_stop_tx, + .start_tx = ks8695uart_start_tx, + .stop_rx = ks8695uart_stop_rx, + .enable_ms = ks8695uart_enable_ms, + .break_ctl = ks8695uart_break_ctl, + .startup = ks8695uart_startup, + .shutdown = ks8695uart_shutdown, + .set_termios = ks8695uart_set_termios, + .type = ks8695uart_type, + .release_port = ks8695uart_release_port, + .request_port = ks8695uart_request_port, + .config_port = ks8695uart_config_port, + .verify_port = ks8695uart_verify_port, +}; + +static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = { + { + .membase = (void *) KS8695_UART_VA, + .mapbase = KS8695_UART_VA, + .iotype = SERIAL_IO_MEM, + .irq = KS8695_IRQ_UART_TX, + .uartclk = KS8695_CLOCK_RATE * 16, + .fifosize = 16, + .ops = &ks8695uart_pops, + .flags = ASYNC_BOOT_AUTOCONF, + .line = 0, + } +}; + +#ifdef CONFIG_SERIAL_KS8695_CONSOLE +static void ks8695_console_putchar(struct uart_port *port, int ch) +{ + while (!(UART_GET_LSR(port) & URLS_URTHRE)) + barrier(); + + UART_PUT_CHAR(port, ch); +} + +static void ks8695_console_write(struct console *co, const char *s, u_int count) +{ + struct uart_port *port = ks8695uart_ports + co->index; + + uart_console_write(port, s, count, ks8695_console_putchar); +} + +static void __init ks8695_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) +{ + unsigned int lcr; + + lcr = UART_GET_LCR(port); + + switch (lcr & URLC_PARITY) { + case URPE_ODD: + *parity = 'o'; + break; + case URPE_EVEN: + *parity = 'e'; + break; + default: + *parity = 'n'; + } + + switch (lcr & URLC_URCL) { + case URCL_5: + *bits = 5; + break; + case URCL_6: + *bits = 6; + break; + case URCL_7: + *bits = 7; + break; + default: + *bits = 8; + } + + *baud = port->uartclk / (UART_GET_BRDR(port) & 0x0FFF); + *baud /= 16; + *baud &= 0xFFFFFFF0; +} + +static int __init ks8695_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 115200; + 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(ks8695uart_ports, SERIAL_KS8695_NR, co); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + ks8695_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver ks8695_reg; + +static struct console ks8695_console = { + .name = SERIAL_KS8695_DEVNAME, + .write = ks8695_console_write, + .device = uart_console_device, + .setup = ks8695_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &ks8695_reg, +}; + +static int __init ks8695_console_init(void) +{ + add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL); + register_console(&ks8695_console); + return 0; +} + +console_initcall(ks8695_console_init); + +#define KS8695_CONSOLE &ks8695_console +#else +#define KS8695_CONSOLE NULL +#endif + +static struct uart_driver ks8695_reg = { + .owner = THIS_MODULE, + .driver_name = "serial_ks8695", + .dev_name = SERIAL_KS8695_DEVNAME, + .major = SERIAL_KS8695_MAJOR, + .minor = SERIAL_KS8695_MINOR, + .nr = SERIAL_KS8695_NR, + .cons = KS8695_CONSOLE, +}; + +static int __init ks8695uart_init(void) +{ + int i, ret; + + printk(KERN_INFO "Serial: Micrel KS8695 UART driver\n"); + + ret = uart_register_driver(&ks8695_reg); + if (ret) + return ret; + + for (i = 0; i < SERIAL_KS8695_NR; i++) + uart_add_one_port(&ks8695_reg, &ks8695uart_ports[0]); + + return 0; +} + +static void __exit ks8695uart_exit(void) +{ + int i; + + for (i = 0; i < SERIAL_KS8695_NR; i++) + uart_remove_one_port(&ks8695_reg, &ks8695uart_ports[0]); + uart_unregister_driver(&ks8695_reg); +} + +module_init(ks8695uart_init); +module_exit(ks8695uart_exit); + +MODULE_DESCRIPTION("KS8695 serial port driver"); +MODULE_AUTHOR("Micrel Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/serial_lh7a40x.c b/drivers/tty/serial/serial_lh7a40x.c new file mode 100644 index 000000000000..ea744707c4d6 --- /dev/null +++ b/drivers/tty/serial/serial_lh7a40x.c @@ -0,0 +1,682 @@ +/* drivers/serial/serial_lh7a40x.c + * + * Copyright (C) 2004 Coastal Environmental Systems + * + * 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. + * + */ + +/* Driver for Sharp LH7A40X embedded serial ports + * + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * Based on drivers/serial/amba.c, by Deep Blue Solutions Ltd. + * + * --- + * + * This driver supports the embedded UARTs of the Sharp LH7A40X series + * CPUs. While similar to the 16550 and other UART chips, there is + * nothing close to register compatibility. Moreover, some of the + * modem control lines are not available, either in the chip or they + * are lacking in the board-level implementation. + * + * - Use of SIRDIS + * For simplicity, we disable the IR functions of any UART whenever + * we enable it. + * + */ + + +#if defined(CONFIG_SERIAL_LH7A40X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEV_MAJOR 204 +#define DEV_MINOR 16 +#define DEV_NR 3 + +#define ISR_LOOP_LIMIT 256 + +#define UR(p,o) _UR ((p)->membase, o) +#define _UR(b,o) (*((volatile unsigned int*)(((unsigned char*) b) + (o)))) +#define BIT_CLR(p,o,m) UR(p,o) = UR(p,o) & (~(unsigned int)m) +#define BIT_SET(p,o,m) UR(p,o) = UR(p,o) | ( (unsigned int)m) + +#define UART_REG_SIZE 32 + +#define UART_R_DATA (0x00) +#define UART_R_FCON (0x04) +#define UART_R_BRCON (0x08) +#define UART_R_CON (0x0c) +#define UART_R_STATUS (0x10) +#define UART_R_RAWISR (0x14) +#define UART_R_INTEN (0x18) +#define UART_R_ISR (0x1c) + +#define UARTEN (0x01) /* UART enable */ +#define SIRDIS (0x02) /* Serial IR disable (UART1 only) */ + +#define RxEmpty (0x10) +#define TxEmpty (0x80) +#define TxFull (0x20) +#define nRxRdy RxEmpty +#define nTxRdy TxFull +#define TxBusy (0x08) + +#define RxBreak (0x0800) +#define RxOverrunError (0x0400) +#define RxParityError (0x0200) +#define RxFramingError (0x0100) +#define RxError (RxBreak | RxOverrunError | RxParityError | RxFramingError) + +#define DCD (0x04) +#define DSR (0x02) +#define CTS (0x01) + +#define RxInt (0x01) +#define TxInt (0x02) +#define ModemInt (0x04) +#define RxTimeoutInt (0x08) + +#define MSEOI (0x10) + +#define WLEN_8 (0x60) +#define WLEN_7 (0x40) +#define WLEN_6 (0x20) +#define WLEN_5 (0x00) +#define WLEN (0x60) /* Mask for all word-length bits */ +#define STP2 (0x08) +#define PEN (0x02) /* Parity Enable */ +#define EPS (0x04) /* Even Parity Set */ +#define FEN (0x10) /* FIFO Enable */ +#define BRK (0x01) /* Send Break */ + + +struct uart_port_lh7a40x { + struct uart_port port; + unsigned int statusPrev; /* Most recently read modem status */ +}; + +static void lh7a40xuart_stop_tx (struct uart_port* port) +{ + BIT_CLR (port, UART_R_INTEN, TxInt); +} + +static void lh7a40xuart_start_tx (struct uart_port* port) +{ + BIT_SET (port, UART_R_INTEN, TxInt); + + /* *** FIXME: do I need to check for startup of the + transmitter? The old driver did, but AMBA + doesn't . */ +} + +static void lh7a40xuart_stop_rx (struct uart_port* port) +{ + BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt); +} + +static void lh7a40xuart_enable_ms (struct uart_port* port) +{ + BIT_SET (port, UART_R_INTEN, ModemInt); +} + +static void lh7a40xuart_rx_chars (struct uart_port* port) +{ + struct tty_struct* tty = port->state->port.tty; + int cbRxMax = 256; /* (Gross) limit on receive */ + unsigned int data; /* Received data and status */ + unsigned int flag; + + while (!(UR (port, UART_R_STATUS) & nRxRdy) && --cbRxMax) { + data = UR (port, UART_R_DATA); + flag = TTY_NORMAL; + ++port->icount.rx; + + if (unlikely(data & RxError)) { + if (data & RxBreak) { + data &= ~(RxFramingError | RxParityError); + ++port->icount.brk; + if (uart_handle_break (port)) + continue; + } + else if (data & RxParityError) + ++port->icount.parity; + else if (data & RxFramingError) + ++port->icount.frame; + if (data & RxOverrunError) + ++port->icount.overrun; + + /* Mask by termios, leave Rx'd byte */ + data &= port->read_status_mask | 0xff; + + if (data & RxBreak) + flag = TTY_BREAK; + else if (data & RxParityError) + flag = TTY_PARITY; + else if (data & RxFramingError) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char (port, (unsigned char) data)) + continue; + + uart_insert_char(port, data, RxOverrunError, data, flag); + } + tty_flip_buffer_push (tty); + return; +} + +static void lh7a40xuart_tx_chars (struct uart_port* port) +{ + struct circ_buf* xmit = &port->state->xmit; + int cbTxMax = port->fifosize; + + if (port->x_char) { + UR (port, UART_R_DATA) = port->x_char; + ++port->icount.tx; + port->x_char = 0; + return; + } + if (uart_circ_empty (xmit) || uart_tx_stopped (port)) { + lh7a40xuart_stop_tx (port); + return; + } + + /* Unlike the AMBA UART, the lh7a40x UART does not guarantee + that at least half of the FIFO is empty. Instead, we check + status for every character. Using the AMBA method causes + the transmitter to drop characters. */ + + do { + UR (port, UART_R_DATA) = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + ++port->icount.tx; + if (uart_circ_empty(xmit)) + break; + } while (!(UR (port, UART_R_STATUS) & nTxRdy) + && cbTxMax--); + + if (uart_circ_chars_pending (xmit) < WAKEUP_CHARS) + uart_write_wakeup (port); + + if (uart_circ_empty (xmit)) + lh7a40xuart_stop_tx (port); +} + +static void lh7a40xuart_modem_status (struct uart_port* port) +{ + unsigned int status = UR (port, UART_R_STATUS); + unsigned int delta + = status ^ ((struct uart_port_lh7a40x*) port)->statusPrev; + + BIT_SET (port, UART_R_RAWISR, MSEOI); /* Clear modem status intr */ + + if (!delta) /* Only happens if we missed 2 transitions */ + return; + + ((struct uart_port_lh7a40x*) port)->statusPrev = status; + + if (delta & DCD) + uart_handle_dcd_change (port, status & DCD); + + if (delta & DSR) + ++port->icount.dsr; + + if (delta & CTS) + uart_handle_cts_change (port, status & CTS); + + wake_up_interruptible (&port->state->port.delta_msr_wait); +} + +static irqreturn_t lh7a40xuart_int (int irq, void* dev_id) +{ + struct uart_port* port = dev_id; + unsigned int cLoopLimit = ISR_LOOP_LIMIT; + unsigned int isr = UR (port, UART_R_ISR); + + + do { + if (isr & (RxInt | RxTimeoutInt)) + lh7a40xuart_rx_chars(port); + if (isr & ModemInt) + lh7a40xuart_modem_status (port); + if (isr & TxInt) + lh7a40xuart_tx_chars (port); + + if (--cLoopLimit == 0) + break; + + isr = UR (port, UART_R_ISR); + } while (isr & (RxInt | TxInt | RxTimeoutInt)); + + return IRQ_HANDLED; +} + +static unsigned int lh7a40xuart_tx_empty (struct uart_port* port) +{ + return (UR (port, UART_R_STATUS) & TxEmpty) ? TIOCSER_TEMT : 0; +} + +static unsigned int lh7a40xuart_get_mctrl (struct uart_port* port) +{ + unsigned int result = 0; + unsigned int status = UR (port, UART_R_STATUS); + + if (status & DCD) + result |= TIOCM_CAR; + if (status & DSR) + result |= TIOCM_DSR; + if (status & CTS) + result |= TIOCM_CTS; + + return result; +} + +static void lh7a40xuart_set_mctrl (struct uart_port* port, unsigned int mctrl) +{ + /* None of the ports supports DTR. UART1 supports RTS through GPIO. */ + /* Note, kernel appears to be setting DTR and RTS on console. */ + + /* *** FIXME: this deserves more work. There's some work in + tracing all of the IO pins. */ +#if 0 + if( port->mapbase == UART1_PHYS) { + gpioRegs_t *gpio = (gpioRegs_t *)IO_ADDRESS(GPIO_PHYS); + + if (mctrl & TIOCM_RTS) + gpio->pbdr &= ~GPIOB_UART1_RTS; + else + gpio->pbdr |= GPIOB_UART1_RTS; + } +#endif +} + +static void lh7a40xuart_break_ctl (struct uart_port* port, int break_state) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + BIT_SET (port, UART_R_FCON, BRK); /* Assert break */ + else + BIT_CLR (port, UART_R_FCON, BRK); /* Deassert break */ + spin_unlock_irqrestore(&port->lock, flags); +} + +static int lh7a40xuart_startup (struct uart_port* port) +{ + int retval; + + retval = request_irq (port->irq, lh7a40xuart_int, 0, + "serial_lh7a40x", port); + if (retval) + return retval; + + /* Initial modem control-line settings */ + ((struct uart_port_lh7a40x*) port)->statusPrev + = UR (port, UART_R_STATUS); + + /* There is presently no configuration option to enable IR. + Thus, we always disable it. */ + + BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); + BIT_SET (port, UART_R_INTEN, RxTimeoutInt | RxInt); + + return 0; +} + +static void lh7a40xuart_shutdown (struct uart_port* port) +{ + free_irq (port->irq, port); + BIT_CLR (port, UART_R_FCON, BRK | FEN); + BIT_CLR (port, UART_R_CON, UARTEN); +} + +static void lh7a40xuart_set_termios (struct uart_port* port, + struct ktermios* termios, + struct ktermios* old) +{ + unsigned int con; + unsigned int inten; + unsigned int fcon; + unsigned long flags; + unsigned int baud; + unsigned int quot; + + baud = uart_get_baud_rate (port, termios, old, 8, port->uartclk/16); + quot = uart_get_divisor (port, baud); /* -1 performed elsewhere */ + + switch (termios->c_cflag & CSIZE) { + case CS5: + fcon = WLEN_5; + break; + case CS6: + fcon = WLEN_6; + break; + case CS7: + fcon = WLEN_7; + break; + case CS8: + default: + fcon = WLEN_8; + break; + } + if (termios->c_cflag & CSTOPB) + fcon |= STP2; + if (termios->c_cflag & PARENB) { + fcon |= PEN; + if (!(termios->c_cflag & PARODD)) + fcon |= EPS; + } + if (port->fifosize > 1) + fcon |= FEN; + + spin_lock_irqsave (&port->lock, flags); + + uart_update_timeout (port, termios->c_cflag, baud); + + port->read_status_mask = RxOverrunError; + if (termios->c_iflag & INPCK) + port->read_status_mask |= RxFramingError | RxParityError; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= RxBreak; + + /* Figure mask for status we ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= RxFramingError | RxParityError; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= RxBreak; + /* Ignore overrun when ignorning parity */ + /* *** FIXME: is this in the right place? */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= RxOverrunError; + } + + /* Ignore all receive errors when receive disabled */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= RxError; + + con = UR (port, UART_R_CON); + inten = (UR (port, UART_R_INTEN) & ~ModemInt); + + if (UART_ENABLE_MS (port, termios->c_cflag)) + inten |= ModemInt; + + BIT_CLR (port, UART_R_CON, UARTEN); /* Disable UART */ + UR (port, UART_R_INTEN) = 0; /* Disable interrupts */ + UR (port, UART_R_BRCON) = quot - 1; /* Set baud rate divisor */ + UR (port, UART_R_FCON) = fcon; /* Set FIFO and frame ctrl */ + UR (port, UART_R_INTEN) = inten; /* Enable interrupts */ + UR (port, UART_R_CON) = con; /* Restore UART mode */ + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char* lh7a40xuart_type (struct uart_port* port) +{ + return port->type == PORT_LH7A40X ? "LH7A40X" : NULL; +} + +static void lh7a40xuart_release_port (struct uart_port* port) +{ + release_mem_region (port->mapbase, UART_REG_SIZE); +} + +static int lh7a40xuart_request_port (struct uart_port* port) +{ + return request_mem_region (port->mapbase, UART_REG_SIZE, + "serial_lh7a40x") != NULL + ? 0 : -EBUSY; +} + +static void lh7a40xuart_config_port (struct uart_port* port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_LH7A40X; + lh7a40xuart_request_port (port); + } +} + +static int lh7a40xuart_verify_port (struct uart_port* port, + struct serial_struct* ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_LH7A40X) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= nr_irqs) + ret = -EINVAL; + if (ser->baud_base < 9600) /* *** FIXME: is this true? */ + ret = -EINVAL; + return ret; +} + +static struct uart_ops lh7a40x_uart_ops = { + .tx_empty = lh7a40xuart_tx_empty, + .set_mctrl = lh7a40xuart_set_mctrl, + .get_mctrl = lh7a40xuart_get_mctrl, + .stop_tx = lh7a40xuart_stop_tx, + .start_tx = lh7a40xuart_start_tx, + .stop_rx = lh7a40xuart_stop_rx, + .enable_ms = lh7a40xuart_enable_ms, + .break_ctl = lh7a40xuart_break_ctl, + .startup = lh7a40xuart_startup, + .shutdown = lh7a40xuart_shutdown, + .set_termios = lh7a40xuart_set_termios, + .type = lh7a40xuart_type, + .release_port = lh7a40xuart_release_port, + .request_port = lh7a40xuart_request_port, + .config_port = lh7a40xuart_config_port, + .verify_port = lh7a40xuart_verify_port, +}; + +static struct uart_port_lh7a40x lh7a40x_ports[DEV_NR] = { + { + .port = { + .membase = (void*) io_p2v (UART1_PHYS), + .mapbase = UART1_PHYS, + .iotype = UPIO_MEM, + .irq = IRQ_UART1INTR, + .uartclk = 14745600/2, + .fifosize = 16, + .ops = &lh7a40x_uart_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + }, + }, + { + .port = { + .membase = (void*) io_p2v (UART2_PHYS), + .mapbase = UART2_PHYS, + .iotype = UPIO_MEM, + .irq = IRQ_UART2INTR, + .uartclk = 14745600/2, + .fifosize = 16, + .ops = &lh7a40x_uart_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, + }, + }, + { + .port = { + .membase = (void*) io_p2v (UART3_PHYS), + .mapbase = UART3_PHYS, + .iotype = UPIO_MEM, + .irq = IRQ_UART3INTR, + .uartclk = 14745600/2, + .fifosize = 16, + .ops = &lh7a40x_uart_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, + }, + }, +}; + +#ifndef CONFIG_SERIAL_LH7A40X_CONSOLE +# define LH7A40X_CONSOLE NULL +#else +# define LH7A40X_CONSOLE &lh7a40x_console + +static void lh7a40xuart_console_putchar(struct uart_port *port, int ch) +{ + while (UR(port, UART_R_STATUS) & nTxRdy) + ; + UR(port, UART_R_DATA) = ch; +} + +static void lh7a40xuart_console_write (struct console* co, + const char* s, + unsigned int count) +{ + struct uart_port* port = &lh7a40x_ports[co->index].port; + unsigned int con = UR (port, UART_R_CON); + unsigned int inten = UR (port, UART_R_INTEN); + + + UR (port, UART_R_INTEN) = 0; /* Disable all interrupts */ + BIT_SET (port, UART_R_CON, UARTEN | SIRDIS); /* Enable UART */ + + uart_console_write(port, s, count, lh7a40xuart_console_putchar); + + /* Wait until all characters are sent */ + while (UR (port, UART_R_STATUS) & TxBusy) + ; + + /* Restore control and interrupt mask */ + UR (port, UART_R_CON) = con; + UR (port, UART_R_INTEN) = inten; +} + +static void __init lh7a40xuart_console_get_options (struct uart_port* port, + int* baud, + int* parity, + int* bits) +{ + if (UR (port, UART_R_CON) & UARTEN) { + unsigned int fcon = UR (port, UART_R_FCON); + unsigned int quot = UR (port, UART_R_BRCON) + 1; + + switch (fcon & (PEN | EPS)) { + default: *parity = 'n'; break; + case PEN: *parity = 'o'; break; + case PEN | EPS: *parity = 'e'; break; + } + + switch (fcon & WLEN) { + default: + case WLEN_8: *bits = 8; break; + case WLEN_7: *bits = 7; break; + case WLEN_6: *bits = 6; break; + case WLEN_5: *bits = 5; break; + } + + *baud = port->uartclk/(16*quot); + } +} + +static int __init lh7a40xuart_console_setup (struct console* co, char* options) +{ + struct uart_port* port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= DEV_NR) /* Bounds check on device number */ + co->index = 0; + port = &lh7a40x_ports[co->index].port; + + if (options) + uart_parse_options (options, &baud, &parity, &bits, &flow); + else + lh7a40xuart_console_get_options (port, &baud, &parity, &bits); + + return uart_set_options (port, co, baud, parity, bits, flow); +} + +static struct uart_driver lh7a40x_reg; +static struct console lh7a40x_console = { + .name = "ttyAM", + .write = lh7a40xuart_console_write, + .device = uart_console_device, + .setup = lh7a40xuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &lh7a40x_reg, +}; + +static int __init lh7a40xuart_console_init(void) +{ + register_console (&lh7a40x_console); + return 0; +} + +console_initcall (lh7a40xuart_console_init); + +#endif + +static struct uart_driver lh7a40x_reg = { + .owner = THIS_MODULE, + .driver_name = "ttyAM", + .dev_name = "ttyAM", + .major = DEV_MAJOR, + .minor = DEV_MINOR, + .nr = DEV_NR, + .cons = LH7A40X_CONSOLE, +}; + +static int __init lh7a40xuart_init(void) +{ + int ret; + + printk (KERN_INFO "serial: LH7A40X serial driver\n"); + + ret = uart_register_driver (&lh7a40x_reg); + + if (ret == 0) { + int i; + + for (i = 0; i < DEV_NR; i++) { + /* UART3, when used, requires GPIO pin reallocation */ + if (lh7a40x_ports[i].port.mapbase == UART3_PHYS) + GPIO_PINMUX |= 1<<3; + uart_add_one_port (&lh7a40x_reg, + &lh7a40x_ports[i].port); + } + } + return ret; +} + +static void __exit lh7a40xuart_exit(void) +{ + int i; + + for (i = 0; i < DEV_NR; i++) + uart_remove_one_port (&lh7a40x_reg, &lh7a40x_ports[i].port); + + uart_unregister_driver (&lh7a40x_reg); +} + +module_init (lh7a40xuart_init); +module_exit (lh7a40xuart_exit); + +MODULE_AUTHOR ("Marc Singer"); +MODULE_DESCRIPTION ("Sharp LH7A40X serial port driver"); +MODULE_LICENSE ("GPL"); diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c new file mode 100644 index 000000000000..c50e9fbbf743 --- /dev/null +++ b/drivers/tty/serial/serial_txx9.c @@ -0,0 +1,1344 @@ +/* + * drivers/serial/serial_txx9.c + * + * Derived from many drivers using generic_serial interface, + * especially serial_tx3912.c by Steven J. Hill and r39xx_serial.c + * (was in Linux/VR tree) by Jim Pick. + * + * Copyright (C) 1999 Harald Koerfgen + * Copyright (C) 2000 Jim Pick + * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) + * Copyright (C) 2000-2002 Toshiba Corporation + * + * 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. + * + * Serial driver for TX3927/TX4927/TX4925/TX4938 internal SIO controller + */ + +#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static char *serial_version = "1.11"; +static char *serial_name = "TX39/49 Serial driver"; + +#define PASS_LIMIT 256 + +#if !defined(CONFIG_SERIAL_TXX9_STDSERIAL) +/* "ttyS" is used for standard serial driver */ +#define TXX9_TTY_NAME "ttyTX" +#define TXX9_TTY_MINOR_START 196 +#define TXX9_TTY_MAJOR 204 +#else +/* acts like standard serial driver */ +#define TXX9_TTY_NAME "ttyS" +#define TXX9_TTY_MINOR_START 64 +#define TXX9_TTY_MAJOR TTY_MAJOR +#endif + +/* flag aliases */ +#define UPF_TXX9_HAVE_CTS_LINE UPF_BUGGY_UART +#define UPF_TXX9_USE_SCLK UPF_MAGIC_MULTIPLIER + +#ifdef CONFIG_PCI +/* support for Toshiba TC86C001 SIO */ +#define ENABLE_SERIAL_TXX9_PCI +#endif + +/* + * Number of serial ports + */ +#define UART_NR CONFIG_SERIAL_TXX9_NR_UARTS + +struct uart_txx9_port { + struct uart_port port; + /* No additional info for now */ +}; + +#define TXX9_REGION_SIZE 0x24 + +/* TXX9 Serial Registers */ +#define TXX9_SILCR 0x00 +#define TXX9_SIDICR 0x04 +#define TXX9_SIDISR 0x08 +#define TXX9_SICISR 0x0c +#define TXX9_SIFCR 0x10 +#define TXX9_SIFLCR 0x14 +#define TXX9_SIBGR 0x18 +#define TXX9_SITFIFO 0x1c +#define TXX9_SIRFIFO 0x20 + +/* SILCR : Line Control */ +#define TXX9_SILCR_SCS_MASK 0x00000060 +#define TXX9_SILCR_SCS_IMCLK 0x00000000 +#define TXX9_SILCR_SCS_IMCLK_BG 0x00000020 +#define TXX9_SILCR_SCS_SCLK 0x00000040 +#define TXX9_SILCR_SCS_SCLK_BG 0x00000060 +#define TXX9_SILCR_UEPS 0x00000010 +#define TXX9_SILCR_UPEN 0x00000008 +#define TXX9_SILCR_USBL_MASK 0x00000004 +#define TXX9_SILCR_USBL_1BIT 0x00000000 +#define TXX9_SILCR_USBL_2BIT 0x00000004 +#define TXX9_SILCR_UMODE_MASK 0x00000003 +#define TXX9_SILCR_UMODE_8BIT 0x00000000 +#define TXX9_SILCR_UMODE_7BIT 0x00000001 + +/* SIDICR : DMA/Int. Control */ +#define TXX9_SIDICR_TDE 0x00008000 +#define TXX9_SIDICR_RDE 0x00004000 +#define TXX9_SIDICR_TIE 0x00002000 +#define TXX9_SIDICR_RIE 0x00001000 +#define TXX9_SIDICR_SPIE 0x00000800 +#define TXX9_SIDICR_CTSAC 0x00000600 +#define TXX9_SIDICR_STIE_MASK 0x0000003f +#define TXX9_SIDICR_STIE_OERS 0x00000020 +#define TXX9_SIDICR_STIE_CTSS 0x00000010 +#define TXX9_SIDICR_STIE_RBRKD 0x00000008 +#define TXX9_SIDICR_STIE_TRDY 0x00000004 +#define TXX9_SIDICR_STIE_TXALS 0x00000002 +#define TXX9_SIDICR_STIE_UBRKD 0x00000001 + +/* SIDISR : DMA/Int. Status */ +#define TXX9_SIDISR_UBRK 0x00008000 +#define TXX9_SIDISR_UVALID 0x00004000 +#define TXX9_SIDISR_UFER 0x00002000 +#define TXX9_SIDISR_UPER 0x00001000 +#define TXX9_SIDISR_UOER 0x00000800 +#define TXX9_SIDISR_ERI 0x00000400 +#define TXX9_SIDISR_TOUT 0x00000200 +#define TXX9_SIDISR_TDIS 0x00000100 +#define TXX9_SIDISR_RDIS 0x00000080 +#define TXX9_SIDISR_STIS 0x00000040 +#define TXX9_SIDISR_RFDN_MASK 0x0000001f + +/* SICISR : Change Int. Status */ +#define TXX9_SICISR_OERS 0x00000020 +#define TXX9_SICISR_CTSS 0x00000010 +#define TXX9_SICISR_RBRKD 0x00000008 +#define TXX9_SICISR_TRDY 0x00000004 +#define TXX9_SICISR_TXALS 0x00000002 +#define TXX9_SICISR_UBRKD 0x00000001 + +/* SIFCR : FIFO Control */ +#define TXX9_SIFCR_SWRST 0x00008000 +#define TXX9_SIFCR_RDIL_MASK 0x00000180 +#define TXX9_SIFCR_RDIL_1 0x00000000 +#define TXX9_SIFCR_RDIL_4 0x00000080 +#define TXX9_SIFCR_RDIL_8 0x00000100 +#define TXX9_SIFCR_RDIL_12 0x00000180 +#define TXX9_SIFCR_RDIL_MAX 0x00000180 +#define TXX9_SIFCR_TDIL_MASK 0x00000018 +#define TXX9_SIFCR_TDIL_MASK 0x00000018 +#define TXX9_SIFCR_TDIL_1 0x00000000 +#define TXX9_SIFCR_TDIL_4 0x00000001 +#define TXX9_SIFCR_TDIL_8 0x00000010 +#define TXX9_SIFCR_TDIL_MAX 0x00000010 +#define TXX9_SIFCR_TFRST 0x00000004 +#define TXX9_SIFCR_RFRST 0x00000002 +#define TXX9_SIFCR_FRSTE 0x00000001 +#define TXX9_SIO_TX_FIFO 8 +#define TXX9_SIO_RX_FIFO 16 + +/* SIFLCR : Flow Control */ +#define TXX9_SIFLCR_RCS 0x00001000 +#define TXX9_SIFLCR_TES 0x00000800 +#define TXX9_SIFLCR_RTSSC 0x00000200 +#define TXX9_SIFLCR_RSDE 0x00000100 +#define TXX9_SIFLCR_TSDE 0x00000080 +#define TXX9_SIFLCR_RTSTL_MASK 0x0000001e +#define TXX9_SIFLCR_RTSTL_MAX 0x0000001e +#define TXX9_SIFLCR_TBRK 0x00000001 + +/* SIBGR : Baudrate Control */ +#define TXX9_SIBGR_BCLK_MASK 0x00000300 +#define TXX9_SIBGR_BCLK_T0 0x00000000 +#define TXX9_SIBGR_BCLK_T2 0x00000100 +#define TXX9_SIBGR_BCLK_T4 0x00000200 +#define TXX9_SIBGR_BCLK_T6 0x00000300 +#define TXX9_SIBGR_BRD_MASK 0x000000ff + +static inline unsigned int sio_in(struct uart_txx9_port *up, int offset) +{ + switch (up->port.iotype) { + default: + return __raw_readl(up->port.membase + offset); + case UPIO_PORT: + return inl(up->port.iobase + offset); + } +} + +static inline void +sio_out(struct uart_txx9_port *up, int offset, int value) +{ + switch (up->port.iotype) { + default: + __raw_writel(value, up->port.membase + offset); + break; + case UPIO_PORT: + outl(value, up->port.iobase + offset); + break; + } +} + +static inline void +sio_mask(struct uart_txx9_port *up, int offset, unsigned int value) +{ + sio_out(up, offset, sio_in(up, offset) & ~value); +} +static inline void +sio_set(struct uart_txx9_port *up, int offset, unsigned int value) +{ + sio_out(up, offset, sio_in(up, offset) | value); +} + +static inline void +sio_quot_set(struct uart_txx9_port *up, int quot) +{ + quot >>= 1; + if (quot < 256) + sio_out(up, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0); + else if (quot < (256 << 2)) + sio_out(up, TXX9_SIBGR, (quot >> 2) | TXX9_SIBGR_BCLK_T2); + else if (quot < (256 << 4)) + sio_out(up, TXX9_SIBGR, (quot >> 4) | TXX9_SIBGR_BCLK_T4); + else if (quot < (256 << 6)) + sio_out(up, TXX9_SIBGR, (quot >> 6) | TXX9_SIBGR_BCLK_T6); + else + sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6); +} + +static struct uart_txx9_port *to_uart_txx9_port(struct uart_port *port) +{ + return container_of(port, struct uart_txx9_port, port); +} + +static void serial_txx9_stop_tx(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); +} + +static void serial_txx9_start_tx(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); +} + +static void serial_txx9_stop_rx(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; +} + +static void serial_txx9_enable_ms(struct uart_port *port) +{ + /* TXX9-SIO can not control DTR... */ +} + +static void serial_txx9_initialize(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned int tmout = 10000; + + sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); + /* TX4925 BUG WORKAROUND. Accessing SIOC register + * immediately after soft reset causes bus error. */ + mmiowb(); + udelay(1); + while ((sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) && --tmout) + udelay(1); + /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1); + /* initial settings */ + sio_out(up, TXX9_SILCR, + TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | + ((up->port.flags & UPF_TXX9_USE_SCLK) ? + TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); + sio_quot_set(up, uart_get_divisor(port, 9600)); + sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); + sio_out(up, TXX9_SIDICR, 0); +} + +static inline void +receive_chars(struct uart_txx9_port *up, unsigned int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch; + unsigned int disr = *status; + int max_count = 256; + char flag; + unsigned int next_ignore_status_mask; + + do { + ch = sio_in(up, TXX9_SIRFIFO); + flag = TTY_NORMAL; + up->port.icount.rx++; + + /* mask out RFDN_MASK bit added by previous overrun */ + next_ignore_status_mask = + up->port.ignore_status_mask & ~TXX9_SIDISR_RFDN_MASK; + if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | + TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) { + /* + * For statistics only + */ + if (disr & TXX9_SIDISR_UBRK) { + disr &= ~(TXX9_SIDISR_UFER | TXX9_SIDISR_UPER); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (disr & TXX9_SIDISR_UPER) + up->port.icount.parity++; + else if (disr & TXX9_SIDISR_UFER) + up->port.icount.frame++; + if (disr & TXX9_SIDISR_UOER) { + up->port.icount.overrun++; + /* + * The receiver read buffer still hold + * a char which caused overrun. + * Ignore next char by adding RFDN_MASK + * to ignore_status_mask temporarily. + */ + next_ignore_status_mask |= + TXX9_SIDISR_RFDN_MASK; + } + + /* + * Mask off conditions which should be ingored. + */ + disr &= up->port.read_status_mask; + + if (disr & TXX9_SIDISR_UBRK) { + flag = TTY_BREAK; + } else if (disr & TXX9_SIDISR_UPER) + flag = TTY_PARITY; + else if (disr & TXX9_SIDISR_UFER) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag); + + ignore_char: + up->port.ignore_status_mask = next_ignore_status_mask; + disr = sio_in(up, TXX9_SIDISR); + } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); + *status = disr; +} + +static inline void transmit_chars(struct uart_txx9_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + sio_out(up, TXX9_SITFIFO, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_txx9_stop_tx(&up->port); + return; + } + + count = TXX9_SIO_TX_FIFO; + do { + sio_out(up, TXX9_SITFIFO, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_txx9_stop_tx(&up->port); +} + +static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) +{ + int pass_counter = 0; + struct uart_txx9_port *up = dev_id; + unsigned int status; + + while (1) { + spin_lock(&up->port.lock); + status = sio_in(up, TXX9_SIDISR); + if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE)) + status &= ~TXX9_SIDISR_TDIS; + if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | + TXX9_SIDISR_TOUT))) { + spin_unlock(&up->port.lock); + break; + } + + if (status & TXX9_SIDISR_RDIS) + receive_chars(up, &status); + if (status & TXX9_SIDISR_TDIS) + transmit_chars(up); + /* Clear TX/RX Int. Status */ + sio_mask(up, TXX9_SIDISR, + TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | + TXX9_SIDISR_TOUT); + spin_unlock(&up->port.lock); + + if (pass_counter++ > PASS_LIMIT) + break; + } + + return pass_counter ? IRQ_HANDLED : IRQ_NONE; +} + +static unsigned int serial_txx9_tx_empty(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_txx9_get_mctrl(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned int ret; + + /* no modem control lines */ + ret = TIOCM_CAR | TIOCM_DSR; + ret |= (sio_in(up, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC) ? 0 : TIOCM_RTS; + ret |= (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS) ? 0 : TIOCM_CTS; + + return ret; +} + +static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + + if (mctrl & TIOCM_RTS) + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); + else + sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); +} + +static void serial_txx9_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + else + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || (CONFIG_CONSOLE_POLL) +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_txx9_port *up) +{ + unsigned int tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + while (--tmout && + !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) + udelay(1); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) + udelay(1); + } +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial_txx9_get_poll_char(struct uart_port *port) +{ + unsigned int ier; + unsigned char c; + struct uart_txx9_port *up = to_uart_txx9_port(port); + + /* + * First save the IER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + + while (sio_in(up, TXX9_SIDISR) & TXX9_SIDISR_UVALID) + ; + + c = sio_in(up, TXX9_SIRFIFO); + + /* + * Finally, clear RX interrupt status + * and restore the IER + */ + sio_mask(up, TXX9_SIDISR, TXX9_SIDISR_RDIS); + sio_out(up, TXX9_SIDICR, ier); + return c; +} + + +static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) +{ + unsigned int ier; + struct uart_txx9_port *up = to_uart_txx9_port(port); + + /* + * First save the IER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + + wait_for_xmitr(up); + /* + * Send the character out. + * If a LF, also do CR... + */ + sio_out(up, TXX9_SITFIFO, c); + if (c == 10) { + wait_for_xmitr(up); + sio_out(up, TXX9_SITFIFO, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, TXX9_SIDICR, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +static int serial_txx9_startup(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned long flags; + int retval; + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + /* clear reset */ + sio_mask(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + sio_out(up, TXX9_SIDICR, 0); + + /* + * Clear the interrupt registers. + */ + sio_out(up, TXX9_SIDISR, 0); + + retval = request_irq(up->port.irq, serial_txx9_interrupt, + IRQF_SHARED, "serial_txx9", up); + if (retval) + return retval; + + /* + * Now, initialize the UART + */ + spin_lock_irqsave(&up->port.lock, flags); + serial_txx9_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* Enable RX/TX */ + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); + + /* + * Finally, enable interrupts. + */ + sio_set(up, TXX9_SIDICR, TXX9_SIDICR_RIE); + + return 0; +} + +static void serial_txx9_shutdown(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned long flags; + + /* + * Disable interrupts from this port + */ + sio_out(up, TXX9_SIDICR, 0); /* disable all intrs */ + + spin_lock_irqsave(&up->port.lock, flags); + serial_txx9_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition + */ + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + +#ifdef CONFIG_SERIAL_TXX9_CONSOLE + if (up->port.cons && up->port.line == up->port.cons->index) { + free_irq(up->port.irq, up); + return; + } +#endif + /* reset FIFOs */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + /* clear reset */ + sio_mask(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + + /* Disable RX/TX */ + sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); + + free_irq(up->port.irq, up); +} + +static void +serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + unsigned int cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + /* + * We don't support modem control lines. + */ + termios->c_cflag &= ~(HUPCL | CMSPAR); + termios->c_cflag |= CLOCAL; + + cval = sio_in(up, TXX9_SILCR); + /* byte size and parity */ + cval &= ~TXX9_SILCR_UMODE_MASK; + switch (termios->c_cflag & CSIZE) { + case CS7: + cval |= TXX9_SILCR_UMODE_7BIT; + break; + default: + case CS5: /* not supported */ + case CS6: /* not supported */ + case CS8: + cval |= TXX9_SILCR_UMODE_8BIT; + break; + } + + cval &= ~TXX9_SILCR_USBL_MASK; + if (termios->c_cflag & CSTOPB) + cval |= TXX9_SILCR_USBL_2BIT; + else + cval |= TXX9_SILCR_USBL_1BIT; + cval &= ~(TXX9_SILCR_UPEN | TXX9_SILCR_UEPS); + if (termios->c_cflag & PARENB) + cval |= TXX9_SILCR_UPEN; + if (!(termios->c_cflag & PARODD)) + cval |= TXX9_SILCR_UEPS; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16/2); + quot = uart_get_divisor(port, baud); + + /* Set up FIFOs */ + /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ + fcr = TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = TXX9_SIDISR_UOER | + TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= TXX9_SIDISR_UBRK; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= TXX9_SIDISR_UPER | TXX9_SIDISR_UFER; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= TXX9_SIDISR_UBRK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= TXX9_SIDISR_UOER; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= TXX9_SIDISR_RDIS; + + /* CTS flow control flag */ + if ((termios->c_cflag & CRTSCTS) && + (up->port.flags & UPF_TXX9_HAVE_CTS_LINE)) { + sio_set(up, TXX9_SIFLCR, + TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES); + } else { + sio_mask(up, TXX9_SIFLCR, + TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES); + } + + sio_out(up, TXX9_SILCR, cval); + sio_quot_set(up, quot); + sio_out(up, TXX9_SIFCR, fcr); + + serial_txx9_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial_txx9_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + /* + * If oldstate was -1 this is called from + * uart_configure_port(). In this case do not initialize the + * port now, because the port was already initialized (for + * non-console port) or should not be initialized here (for + * console port). If we initialized the port here we lose + * serial console settings. + */ + if (state == 0 && oldstate != -1) + serial_txx9_initialize(port); +} + +static int serial_txx9_request_resource(struct uart_txx9_port *up) +{ + unsigned int size = TXX9_REGION_SIZE; + int ret = 0; + + switch (up->port.iotype) { + default: + if (!up->port.mapbase) + break; + + if (!request_mem_region(up->port.mapbase, size, "serial_txx9")) { + ret = -EBUSY; + break; + } + + if (up->port.flags & UPF_IOREMAP) { + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) { + release_mem_region(up->port.mapbase, size); + ret = -ENOMEM; + } + } + break; + + case UPIO_PORT: + if (!request_region(up->port.iobase, size, "serial_txx9")) + ret = -EBUSY; + break; + } + return ret; +} + +static void serial_txx9_release_resource(struct uart_txx9_port *up) +{ + unsigned int size = TXX9_REGION_SIZE; + + switch (up->port.iotype) { + default: + if (!up->port.mapbase) + break; + + if (up->port.flags & UPF_IOREMAP) { + iounmap(up->port.membase); + up->port.membase = NULL; + } + + release_mem_region(up->port.mapbase, size); + break; + + case UPIO_PORT: + release_region(up->port.iobase, size); + break; + } +} + +static void serial_txx9_release_port(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + serial_txx9_release_resource(up); +} + +static int serial_txx9_request_port(struct uart_port *port) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + return serial_txx9_request_resource(up); +} + +static void serial_txx9_config_port(struct uart_port *port, int uflags) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + int ret; + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + ret = serial_txx9_request_resource(up); + if (ret < 0) + return; + port->type = PORT_TXX9; + up->port.fifosize = TXX9_SIO_TX_FIFO; + +#ifdef CONFIG_SERIAL_TXX9_CONSOLE + if (up->port.line == up->port.cons->index) + return; +#endif + serial_txx9_initialize(port); +} + +static const char * +serial_txx9_type(struct uart_port *port) +{ + return "txx9"; +} + +static struct uart_ops serial_txx9_pops = { + .tx_empty = serial_txx9_tx_empty, + .set_mctrl = serial_txx9_set_mctrl, + .get_mctrl = serial_txx9_get_mctrl, + .stop_tx = serial_txx9_stop_tx, + .start_tx = serial_txx9_start_tx, + .stop_rx = serial_txx9_stop_rx, + .enable_ms = serial_txx9_enable_ms, + .break_ctl = serial_txx9_break_ctl, + .startup = serial_txx9_startup, + .shutdown = serial_txx9_shutdown, + .set_termios = serial_txx9_set_termios, + .pm = serial_txx9_pm, + .type = serial_txx9_type, + .release_port = serial_txx9_release_port, + .request_port = serial_txx9_request_port, + .config_port = serial_txx9_config_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_txx9_get_poll_char, + .poll_put_char = serial_txx9_put_poll_char, +#endif +}; + +static struct uart_txx9_port serial_txx9_ports[UART_NR]; + +static void __init serial_txx9_register_ports(struct uart_driver *drv, + struct device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + up->port.line = i; + up->port.ops = &serial_txx9_pops; + up->port.dev = dev; + if (up->port.iobase || up->port.mapbase) + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_TXX9_CONSOLE + +static void serial_txx9_console_putchar(struct uart_port *port, int ch) +{ + struct uart_txx9_port *up = to_uart_txx9_port(port); + + wait_for_xmitr(up); + sio_out(up, TXX9_SITFIFO, ch); +} + +/* + * 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. + */ +static void +serial_txx9_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_txx9_port *up = &serial_txx9_ports[co->index]; + unsigned int ier, flcr; + + /* + * First save the UER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + /* + * Disable flow-control if enabled (and unnecessary) + */ + flcr = sio_in(up, TXX9_SIFLCR); + if (!(up->port.flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES)) + sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES); + + uart_console_write(&up->port, s, count, serial_txx9_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, TXX9_SIFLCR, flcr); + sio_out(up, TXX9_SIDICR, ier); +} + +static int __init serial_txx9_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + struct uart_txx9_port *up; + int baud = 9600; + 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. + */ + if (co->index >= UART_NR) + co->index = 0; + up = &serial_txx9_ports[co->index]; + port = &up->port; + if (!port->ops) + return -ENODEV; + + serial_txx9_initialize(&up->port); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver serial_txx9_reg; +static struct console serial_txx9_console = { + .name = TXX9_TTY_NAME, + .write = serial_txx9_console_write, + .device = uart_console_device, + .setup = serial_txx9_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_txx9_reg, +}; + +static int __init serial_txx9_console_init(void) +{ + register_console(&serial_txx9_console); + return 0; +} +console_initcall(serial_txx9_console_init); + +#define SERIAL_TXX9_CONSOLE &serial_txx9_console +#else +#define SERIAL_TXX9_CONSOLE NULL +#endif + +static struct uart_driver serial_txx9_reg = { + .owner = THIS_MODULE, + .driver_name = "serial_txx9", + .dev_name = TXX9_TTY_NAME, + .major = TXX9_TTY_MAJOR, + .minor = TXX9_TTY_MINOR_START, + .nr = UART_NR, + .cons = SERIAL_TXX9_CONSOLE, +}; + +int __init early_serial_txx9_setup(struct uart_port *port) +{ + if (port->line >= ARRAY_SIZE(serial_txx9_ports)) + return -ENODEV; + + serial_txx9_ports[port->line].port = *port; + serial_txx9_ports[port->line].port.ops = &serial_txx9_pops; + serial_txx9_ports[port->line].port.flags |= + UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + return 0; +} + +static DEFINE_MUTEX(serial_txx9_mutex); + +/** + * serial_txx9_register_port - register a serial port + * @port: serial port template + * + * Configure the serial port specified by the request. + * + * The port is then probed and if necessary the IRQ is autodetected + * If this fails an error is returned. + * + * 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) +{ + int i; + struct uart_txx9_port *uart; + int ret = -ENOSPC; + + mutex_lock(&serial_txx9_mutex); + for (i = 0; i < UART_NR; i++) { + uart = &serial_txx9_ports[i]; + if (uart_match_port(&uart->port, port)) { + uart_remove_one_port(&serial_txx9_reg, &uart->port); + break; + } + } + if (i == UART_NR) { + /* Find unused port */ + for (i = 0; i < UART_NR; i++) { + uart = &serial_txx9_ports[i]; + if (!(uart->port.iobase || uart->port.mapbase)) + break; + } + } + if (i < UART_NR) { + uart->port.iobase = port->iobase; + uart->port.membase = port->membase; + uart->port.irq = port->irq; + uart->port.uartclk = port->uartclk; + uart->port.iotype = port->iotype; + uart->port.flags = port->flags + | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + uart->port.mapbase = port->mapbase; + if (port->dev) + uart->port.dev = port->dev; + ret = uart_add_one_port(&serial_txx9_reg, &uart->port); + if (ret == 0) + ret = uart->port.line; + } + mutex_unlock(&serial_txx9_mutex); + return ret; +} + +/** + * serial_txx9_unregister_port - remove a txx9 serial port at runtime + * @line: serial line number + * + * 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) +{ + struct uart_txx9_port *uart = &serial_txx9_ports[line]; + + mutex_lock(&serial_txx9_mutex); + uart_remove_one_port(&serial_txx9_reg, &uart->port); + uart->port.flags = 0; + uart->port.type = PORT_UNKNOWN; + uart->port.iobase = 0; + uart->port.mapbase = 0; + uart->port.membase = NULL; + uart->port.dev = NULL; + mutex_unlock(&serial_txx9_mutex); +} + +/* + * Register a set of serial devices attached to a platform device. + */ +static int __devinit serial_txx9_probe(struct platform_device *dev) +{ + struct uart_port *p = dev->dev.platform_data; + struct uart_port port; + int ret, i; + + memset(&port, 0, sizeof(struct uart_port)); + for (i = 0; p && p->uartclk != 0; p++, i++) { + port.iobase = p->iobase; + port.membase = p->membase; + port.irq = p->irq; + port.uartclk = p->uartclk; + port.iotype = p->iotype; + port.flags = p->flags; + port.mapbase = p->mapbase; + port.dev = &dev->dev; + ret = serial_txx9_register_port(&port); + if (ret < 0) { + dev_err(&dev->dev, "unable to register port at index %d " + "(IO%lx MEM%llx IRQ%d): %d\n", i, + p->iobase, (unsigned long long)p->mapbase, + p->irq, ret); + } + } + return 0; +} + +/* + * Remove serial ports registered against a platform device. + */ +static int __devexit serial_txx9_remove(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + if (up->port.dev == &dev->dev) + serial_txx9_unregister_port(i); + } + return 0; +} + +#ifdef CONFIG_PM +static int serial_txx9_suspend(struct platform_device *dev, pm_message_t state) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_suspend_port(&serial_txx9_reg, &up->port); + } + + return 0; +} + +static int serial_txx9_resume(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_resume_port(&serial_txx9_reg, &up->port); + } + + return 0; +} +#endif + +static struct platform_driver serial_txx9_plat_driver = { + .probe = serial_txx9_probe, + .remove = __devexit_p(serial_txx9_remove), +#ifdef CONFIG_PM + .suspend = serial_txx9_suspend, + .resume = serial_txx9_resume, +#endif + .driver = { + .name = "serial_txx9", + .owner = THIS_MODULE, + }, +}; + +#ifdef ENABLE_SERIAL_TXX9_PCI +/* + * Probe one serial board. Unfortunately, there is no rhyme nor reason + * to the arrangement of serial ports on a PCI card. + */ +static int __devinit +pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct uart_port port; + int line; + int rc; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + memset(&port, 0, sizeof(port)); + port.ops = &serial_txx9_pops; + port.flags |= UPF_TXX9_HAVE_CTS_LINE; + port.uartclk = 66670000; + port.irq = dev->irq; + port.iotype = UPIO_PORT; + port.iobase = pci_resource_start(dev, 1); + port.dev = &dev->dev; + line = serial_txx9_register_port(&port); + if (line < 0) { + printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line); + pci_disable_device(dev); + return line; + } + pci_set_drvdata(dev, &serial_txx9_ports[line]); + + return 0; +} + +static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) +{ + struct uart_txx9_port *up = pci_get_drvdata(dev); + + pci_set_drvdata(dev, NULL); + + if (up) { + serial_txx9_unregister_port(up->port.line); + pci_disable_device(dev); + } +} + +#ifdef CONFIG_PM +static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state) +{ + struct uart_txx9_port *up = pci_get_drvdata(dev); + + if (up) + uart_suspend_port(&serial_txx9_reg, &up->port); + pci_save_state(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + return 0; +} + +static int pciserial_txx9_resume_one(struct pci_dev *dev) +{ + struct uart_txx9_port *up = pci_get_drvdata(dev); + + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + if (up) + uart_resume_port(&serial_txx9_reg, &up->port); + return 0; +} +#endif + +static const struct pci_device_id serial_txx9_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC) }, + { 0, } +}; + +static struct pci_driver serial_txx9_pci_driver = { + .name = "serial_txx9", + .probe = pciserial_txx9_init_one, + .remove = __devexit_p(pciserial_txx9_remove_one), +#ifdef CONFIG_PM + .suspend = pciserial_txx9_suspend_one, + .resume = pciserial_txx9_resume_one, +#endif + .id_table = serial_txx9_pci_tbl, +}; + +MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); +#endif /* ENABLE_SERIAL_TXX9_PCI */ + +static struct platform_device *serial_txx9_plat_devs; + +static int __init serial_txx9_init(void) +{ + int ret; + + printk(KERN_INFO "%s version %s\n", serial_name, serial_version); + + ret = uart_register_driver(&serial_txx9_reg); + if (ret) + goto out; + + serial_txx9_plat_devs = platform_device_alloc("serial_txx9", -1); + if (!serial_txx9_plat_devs) { + ret = -ENOMEM; + goto unreg_uart_drv; + } + + ret = platform_device_add(serial_txx9_plat_devs); + if (ret) + goto put_dev; + + serial_txx9_register_ports(&serial_txx9_reg, + &serial_txx9_plat_devs->dev); + + ret = platform_driver_register(&serial_txx9_plat_driver); + if (ret) + goto del_dev; + +#ifdef ENABLE_SERIAL_TXX9_PCI + ret = pci_register_driver(&serial_txx9_pci_driver); +#endif + if (ret == 0) + goto out; + + del_dev: + platform_device_del(serial_txx9_plat_devs); + put_dev: + platform_device_put(serial_txx9_plat_devs); + unreg_uart_drv: + uart_unregister_driver(&serial_txx9_reg); + out: + return ret; +} + +static void __exit serial_txx9_exit(void) +{ + int i; + +#ifdef ENABLE_SERIAL_TXX9_PCI + pci_unregister_driver(&serial_txx9_pci_driver); +#endif + platform_driver_unregister(&serial_txx9_plat_driver); + platform_device_unregister(serial_txx9_plat_devs); + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + if (up->port.iobase || up->port.mapbase) + uart_remove_one_port(&serial_txx9_reg, &up->port); + } + + uart_unregister_driver(&serial_txx9_reg); +} + +module_init(serial_txx9_init); +module_exit(serial_txx9_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TX39/49 serial driver"); + +MODULE_ALIAS_CHARDEV_MAJOR(TXX9_TTY_MAJOR); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c new file mode 100644 index 000000000000..c291b3add1d2 --- /dev/null +++ b/drivers/tty/serial/sh-sci.c @@ -0,0 +1,2027 @@ +/* + * drivers/serial/sh-sci.c + * + * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) + * + * Copyright (C) 2002 - 2008 Paul Mundt + * Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). + * + * based off of the old drivers/char/sh-sci.c by: + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * Copyright (C) 2000 Sugioka Toshinobu + * Modified to support multiple serial ports. Stuart Menefy (May 2000). + * Modified to support SecureEdge. David McCullough (2002) + * Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003). + * Removed SH7300 support (Jul 2007). + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#undef DEBUG + +#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 +#include + +#ifdef CONFIG_SUPERH +#include +#endif + +#ifdef CONFIG_H8300 +#include +#endif + +#include "sh-sci.h" + +struct sci_port { + struct uart_port port; + + /* Port type */ + unsigned int type; + + /* Port IRQs: ERI, RXI, TXI, BRI (optional) */ + unsigned int irqs[SCIx_NR_IRQS]; + + /* Port enable callback */ + void (*enable)(struct uart_port *port); + + /* Port disable callback */ + void (*disable)(struct uart_port *port); + + /* Break timer */ + struct timer_list break_timer; + int break_flag; + + /* Interface clock */ + struct clk *iclk; + /* Function clock */ + struct clk *fclk; + + struct list_head node; + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct device *dma_dev; + unsigned int slave_tx; + unsigned int slave_rx; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx[2]; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx[2]; + dma_cookie_t active_rx; + struct scatterlist sg_tx; + unsigned int sg_len_tx; + struct scatterlist sg_rx[2]; + size_t buf_len_rx; + struct sh_dmae_slave param_tx; + struct sh_dmae_slave param_rx; + struct work_struct work_tx; + struct work_struct work_rx; + struct timer_list rx_timer; + unsigned int rx_timeout; +#endif +}; + +struct sh_sci_priv { + spinlock_t lock; + struct list_head ports; + struct notifier_block clk_nb; +}; + +/* Function prototypes */ +static void sci_stop_tx(struct uart_port *port); + +#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS + +static struct sci_port sci_ports[SCI_NPORTS]; +static struct uart_driver sci_uart_driver; + +static inline struct sci_port * +to_sci_port(struct uart_port *uart) +{ + return container_of(uart, struct sci_port, port); +} + +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) + +#ifdef CONFIG_CONSOLE_POLL +static inline void handle_error(struct uart_port *port) +{ + /* Clear error flags */ + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); +} + +static int sci_poll_get_char(struct uart_port *port) +{ + unsigned short status; + int c; + + do { + status = sci_in(port, SCxSR); + if (status & SCxSR_ERRORS(port)) { + handle_error(port); + continue; + } + break; + } while (1); + + if (!(status & SCxSR_RDxF(port))) + return NO_POLL_CHAR; + + c = sci_in(port, SCxRDR); + + /* Dummy read */ + sci_in(port, SCxSR); + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + + return c; +} +#endif + +static void sci_poll_put_char(struct uart_port *port, unsigned char c) +{ + unsigned short status; + + do { + status = sci_in(port, SCxSR); + } while (!(status & SCxSR_TDxE(port))); + + sci_out(port, SCxTDR, c); + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); +} +#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ + +#if defined(__H8300H__) || defined(__H8300S__) +static void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + int ch = (port->mapbase - SMR0) >> 3; + + /* set DDR regs */ + H8300_GPIO_DDR(h8300_sci_pins[ch].port, + h8300_sci_pins[ch].rx, + H8300_GPIO_INPUT); + H8300_GPIO_DDR(h8300_sci_pins[ch].port, + h8300_sci_pins[ch].tx, + H8300_GPIO_OUTPUT); + + /* tx mark output*/ + H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx; +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + if (port->mapbase == 0xA4400000) { + __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); + __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); + } else if (port->mapbase == 0xA4410000) + __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (cflag & CRTSCTS) { + /* enable RTS/CTS */ + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 9-2; enable all scif pins but sck */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xfc03), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 9-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xfc03), PORT_PVCR); + } + } else { + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 5-2; enable only tx and rx */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xffc3), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 5-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xffc3), PORT_PVCR); + } + } +} +#elif defined(CONFIG_CPU_SH3) +/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ + __raw_writew(data & 0x0fcf, SCPCR); + + if (!(cflag & CRTSCTS)) { + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + + data = __raw_readb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + __raw_writeb(data & 0xbf, SCPDR); + } +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7722) +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + unsigned short data; + + if (port->mapbase == 0xffe00000) { + data = __raw_readw(PSCR); + data &= ~0x03cf; + if (!(cflag & CRTSCTS)) + data |= 0x0340; + + __raw_writew(data, PSCR); + } +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) || \ + defined(CONFIG_CPU_SUBTYPE_SHX3) +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + if (!(cflag & CRTSCTS)) + __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */ +} +#elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A) +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + if (!(cflag & CRTSCTS)) + __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */ +} +#else +static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) +{ + /* Nothing to do */ +} +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) +static int scif_txfill(struct uart_port *port) +{ + return sci_in(port, SCTFDR) & 0xff; +} + +static int scif_txroom(struct uart_port *port) +{ + return SCIF_TXROOM_MAX - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) +{ + return sci_in(port, SCRFDR) & 0xff; +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +static int scif_txfill(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) + /* SCIF0/1*/ + return sci_in(port, SCTFDR) & 0xff; + else + /* SCIF2 */ + return sci_in(port, SCFDR) >> 8; +} + +static int scif_txroom(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) + /* SCIF0/1*/ + return SCIF_TXROOM_MAX - scif_txfill(port); + else + /* SCIF2 */ + return SCIF2_TXROOM_MAX - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) +{ + if ((port->mapbase == 0xffe00000) || + (port->mapbase == 0xffe08000)) { + /* SCIF0/1*/ + return sci_in(port, SCRFDR) & 0xff; + } else { + /* SCIF2 */ + return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; + } +} +#elif defined(CONFIG_ARCH_SH7372) +static int scif_txfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) >> 8; + else + return sci_in(port, SCTFDR); +} + +static int scif_txroom(struct uart_port *port) +{ + return port->fifosize - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + else + return sci_in(port, SCRFDR); +} +#else +static int scif_txfill(struct uart_port *port) +{ + return sci_in(port, SCFDR) >> 8; +} + +static int scif_txroom(struct uart_port *port) +{ + return SCIF_TXROOM_MAX - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) +{ + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; +} +#endif + +static int sci_txfill(struct uart_port *port) +{ + return !(sci_in(port, SCxSR) & SCI_TDRE); +} + +static int sci_txroom(struct uart_port *port) +{ + return !sci_txfill(port); +} + +static int sci_rxfill(struct uart_port *port) +{ + return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; +} + +/* ********************************************************************** * + * the interrupt related routines * + * ********************************************************************** */ + +static void sci_transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned int stopped = uart_tx_stopped(port); + unsigned short status; + unsigned short ctrl; + int count; + + status = sci_in(port, SCxSR); + if (!(status & SCxSR_TDxE(port))) { + ctrl = sci_in(port, SCSCR); + if (uart_circ_empty(xmit)) + ctrl &= ~SCI_CTRL_FLAGS_TIE; + else + ctrl |= SCI_CTRL_FLAGS_TIE; + sci_out(port, SCSCR, ctrl); + return; + } + + if (port->type == PORT_SCI) + count = sci_txroom(port); + else + count = scif_txroom(port); + + do { + unsigned char c; + + if (port->x_char) { + c = port->x_char; + port->x_char = 0; + } else if (!uart_circ_empty(xmit) && !stopped) { + c = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } else { + break; + } + + sci_out(port, SCxTDR, c); + + port->icount.tx++; + } while (--count > 0); + + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + if (uart_circ_empty(xmit)) { + sci_stop_tx(port); + } else { + ctrl = sci_in(port, SCSCR); + + if (port->type != PORT_SCI) { + sci_in(port, SCxSR); /* Dummy read */ + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + } + + ctrl |= SCI_CTRL_FLAGS_TIE; + sci_out(port, SCSCR, ctrl); + } +} + +/* On SH3, SCIF may read end-of-break as a space->mark char */ +#define STEPFN(c) ({int __c = (c); (((__c-1)|(__c)) == -1); }) + +static inline void sci_receive_chars(struct uart_port *port) +{ + struct sci_port *sci_port = to_sci_port(port); + struct tty_struct *tty = port->state->port.tty; + int i, count, copied = 0; + unsigned short status; + unsigned char flag; + + status = sci_in(port, SCxSR); + if (!(status & SCxSR_RDxF(port))) + return; + + while (1) { + if (port->type == PORT_SCI) + count = sci_rxfill(port); + else + count = scif_rxfill(port); + + /* Don't copy more bytes than there is room for in the buffer */ + count = tty_buffer_request_room(tty, count); + + /* If for any reason we can't copy more data, we're done! */ + if (count == 0) + break; + + if (port->type == PORT_SCI) { + char c = sci_in(port, SCxRDR); + if (uart_handle_sysrq_char(port, c) || + sci_port->break_flag) + count = 0; + else + tty_insert_flip_char(tty, c, TTY_NORMAL); + } else { + for (i = 0; i < count; i++) { + char c = sci_in(port, SCxRDR); + status = sci_in(port, SCxSR); +#if defined(CONFIG_CPU_SH3) + /* Skip "chars" during break */ + if (sci_port->break_flag) { + if ((c == 0) && + (status & SCxSR_FER(port))) { + count--; i--; + continue; + } + + /* Nonzero => end-of-break */ + dev_dbg(port->dev, "debounce<%02x>\n", c); + sci_port->break_flag = 0; + + if (STEPFN(c)) { + count--; i--; + continue; + } + } +#endif /* CONFIG_CPU_SH3 */ + if (uart_handle_sysrq_char(port, c)) { + count--; i--; + continue; + } + + /* Store data and status */ + if (status & SCxSR_FER(port)) { + flag = TTY_FRAME; + dev_notice(port->dev, "frame error\n"); + } else if (status & SCxSR_PER(port)) { + flag = TTY_PARITY; + dev_notice(port->dev, "parity error\n"); + } else + flag = TTY_NORMAL; + + tty_insert_flip_char(tty, c, flag); + } + } + + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + + copied += count; + port->icount.rx += count; + } + + if (copied) { + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tty); + } else { + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + } +} + +#define SCI_BREAK_JIFFIES (HZ/20) +/* The sci generates interrupts during the break, + * 1 per millisecond or so during the break period, for 9600 baud. + * So dont bother disabling interrupts. + * But dont want more than 1 break event. + * Use a kernel timer to periodically poll the rx line until + * the break is finished. + */ +static void sci_schedule_break_timer(struct sci_port *port) +{ + port->break_timer.expires = jiffies + SCI_BREAK_JIFFIES; + add_timer(&port->break_timer); +} +/* Ensure that two consecutive samples find the break over. */ +static void sci_break_timer(unsigned long data) +{ + struct sci_port *port = (struct sci_port *)data; + + if (sci_rxd_in(&port->port) == 0) { + port->break_flag = 1; + sci_schedule_break_timer(port); + } else if (port->break_flag == 1) { + /* break is over. */ + port->break_flag = 2; + sci_schedule_break_timer(port); + } else + port->break_flag = 0; +} + +static inline int sci_handle_errors(struct uart_port *port) +{ + int copied = 0; + unsigned short status = sci_in(port, SCxSR); + struct tty_struct *tty = port->state->port.tty; + + if (status & SCxSR_ORER(port)) { + /* overrun error */ + if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) + copied++; + + dev_notice(port->dev, "overrun error"); + } + + if (status & SCxSR_FER(port)) { + if (sci_rxd_in(port) == 0) { + /* Notify of BREAK */ + struct sci_port *sci_port = to_sci_port(port); + + if (!sci_port->break_flag) { + sci_port->break_flag = 1; + sci_schedule_break_timer(sci_port); + + /* Do sysrq handling. */ + if (uart_handle_break(port)) + return 0; + + dev_dbg(port->dev, "BREAK detected\n"); + + if (tty_insert_flip_char(tty, 0, TTY_BREAK)) + copied++; + } + + } else { + /* frame error */ + if (tty_insert_flip_char(tty, 0, TTY_FRAME)) + copied++; + + dev_notice(port->dev, "frame error\n"); + } + } + + if (status & SCxSR_PER(port)) { + /* parity error */ + if (tty_insert_flip_char(tty, 0, TTY_PARITY)) + copied++; + + dev_notice(port->dev, "parity error"); + } + + if (copied) + tty_flip_buffer_push(tty); + + return copied; +} + +static inline int sci_handle_fifo_overrun(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + int copied = 0; + + if (port->type != PORT_SCIF) + return 0; + + if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { + sci_out(port, SCLSR, 0); + + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_flip_buffer_push(tty); + + dev_notice(port->dev, "overrun error\n"); + copied++; + } + + return copied; +} + +static inline int sci_handle_breaks(struct uart_port *port) +{ + int copied = 0; + unsigned short status = sci_in(port, SCxSR); + struct tty_struct *tty = port->state->port.tty; + struct sci_port *s = to_sci_port(port); + + if (uart_handle_break(port)) + return 0; + + if (!s->break_flag && status & SCxSR_BRK(port)) { +#if defined(CONFIG_CPU_SH3) + /* Debounce break */ + s->break_flag = 1; +#endif + /* Notify of BREAK */ + if (tty_insert_flip_char(tty, 0, TTY_BREAK)) + copied++; + + dev_dbg(port->dev, "BREAK detected\n"); + } + + if (copied) + tty_flip_buffer_push(tty); + + copied += sci_handle_fifo_overrun(port); + + return copied; +} + +static irqreturn_t sci_rx_interrupt(int irq, void *ptr) +{ +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + + if (s->chan_rx) { + u16 scr = sci_in(port, SCSCR); + u16 ssr = sci_in(port, SCxSR); + + /* Disable future Rx interrupts */ + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { + scr &= ~SCI_CTRL_FLAGS_RIE; + } + sci_out(port, SCSCR, scr); + /* Clear current interrupt */ + sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + return IRQ_HANDLED; + } +#endif + + /* I think sci_receive_chars has to be called irrespective + * of whether the I_IXOFF is set, otherwise, how is the interrupt + * to be disabled? + */ + sci_receive_chars(ptr); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_tx_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + sci_transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_er_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + + /* Handle errors */ + if (port->type == PORT_SCI) { + if (sci_handle_errors(port)) { + /* discard character in rx buffer */ + sci_in(port, SCxSR); + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + } + } else { + sci_handle_fifo_overrun(port); + sci_rx_interrupt(irq, ptr); + } + + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + + /* Kick the transmission */ + sci_tx_interrupt(irq, ptr); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_br_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + + /* Handle BREAKs */ + sci_handle_breaks(port); + sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) +{ + unsigned short ssr_status, scr_status, err_enabled; + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + irqreturn_t ret = IRQ_NONE; + + ssr_status = sci_in(port, SCxSR); + scr_status = sci_in(port, SCSCR); + err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE); + + /* Tx Interrupt */ + if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) && + !s->chan_tx) + ret = sci_tx_interrupt(irq, ptr); + /* + * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / + * DR flags + */ + if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && + (scr_status & SCI_CTRL_FLAGS_RIE)) + ret = sci_rx_interrupt(irq, ptr); + /* Error Interrupt */ + if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) + ret = sci_er_interrupt(irq, ptr); + /* Break Interrupt */ + if ((ssr_status & SCxSR_BRK(port)) && err_enabled) + ret = sci_br_interrupt(irq, ptr); + + return ret; +} + +/* + * Here we define a transistion notifier so that we can update all of our + * ports' baud rate when the peripheral clock changes. + */ +static int sci_notifier(struct notifier_block *self, + unsigned long phase, void *p) +{ + struct sh_sci_priv *priv = container_of(self, + struct sh_sci_priv, clk_nb); + struct sci_port *sci_port; + unsigned long flags; + + if ((phase == CPUFREQ_POSTCHANGE) || + (phase == CPUFREQ_RESUMECHANGE)) { + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(sci_port, &priv->ports, node) + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + spin_unlock_irqrestore(&priv->lock, flags); + } + + return NOTIFY_OK; +} + +static void sci_clk_enable(struct uart_port *port) +{ + struct sci_port *sci_port = to_sci_port(port); + + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); +} + +static void sci_clk_disable(struct uart_port *port) +{ + struct sci_port *sci_port = to_sci_port(port); + + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); +} + +static int sci_request_irq(struct sci_port *port) +{ + int i; + irqreturn_t (*handlers[4])(int irq, void *ptr) = { + sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, + sci_br_interrupt, + }; + const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", + "SCI Transmit Data Empty", "SCI Break" }; + + if (port->irqs[0] == port->irqs[1]) { + if (unlikely(!port->irqs[0])) + return -ENODEV; + + if (request_irq(port->irqs[0], sci_mpxed_interrupt, + IRQF_DISABLED, "sci", port)) { + dev_err(port->port.dev, "Can't allocate IRQ\n"); + return -ENODEV; + } + } else { + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (unlikely(!port->irqs[i])) + continue; + + if (request_irq(port->irqs[i], handlers[i], + IRQF_DISABLED, desc[i], port)) { + dev_err(port->port.dev, "Can't allocate IRQ\n"); + return -ENODEV; + } + } + } + + return 0; +} + +static void sci_free_irq(struct sci_port *port) +{ + int i; + + if (port->irqs[0] == port->irqs[1]) + free_irq(port->irqs[0], port); + else { + for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { + if (!port->irqs[i]) + continue; + + free_irq(port->irqs[i], port); + } + } +} + +static unsigned int sci_tx_empty(struct uart_port *port) +{ + unsigned short status = sci_in(port, SCxSR); + unsigned short in_tx_fifo = scif_txfill(port); + + return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; +} + +static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ + /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ + /* If you have signals for DTR and DCD, please implement here. */ +} + +static unsigned int sci_get_mctrl(struct uart_port *port) +{ + /* This routine is used for getting signals of: DTR, DCD, DSR, RI, + and CTS/RTS */ + + return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; +} + +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static void sci_dma_tx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + spin_lock_irqsave(&port->lock, flags); + + xmit->tail += sg_dma_len(&s->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += sg_dma_len(&s->sg_tx); + + async_tx_ack(s->desc_tx); + s->cookie_tx = -EINVAL; + s->desc_tx = NULL; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!uart_circ_empty(xmit)) { + schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Locking: called with port lock held */ +static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, + size_t count) +{ + struct uart_port *port = &s->port; + int i, active, room; + + room = tty_buffer_request_room(tty, count); + + if (s->active_rx == s->cookie_rx[0]) { + active = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + active = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return 0; + } + + if (room < count) + dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + count - room); + if (!room) + return room; + + for (i = 0; i < room; i++) + tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], + TTY_NORMAL); + + port->icount.rx += room; + + return room; +} + +static void sci_dma_rx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct tty_struct *tty = port->state->port.tty; + unsigned long flags; + int count; + + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); + + spin_lock_irqsave(&port->lock, flags); + + count = sci_dma_rx_push(s, tty, s->buf_len_rx); + + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + schedule_work(&s->work_rx); +} + +static void sci_start_rx(struct uart_port *port); +static void sci_start_tx(struct uart_port *port); + +static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + + s->chan_rx = NULL; + s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + dma_release_channel(chan); + if (sg_dma_address(&s->sg_rx[0])) + dma_free_coherent(port->dev, s->buf_len_rx * 2, + sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + if (enable_pio) + sci_start_rx(port); +} + +static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + + s->chan_tx = NULL; + s->cookie_tx = -EINVAL; + dma_release_channel(chan); + if (enable_pio) + sci_start_tx(port); +} + +static void sci_submit_rx(struct sci_port *s) +{ + struct dma_chan *chan = s->chan_rx; + int i; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + struct dma_async_tx_descriptor *desc; + + desc = chan->device->device_prep_slave_sg(chan, + sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); + + if (desc) { + s->desc_rx[i] = desc; + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[i] = desc->tx_submit(desc); + } + + if (!desc || s->cookie_rx[i] < 0) { + if (i) { + async_tx_ack(s->desc_rx[0]); + s->cookie_rx[0] = -EINVAL; + } + if (desc) { + async_tx_ack(desc); + s->cookie_rx[i] = -EINVAL; + } + dev_warn(s->port.dev, + "failed to re-start DMA, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); + } + + s->active_rx = s->cookie_rx[0]; + + dma_async_issue_pending(chan); +} + +static void work_fn_rx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_rx); + struct uart_port *port = &s->port; + struct dma_async_tx_descriptor *desc; + int new; + + if (s->active_rx == s->cookie_rx[0]) { + new = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + new = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return; + } + desc = s->desc_rx[new]; + + if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != + DMA_SUCCESS) { + /* Handle incomplete DMA receive */ + struct tty_struct *tty = port->state->port.tty; + struct dma_chan *chan = s->chan_rx; + struct sh_desc *sh_desc = container_of(desc, struct sh_desc, + async_tx); + unsigned long flags; + int count; + + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", + sh_desc->partial, sh_desc->cookie); + + spin_lock_irqsave(&port->lock, flags); + count = sci_dma_rx_push(s, tty, sh_desc->partial); + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + sci_submit_rx(s); + + return; + } + + s->cookie_rx[new] = desc->tx_submit(desc); + if (s->cookie_rx[new] < 0) { + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); + return; + } + + s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); +} + +static void work_fn_tx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_tx); + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + struct scatterlist *sg = &s->sg_tx; + + /* + * DMA is idle now. + * Port xmit buffer is already mapped, and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, we have to + * transmit till the end, and then the rest. Take the port lock to get a + * consistent xmit buffer state. + */ + spin_lock_irq(&port->lock); + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); + spin_unlock_irq(&port->lock); + + BUG_ON(!sg_dma_len(sg)); + + desc = chan->device->device_prep_slave_sg(chan, + sg, s->sg_len_tx, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + + spin_lock_irq(&port->lock); + s->desc_tx = desc; + desc->callback = sci_dma_tx_complete; + desc->callback_param = s; + spin_unlock_irq(&port->lock); + s->cookie_tx = desc->tx_submit(desc); + if (s->cookie_tx < 0) { + dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, + xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + + dma_async_issue_pending(chan); +} +#endif + +static void sci_start_tx(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + unsigned short ctrl; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); + } + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); +#endif + if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); + } +} + +static void sci_stop_tx(struct uart_port *port) +{ + unsigned short ctrl; + + /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x8000; + ctrl &= ~SCI_CTRL_FLAGS_TIE; + sci_out(port, SCSCR, ctrl); +} + +static void sci_start_rx(struct uart_port *port) +{ + unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; + + /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ + ctrl |= sci_in(port, SCSCR); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x4000; + sci_out(port, SCSCR, ctrl); +} + +static void sci_stop_rx(struct uart_port *port) +{ + unsigned short ctrl; + + /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x4000; + ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); + sci_out(port, SCSCR, ctrl); +} + +static void sci_enable_ms(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +static void sci_break_ctl(struct uart_port *port, int break_state) +{ + /* Nothing here yet .. */ +} + +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static bool filter(struct dma_chan *chan, void *slave) +{ + struct sh_dmae_slave *param = slave; + + dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, + param->slave_id); + + if (param->dma_dev == chan->device->dev) { + chan->private = param; + return true; + } else { + return false; + } +} + +static void rx_timer_fn(unsigned long arg) +{ + struct sci_port *s = (struct sci_port *)arg; + struct uart_port *port = &s->port; + u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } + sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); + dev_dbg(port->dev, "DMA Rx timed out\n"); + schedule_work(&s->work_rx); +} + +static void sci_request_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct sh_dmae_slave *param; + struct dma_chan *chan; + dma_cap_mask_t mask; + int nent; + + dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, + port->line, s->dma_dev); + + if (!s->dma_dev) + return; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + param = &s->param_tx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ + param->slave_id = s->slave_tx; + param->dma_dev = s->dma_dev; + + s->cookie_tx = -EINVAL; + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); + if (chan) { + s->chan_tx = chan; + sg_init_table(&s->sg_tx, 1); + /* UART circular tx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), + UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); + if (!nent) + sci_tx_dma_release(s, false); + else + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&s->sg_tx), + port->state->xmit.buf, sg_dma_address(&s->sg_tx)); + + s->sg_len_tx = nent; + + INIT_WORK(&s->work_tx, work_fn_tx); + } + + param = &s->param_rx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ + param->slave_id = s->slave_rx; + param->dma_dev = s->dma_dev; + + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); + if (chan) { + dma_addr_t dma[2]; + void *buf[2]; + int i; + + s->chan_rx = chan; + + s->buf_len_rx = 2 * max(16, (int)port->fifosize); + buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, + &dma[0], GFP_KERNEL); + + if (!buf[0]) { + dev_warn(port->dev, + "failed to allocate dma buffer, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + + buf[1] = buf[0] + s->buf_len_rx; + dma[1] = dma[0] + s->buf_len_rx; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + + sg_init_table(sg, 1); + sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, + (int)buf[i] & ~PAGE_MASK); + sg_dma_address(sg) = dma[i]; + } + + INIT_WORK(&s->work_rx, work_fn_rx); + setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); + + sci_submit_rx(s); + } +} + +static void sci_free_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + if (!s->dma_dev) + return; + + if (s->chan_tx) + sci_tx_dma_release(s, false); + if (s->chan_rx) + sci_rx_dma_release(s, false); +} +#endif + +static int sci_startup(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + if (s->enable) + s->enable(port); + + sci_request_irq(s); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_request_dma(port); +#endif + sci_start_tx(port); + sci_start_rx(port); + + return 0; +} + +static void sci_shutdown(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + sci_stop_rx(port); + sci_stop_tx(port); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_free_dma(port); +#endif + sci_free_irq(s); + + if (s->disable) + s->disable(port); +} + +static void sci_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); +#endif + unsigned int status, baud, smr_val, max_baud; + int t = -1; + u16 scfcr = 0; + + /* + * earlyprintk comes here early on with port->uartclk set to zero. + * the clock framework is not up and running at this point so here + * we assume that 115200 is the maximum baud rate. please note that + * the baud rate is not programmed during earlyprintk - it is assumed + * that the previous boot loader has enabled required clocks and + * setup the baud rate generator hardware for us already. + */ + max_baud = port->uartclk ? port->uartclk / 16 : 115200; + + baud = uart_get_baud_rate(port, termios, old, 0, max_baud); + if (likely(baud && port->uartclk)) + t = SCBRR_VALUE(baud, port->uartclk); + + do { + status = sci_in(port, SCxSR); + } while (!(status & SCxSR_TEND(port))); + + sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ + + if (port->type != PORT_SCI) + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); + + smr_val = sci_in(port, SCSMR) & 3; + if ((termios->c_cflag & CSIZE) == CS7) + smr_val |= 0x40; + if (termios->c_cflag & PARENB) + smr_val |= 0x20; + if (termios->c_cflag & PARODD) + smr_val |= 0x30; + if (termios->c_cflag & CSTOPB) + smr_val |= 0x08; + + uart_update_timeout(port, termios->c_cflag, baud); + + sci_out(port, SCSMR, smr_val); + + dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, + SCSCR_INIT(port)); + + if (t > 0) { + if (t >= 256) { + sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); + t >>= 2; + } else + sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); + + sci_out(port, SCBRR, t); + udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ + } + + sci_init_pins(port, termios->c_cflag); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); + + sci_out(port, SCSCR, SCSCR_INIT(port)); + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } +#endif + + if ((termios->c_cflag & CREAD) != 0) + sci_start_rx(port); +} + +static const char *sci_type(struct uart_port *port) +{ + switch (port->type) { + case PORT_IRDA: + return "irda"; + case PORT_SCI: + return "sci"; + case PORT_SCIF: + return "scif"; + case PORT_SCIFA: + return "scifa"; + case PORT_SCIFB: + return "scifb"; + } + + return NULL; +} + +static void sci_release_port(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +static int sci_request_port(struct uart_port *port) +{ + /* Nothing here yet .. */ + return 0; +} + +static void sci_config_port(struct uart_port *port, int flags) +{ + struct sci_port *s = to_sci_port(port); + + port->type = s->type; + + if (port->membase) + return; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap_nocache(port->mapbase, 0x40); + + if (IS_ERR(port->membase)) + dev_err(port->dev, "can't remap port#%d\n", port->line); + } else { + /* + * For the simple (and majority of) cases where we don't + * need to do any remapping, just cast the cookie + * directly. + */ + port->membase = (void __iomem *)port->mapbase; + } +} + +static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + struct sci_port *s = to_sci_port(port); + + if (ser->irq != s->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs) + return -EINVAL; + if (ser->baud_base < 2400) + /* No paper tape reader for Mitch.. */ + return -EINVAL; + + return 0; +} + +static struct uart_ops sci_uart_ops = { + .tx_empty = sci_tx_empty, + .set_mctrl = sci_set_mctrl, + .get_mctrl = sci_get_mctrl, + .start_tx = sci_start_tx, + .stop_tx = sci_stop_tx, + .stop_rx = sci_stop_rx, + .enable_ms = sci_enable_ms, + .break_ctl = sci_break_ctl, + .startup = sci_startup, + .shutdown = sci_shutdown, + .set_termios = sci_set_termios, + .type = sci_type, + .release_port = sci_release_port, + .request_port = sci_request_port, + .config_port = sci_config_port, + .verify_port = sci_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = sci_poll_get_char, + .poll_put_char = sci_poll_put_char, +#endif +}; + +static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) +{ + struct uart_port *port = &sci_port->port; + + port->ops = &sci_uart_ops; + port->iotype = UPIO_MEM; + port->line = index; + + switch (p->type) { + case PORT_SCIFB: + port->fifosize = 256; + break; + case PORT_SCIFA: + port->fifosize = 64; + break; + case PORT_SCIF: + port->fifosize = 16; + break; + default: + port->fifosize = 1; + break; + } + + if (dev) { + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + + sci_port->enable = sci_clk_enable; + sci_port->disable = sci_clk_disable; + port->dev = &dev->dev; + } + + sci_port->break_timer.data = (unsigned long)sci_port; + sci_port->break_timer.function = sci_break_timer; + init_timer(&sci_port->break_timer); + + port->mapbase = p->mapbase; + port->membase = p->membase; + + port->irq = p->irqs[SCIx_TXI_IRQ]; + port->flags = p->flags; + sci_port->type = port->type = p->type; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_port->dma_dev = p->dma_dev; + sci_port->slave_tx = p->dma_slave_tx; + sci_port->slave_rx = p->dma_slave_rx; + + dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__, + p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); +#endif + + memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; +} + +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +static struct tty_driver *serial_console_device(struct console *co, int *index) +{ + struct uart_driver *p = &sci_uart_driver; + *index = co->index; + return p->tty_driver; +} + +static void serial_console_putchar(struct uart_port *port, int ch) +{ + sci_poll_put_char(port, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + struct uart_port *port = co->data; + struct sci_port *sci_port = to_sci_port(port); + unsigned short bits; + + if (sci_port->enable) + sci_port->enable(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 ((sci_in(port, SCxSR) & bits) != bits) + cpu_relax(); + + if (sci_port->disable) + sci_port->disable(port); +} + +static int __devinit serial_console_setup(struct console *co, char *options) +{ + struct sci_port *sci_port; + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= SCI_NPORTS) + co->index = 0; + + if (co->data) { + port = co->data; + sci_port = to_sci_port(port); + } else { + sci_port = &sci_ports[co->index]; + port = &sci_port->port; + co->data = port; + } + + /* + * Also need to check port->type, we don't actually have any + * UPIO_PORT ports, but uart_report_port() handily misreports + * it anyways if we don't have a port available by the time this is + * called. + */ + if (!port->type) + return -ENODEV; + + sci_config_port(port, 0); + + if (sci_port->enable) + sci_port->enable(port); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + ret = uart_set_options(port, co, baud, parity, bits, flow); +#if defined(__H8300H__) || defined(__H8300S__) + /* disable rx interrupt */ + if (ret == 0) + sci_stop_rx(port); +#endif + /* TODO: disable clock */ + return ret; +} + +static struct console serial_console = { + .name = "ttySC", + .device = serial_console_device, + .write = serial_console_write, + .setup = serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static int __init sci_console_init(void) +{ + register_console(&serial_console); + return 0; +} +console_initcall(sci_console_init); + +static struct sci_port early_serial_port; +static struct console early_serial_console = { + .name = "early_ttySC", + .write = serial_console_write, + .flags = CON_PRINTBUFFER, +}; +static char early_serial_buf[32]; + +#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ + +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) +#define SCI_CONSOLE (&serial_console) +#else +#define SCI_CONSOLE 0 +#endif + +static char banner[] __initdata = + KERN_INFO "SuperH SCI(F) driver initialized\n"; + +static struct uart_driver sci_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sci", + .dev_name = "ttySC", + .major = SCI_MAJOR, + .minor = SCI_MINOR_START, + .nr = SCI_NPORTS, + .cons = SCI_CONSOLE, +}; + + +static int sci_remove(struct platform_device *dev) +{ + struct sh_sci_priv *priv = platform_get_drvdata(dev); + struct sci_port *p; + unsigned long flags; + + cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(p, &priv->ports, node) { + uart_remove_one_port(&sci_uart_driver, &p->port); + clk_put(p->iclk); + clk_put(p->fclk); + } + spin_unlock_irqrestore(&priv->lock, flags); + + kfree(priv); + return 0; +} + +static int __devinit sci_probe_single(struct platform_device *dev, + unsigned int index, + struct plat_sci_port *p, + struct sci_port *sciport) +{ + struct sh_sci_priv *priv = platform_get_drvdata(dev); + unsigned long flags; + int ret; + + /* Sanity check */ + if (unlikely(index >= SCI_NPORTS)) { + dev_notice(&dev->dev, "Attempting to register port " + "%d when only %d are available.\n", + index+1, SCI_NPORTS); + dev_notice(&dev->dev, "Consider bumping " + "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); + return 0; + } + + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; + + ret = uart_add_one_port(&sci_uart_driver, &sciport->port); + if (ret) + return ret; + + INIT_LIST_HEAD(&sciport->node); + + spin_lock_irqsave(&priv->lock, flags); + list_add(&sciport->node, &priv->ports); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +/* + * Register a set of serial devices attached to a platform device. The + * list is terminated with a zero flags entry, which means we expect + * all entries to have at least UPF_BOOT_AUTOCONF set. Platforms that need + * remapping (such as sh64) should also set UPF_IOREMAP. + */ +static int __devinit sci_probe(struct platform_device *dev) +{ + struct plat_sci_port *p = dev->dev.platform_data; + struct sh_sci_priv *priv; + int i, ret = -EINVAL; + +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE + if (is_early_platform_device(dev)) { + if (dev->id == -1) + return -ENOTSUPP; + early_serial_console.index = dev->id; + early_serial_console.data = &early_serial_port.port; + sci_init_single(NULL, &early_serial_port, dev->id, p); + serial_console_setup(&early_serial_console, early_serial_buf); + if (!strstr(early_serial_buf, "keep")) + early_serial_console.flags |= CON_BOOT; + register_console(&early_serial_console); + return 0; + } +#endif + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_LIST_HEAD(&priv->ports); + spin_lock_init(&priv->lock); + platform_set_drvdata(dev, priv); + + priv->clk_nb.notifier_call = sci_notifier; + cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); + + if (dev->id != -1) { + ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); + if (ret) + goto err_unreg; + } else { + for (i = 0; p && p->flags != 0; p++, i++) { + ret = sci_probe_single(dev, i, p, &sci_ports[i]); + if (ret) + goto err_unreg; + } + } + +#ifdef CONFIG_SH_STANDARD_BIOS + sh_bios_gdb_detach(); +#endif + + return 0; + +err_unreg: + sci_remove(dev); + return ret; +} + +static int sci_suspend(struct device *dev) +{ + struct sh_sci_priv *priv = dev_get_drvdata(dev); + struct sci_port *p; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(p, &priv->ports, node) + uart_suspend_port(&sci_uart_driver, &p->port); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int sci_resume(struct device *dev) +{ + struct sh_sci_priv *priv = dev_get_drvdata(dev); + struct sci_port *p; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(p, &priv->ports, node) + uart_resume_port(&sci_uart_driver, &p->port); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static const struct dev_pm_ops sci_dev_pm_ops = { + .suspend = sci_suspend, + .resume = sci_resume, +}; + +static struct platform_driver sci_driver = { + .probe = sci_probe, + .remove = sci_remove, + .driver = { + .name = "sh-sci", + .owner = THIS_MODULE, + .pm = &sci_dev_pm_ops, + }, +}; + +static int __init sci_init(void) +{ + int ret; + + printk(banner); + + ret = uart_register_driver(&sci_uart_driver); + if (likely(ret == 0)) { + ret = platform_driver_register(&sci_driver); + if (unlikely(ret)) + uart_unregister_driver(&sci_uart_driver); + } + + return ret; +} + +static void __exit sci_exit(void) +{ + platform_driver_unregister(&sci_driver); + uart_unregister_driver(&sci_uart_driver); +} + +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +early_platform_init_buffer("earlyprintk", &sci_driver, + early_serial_buf, ARRAY_SIZE(early_serial_buf)); +#endif +module_init(sci_init); +module_exit(sci_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sh-sci"); diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h new file mode 100644 index 000000000000..4bc614e4221c --- /dev/null +++ b/drivers/tty/serial/sh-sci.h @@ -0,0 +1,660 @@ +#include +#include +#include + +#if defined(CONFIG_H83007) || defined(CONFIG_H83068) +#include +#endif +#if defined(CONFIG_H8S2678) +#include +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7708) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) +# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ +# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7705) +# define SCIF0 0xA4400000 +# define SCIF2 0xA4410000 +# define SCSMR_Ir 0xA44A0000 +# define IRDA_SCIF SCIF0 +# define SCPCR 0xA4000116 +# define SCPDR 0xA4000136 + +/* Set the clock source, + * SCIF2 (0xA4410000) -> External clock, SCK pin used as clock input + * SCIF0 (0xA4400000) -> Internal clock, SCK pin as serial clock output + */ +# define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0 +#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) +# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define PORT_PTCR 0xA405011EUL +# define PORT_PVCR 0xA4050122UL +# define SCIF_ORER 0x0200 /* overrun error bit */ +#elif defined(CONFIG_SH_RTS7751R2D) +# define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ +# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) +# define SCSPTR1 0xffe0001c /* 8 bit SCI */ +# define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ + 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ + 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) +#elif defined(CONFIG_CPU_SUBTYPE_SH7760) +# define SCSPTR0 0xfe600024 /* 16 bit SCIF */ +# define SCSPTR1 0xfe610024 /* 16 bit SCIF */ +# define SCSPTR2 0xfe620024 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) +# define SCSPTR0 0xA4400000 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define PACR 0xa4050100 +# define PBCR 0xa4050102 +# define SCSCR_INIT(port) 0x3B +#elif defined(CONFIG_CPU_SUBTYPE_SH7343) +# define SCSPTR0 0xffe00010 /* 16 bit SCIF */ +# define SCSPTR1 0xffe10010 /* 16 bit SCIF */ +# define SCSPTR2 0xffe20010 /* 16 bit SCIF */ +# define SCSPTR3 0xffe30010 /* 16 bit SCIF */ +# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7722) +# define PADR 0xA4050120 +# define PSDR 0xA405013e +# define PWDR 0xA4050166 +# define PSCR 0xA405011E +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7366) +# define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ +# define SCSPTR0 SCPDR0 +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) +# define SCSPTR0 0xa4050160 +# define SCSPTR1 0xa405013e +# define SCSPTR2 0xa4050160 +# define SCSPTR3 0xa405013e +# define SCSPTR4 0xa4050128 +# define SCSPTR5 0xa4050128 +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7724) +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) ((port)->type == PORT_SCIFA ? \ + 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ + 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) +#elif defined(CONFIG_CPU_SUBTYPE_SH4_202) +# define SCSPTR2 0xffe80020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103) +# define SCIF_BASE_ADDR 0x01030000 +# define SCIF_ADDR_SH5 PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR +# define SCIF_PTR2_OFFS 0x0000020 +# define SCIF_LSR2_OFFS 0x0000024 +# define SCSPTR2 ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */ +# define SCLSR2 ((port->mapbase)+SCIF_LSR2_OFFS) /* 16 bit SCIF */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0, TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_H83007) || defined(CONFIG_H83068) +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) +#elif defined(CONFIG_H8S2678) +# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ +# define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) +# define SCSPTR0 0xfe4b0020 +# define SCSPTR1 0xfe4b0020 +# define SCSPTR2 0xfe4b0020 +# define SCIF_ORER 0x0001 +# define SCSCR_INIT(port) 0x38 +# define SCIF_ONLY +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ +# define SCSPTR1 0xffe08024 /* 16 bit SCIF */ +# define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7770) +# define SCSPTR0 0xff923020 /* 16 bit SCIF */ +# define SCSPTR1 0xff924020 /* 16 bit SCIF */ +# define SCSPTR2 0xff925020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7780) +# define SCSPTR0 0xffe00024 /* 16 bit SCIF */ +# define SCSPTR1 0xffe10024 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* Overrun error bit */ + +#if defined(CONFIG_SH_SH2007) +/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ +# define SCSCR_INIT(port) 0x38 +#else +/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ +# define SCSCR_INIT(port) 0x3a +#endif + +#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) +# define SCSPTR0 0xffea0024 /* 16 bit SCIF */ +# define SCSPTR1 0xffeb0024 /* 16 bit SCIF */ +# define SCSPTR2 0xffec0024 /* 16 bit SCIF */ +# define SCSPTR3 0xffed0024 /* 16 bit SCIF */ +# define SCSPTR4 0xffee0024 /* 16 bit SCIF */ +# define SCSPTR5 0xffef0024 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* Overrun error bit */ +# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ + defined(CONFIG_CPU_SUBTYPE_SH7203) || \ + defined(CONFIG_CPU_SUBTYPE_SH7206) || \ + defined(CONFIG_CPU_SUBTYPE_SH7263) +# define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ +# define SCSPTR1 0xfffe8820 /* 16 bit SCIF */ +# define SCSPTR2 0xfffe9020 /* 16 bit SCIF */ +# define SCSPTR3 0xfffe9820 /* 16 bit SCIF */ +# if defined(CONFIG_CPU_SUBTYPE_SH7201) +# define SCSPTR4 0xfffeA020 /* 16 bit SCIF */ +# define SCSPTR5 0xfffeA820 /* 16 bit SCIF */ +# define SCSPTR6 0xfffeB020 /* 16 bit SCIF */ +# define SCSPTR7 0xfffeB820 /* 16 bit SCIF */ +# endif +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7619) +# define SCSPTR0 0xf8400020 /* 16 bit SCIF */ +# define SCSPTR1 0xf8410020 /* 16 bit SCIF */ +# define SCSPTR2 0xf8420020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#elif defined(CONFIG_CPU_SUBTYPE_SHX3) +# define SCSPTR0 0xffc30020 /* 16 bit SCIF */ +# define SCSPTR1 0xffc40020 /* 16 bit SCIF */ +# define SCSPTR2 0xffc50020 /* 16 bit SCIF */ +# define SCSPTR3 0xffc60020 /* 16 bit SCIF */ +# define SCIF_ORER 0x0001 /* Overrun error bit */ +# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ +#else +# error CPU subtype not defined +#endif + +/* SCSCR */ +#define SCI_CTRL_FLAGS_TIE 0x80 /* all */ +#define SCI_CTRL_FLAGS_RIE 0x40 /* all */ +#define SCI_CTRL_FLAGS_TE 0x20 /* all */ +#define SCI_CTRL_FLAGS_RE 0x10 /* all */ +#if defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7722) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) || \ + defined(CONFIG_CPU_SUBTYPE_SHX3) +#define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7724) +#define SCI_CTRL_FLAGS_REIE ((port)->type == PORT_SCIFA ? 0 : 8) +#else +#define SCI_CTRL_FLAGS_REIE 0 +#endif +/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_CTRL_FLAGS_CKE1 0x02 * all */ +/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */ + +/* SCxSR SCI */ +#define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +#define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ +/* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + +#define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) + +/* SCxSR SCIF */ +#define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ +#define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + +#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) +# define SCIF_ORER 0x0200 +# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) +# define SCIF_RFDC_MASK 0x007f +# define SCIF_TXROOM_MAX 64 +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK ) +# define SCIF_RFDC_MASK 0x007f +# define SCIF_TXROOM_MAX 64 +/* SH7763 SCIF2 support */ +# define SCIF2_RFDC_MASK 0x001f +# define SCIF2_TXROOM_MAX 16 +#else +# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) +# define SCIF_RFDC_MASK 0x001f +# define SCIF_TXROOM_MAX 16 +#endif + +#ifndef SCIF_ORER +#define SCIF_ORER 0x0000 +#endif + +#define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) +#define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) +#define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) +#define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) +#define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) +#define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) +#define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) +#define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) + +#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) +# define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) +# define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) +# define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) +# define SCxSR_BREAK_CLEAR(port) (sci_in(port, SCxSR) & 0xffe3) +#else +# define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) +# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) +# define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) +# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) +#endif + +/* SCFCR */ +#define SCFCR_RFRST 0x0002 +#define SCFCR_TFRST 0x0004 +#define SCFCR_TCRST 0x4000 +#define SCFCR_MCE 0x0008 + +#define SCI_MAJOR 204 +#define SCI_MINOR_START 8 + +/* Generic serial flags */ +#define SCI_RX_THROTTLE 0x0000001 + +#define SCI_MAGIC 0xbabeface + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define SCI_EVENT_WRITE_WAKEUP 0 + +#define SCI_IN(size, offset) \ + if ((size) == 8) { \ + return ioread8(port->membase + (offset)); \ + } else { \ + return ioread16(port->membase + (offset)); \ + } +#define SCI_OUT(size, offset, value) \ + if ((size) == 8) { \ + iowrite8(value, port->membase + (offset)); \ + } else if ((size) == 16) { \ + iowrite16(value, port->membase + (offset)); \ + } + +#define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ + SCI_IN(scif_size, scif_offset) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_IN(sci_size, sci_offset); \ + } \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ + SCI_OUT(scif_size, scif_offset, value) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_OUT(sci_size, sci_offset, value); \ + } \ + } + +#ifdef CONFIG_H8300 +/* h8300 don't have SCIF */ +#define CPU_SCIF_FNS(name) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + return 0; \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + } +#else +#define CPU_SCIF_FNS(name, scif_offset, scif_size) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + SCI_IN(scif_size, scif_offset); \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + SCI_OUT(scif_size, scif_offset, value); \ + } +#endif + +#define CPU_SCI_FNS(name, sci_offset, sci_size) \ + static inline unsigned int sci_##name##_in(struct uart_port* port) \ + { \ + SCI_IN(sci_size, sci_offset); \ + } \ + static inline void sci_##name##_out(struct uart_port* port, unsigned int value) \ + { \ + SCI_OUT(sci_size, sci_offset, value); \ + } + +#if defined(CONFIG_CPU_SH3) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) +#if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) +#define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) +#elif defined(CONFIG_ARCH_SH7372) +#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) +#define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) +#else +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) +#endif +#elif defined(__H8300H__) || defined(__H8300S__) +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name) +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ + defined(CONFIG_CPU_SUBTYPE_SH7724) + #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) + #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#else +#define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) +#define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) + +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x04, 8) +SCIF_FNS(SCSCR, 0x08, 16) +SCIF_FNS(SCTDSR, 0x0c, 8) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCxSR, 0x14, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCxTDR, 0x20, 8) +SCIF_FNS(SCxRDR, 0x24, 8) +SCIF_FNS(SCLSR, 0x00, 0) +#elif defined(CONFIG_ARCH_SH7372) +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x04, 8) +SCIF_FNS(SCSCR, 0x08, 16) +SCIF_FNS(SCTDSR, 0x0c, 16) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCxSR, 0x14, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCTFDR, 0x38, 16) +SCIF_FNS(SCRFDR, 0x3c, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) +SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) +SCIF_FNS(SCLSR, 0x00, 0) +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ + defined(CONFIG_CPU_SUBTYPE_SH7724) +SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) +SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) +SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) +SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) +SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) +SCIx_FNS(SCSPTR, 0, 0, 0, 0) +SCIF_FNS(SCTDSR, 0x0c, 8) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCLSR, 0x24, 16) +#else +/* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ +/* name off sz off sz off sz off sz off sz*/ +SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) +SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) +SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) +SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) +SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) +SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) +SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) +#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) +SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) +SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) +SCIF_FNS(SCLSR, 0, 0, 0x28, 16) +#elif defined(CONFIG_CPU_SUBTYPE_SH7763) +SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) +SCIF_FNS(SCSPTR2, 0, 0, 0x20, 16) +SCIF_FNS(SCLSR2, 0, 0, 0x24, 16) +SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) +SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) +SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) +SCIF_FNS(SCLSR, 0, 0, 0x28, 16) +#else +SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) +#if defined(CONFIG_CPU_SUBTYPE_SH7722) +SCIF_FNS(SCSPTR, 0, 0, 0, 0) +#else +SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) +#endif +SCIF_FNS(SCLSR, 0, 0, 0x24, 16) +#endif +#endif +#define sci_in(port, reg) sci_##reg##_in(port) +#define sci_out(port, reg, value) sci_##reg##_out(port, value) + +/* H8/300 series SCI pins assignment */ +#if defined(__H8300H__) || defined(__H8300S__) +static const struct __attribute__((packed)) { + int port; /* GPIO port no */ + unsigned short rx,tx; /* GPIO bit no */ +} h8300_sci_pins[] = { +#if defined(CONFIG_H83007) || defined(CONFIG_H83068) + { /* SCI0 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_PB, + .rx = H8300_GPIO_B7, + .tx = H8300_GPIO_B6, + } +#elif defined(CONFIG_H8S2678) + { /* SCI0 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_P5, + .rx = H8300_GPIO_B1, + .tx = H8300_GPIO_B0, + } +#endif +}; +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7708) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xfffffe80) + return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ + return 1; +} +#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000) + return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ + return 1; +} +#elif defined(__H8300H__) || defined(__H8300S__) +static inline int sci_rxd_in(struct uart_port *port) +{ + int ch = (port->mapbase - SMR0) >> 3; + return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; +} +#else /* default case for non-SCI processors */ +static inline int sci_rxd_in(struct uart_port *port) +{ + return 1; +} +#endif + +/* + * Values for the BitRate Register (SCBRR) + * + * The values are actually divisors for a frequency which can + * be internal to the SH3 (14.7456MHz) or derived from an external + * clock source. This driver assumes the internal clock is used; + * to support using an external clock source, config options or + * possibly command-line options would need to be added. + * + * Also, to support speeds below 2400 (why?) the lower 2 bits of + * the SCSMR register would also need to be set to non-zero values. + * + * -- Greg Banks 27Feb2000 + * + * Answer: The SCBRR register is only eight bits, and the value in + * it gets larger with lower baud rates. At around 2400 (depending on + * the peripherial module clock) you run out of bits. However the + * lower two bits of SCSMR allow the module clock to be divided down, + * scaling the value which is needed in SCBRR. + * + * -- Stuart Menefy - 23 May 2000 + * + * I meant, why would anyone bother with bitrates below 2400. + * + * -- Greg Banks - 7Jul2000 + * + * You "speedist"! How will I use my 110bps ASR-33 teletype with paper + * tape reader as a console! + * + * -- Mitch Davis - 15 Jul 2000 + */ + +#if (defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786)) && \ + !defined(CONFIG_SH_SH2007) +#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) +#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) +#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) +#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ + defined(CONFIG_CPU_SUBTYPE_SH7724) +static inline int scbrr_calc(struct uart_port *port, int bps, int clk) +{ + if (port->type == PORT_SCIF) + return (clk+16*bps)/(32*bps)-1; + else + return ((clk*2)+16*bps)/(16*bps)-1; +} +#define SCBRR_VALUE(bps, clk) scbrr_calc(port, bps, clk) +#elif defined(__H8300H__) || defined(__H8300S__) +#define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) +#else /* Generic SH */ +#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) +#endif diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c new file mode 100644 index 000000000000..cff9a306660f --- /dev/null +++ b/drivers/tty/serial/sn_console.c @@ -0,0 +1,1085 @@ +/* + * C-Brick Serial Port (and console) driver for SGI Altix machines. + * + * This driver is NOT suitable for talking to the l1-controller for + * anything other than 'console activities' --- please use the l1 + * driver for that. + * + * + * Copyright (c) 2004-2006 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for mdelay */ +#include +#include + +#include +#include +#include + +/* number of characters we can transmit to the SAL console at a time */ +#define SN_SAL_MAX_CHARS 120 + +/* 64K, when we're asynch, it must be at least printk's LOG_BUF_LEN to + * avoid losing chars, (always has to be a power of 2) */ +#define SN_SAL_BUFFER_SIZE (64 * (1 << 10)) + +#define SN_SAL_UART_FIFO_DEPTH 16 +#define SN_SAL_UART_FIFO_SPEED_CPS (9600/10) + +/* sn_transmit_chars() calling args */ +#define TRANSMIT_BUFFERED 0 +#define TRANSMIT_RAW 1 + +/* To use dynamic numbers only and not use the assigned major and minor, + * define the following.. */ + /* #define USE_DYNAMIC_MINOR 1 *//* use dynamic minor number */ +#define USE_DYNAMIC_MINOR 0 /* Don't rely on misc_register dynamic minor */ + +/* Device name we're using */ +#define DEVICE_NAME "ttySG" +#define DEVICE_NAME_DYNAMIC "ttySG0" /* need full name for misc_register */ +/* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */ +#define DEVICE_MAJOR 204 +#define DEVICE_MINOR 40 + +#ifdef CONFIG_MAGIC_SYSRQ +static char sysrq_serial_str[] = "\eSYS"; +static char *sysrq_serial_ptr = sysrq_serial_str; +static unsigned long sysrq_requested; +#endif /* CONFIG_MAGIC_SYSRQ */ + +/* + * Port definition - this kinda drives it all + */ +struct sn_cons_port { + struct timer_list sc_timer; + struct uart_port sc_port; + struct sn_sal_ops { + int (*sal_puts_raw) (const char *s, int len); + int (*sal_puts) (const char *s, int len); + int (*sal_getc) (void); + int (*sal_input_pending) (void); + void (*sal_wakeup_transmit) (struct sn_cons_port *, int); + } *sc_ops; + unsigned long sc_interrupt_timeout; + int sc_is_asynch; +}; + +static struct sn_cons_port sal_console_port; +static int sn_process_input; + +/* Only used if USE_DYNAMIC_MINOR is set to 1 */ +static struct miscdevice misc; /* used with misc_register for dynamic */ + +extern void early_sn_setup(void); + +#undef DEBUG +#ifdef DEBUG +static int sn_debug_printf(const char *fmt, ...); +#define DPRINTF(x...) sn_debug_printf(x) +#else +#define DPRINTF(x...) do { } while (0) +#endif + +/* Prototypes */ +static int snt_hw_puts_raw(const char *, int); +static int snt_hw_puts_buffered(const char *, int); +static int snt_poll_getc(void); +static int snt_poll_input_pending(void); +static int snt_intr_getc(void); +static int snt_intr_input_pending(void); +static void sn_transmit_chars(struct sn_cons_port *, int); + +/* A table for polling: + */ +static struct sn_sal_ops poll_ops = { + .sal_puts_raw = snt_hw_puts_raw, + .sal_puts = snt_hw_puts_raw, + .sal_getc = snt_poll_getc, + .sal_input_pending = snt_poll_input_pending +}; + +/* A table for interrupts enabled */ +static struct sn_sal_ops intr_ops = { + .sal_puts_raw = snt_hw_puts_raw, + .sal_puts = snt_hw_puts_buffered, + .sal_getc = snt_intr_getc, + .sal_input_pending = snt_intr_input_pending, + .sal_wakeup_transmit = sn_transmit_chars +}; + +/* the console does output in two distinctly different ways: + * synchronous (raw) and asynchronous (buffered). initally, early_printk + * does synchronous output. any data written goes directly to the SAL + * to be output (incidentally, it is internally buffered by the SAL) + * after interrupts and timers are initialized and available for use, + * the console init code switches to asynchronous output. this is + * also the earliest opportunity to begin polling for console input. + * after console initialization, console output and tty (serial port) + * output is buffered and sent to the SAL asynchronously (either by + * timer callback or by UART interrupt) */ + +/* routines for running the console in polling mode */ + +/** + * snt_poll_getc - Get a character from the console in polling mode + * + */ +static int snt_poll_getc(void) +{ + int ch; + + ia64_sn_console_getc(&ch); + return ch; +} + +/** + * snt_poll_input_pending - Check if any input is waiting - polling mode. + * + */ +static int snt_poll_input_pending(void) +{ + int status, input; + + status = ia64_sn_console_check(&input); + return !status && input; +} + +/* routines for an interrupt driven console (normal) */ + +/** + * snt_intr_getc - Get a character from the console, interrupt mode + * + */ +static int snt_intr_getc(void) +{ + return ia64_sn_console_readc(); +} + +/** + * snt_intr_input_pending - Check if input is pending, interrupt mode + * + */ +static int snt_intr_input_pending(void) +{ + return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV; +} + +/* these functions are polled and interrupt */ + +/** + * snt_hw_puts_raw - Send raw string to the console, polled or interrupt mode + * @s: String + * @len: Length + * + */ +static int snt_hw_puts_raw(const char *s, int len) +{ + /* this will call the PROM and not return until this is done */ + return ia64_sn_console_putb(s, len); +} + +/** + * snt_hw_puts_buffered - Send string to console, polled or interrupt mode + * @s: String + * @len: Length + * + */ +static int snt_hw_puts_buffered(const char *s, int len) +{ + /* queue data to the PROM */ + return ia64_sn_console_xmit_chars((char *)s, len); +} + +/* uart interface structs + * These functions are associated with the uart_port that the serial core + * infrastructure calls. + * + * Note: Due to how the console works, many routines are no-ops. + */ + +/** + * snp_type - What type of console are we? + * @port: Port to operate with (we ignore since we only have one port) + * + */ +static const char *snp_type(struct uart_port *port) +{ + return ("SGI SN L1"); +} + +/** + * snp_tx_empty - Is the transmitter empty? We pretend we're always empty + * @port: Port to operate on (we ignore since we only have one port) + * + */ +static unsigned int snp_tx_empty(struct uart_port *port) +{ + return 1; +} + +/** + * snp_stop_tx - stop the transmitter - no-op for us + * @port: Port to operat eon - we ignore - no-op function + * + */ +static void snp_stop_tx(struct uart_port *port) +{ +} + +/** + * snp_release_port - Free i/o and resources for port - no-op for us + * @port: Port to operate on - we ignore - no-op function + * + */ +static void snp_release_port(struct uart_port *port) +{ +} + +/** + * snp_enable_ms - Force modem status interrupts on - no-op for us + * @port: Port to operate on - we ignore - no-op function + * + */ +static void snp_enable_ms(struct uart_port *port) +{ +} + +/** + * snp_shutdown - shut down the port - free irq and disable - no-op for us + * @port: Port to shut down - we ignore + * + */ +static void snp_shutdown(struct uart_port *port) +{ +} + +/** + * snp_set_mctrl - set control lines (dtr, rts, etc) - no-op for our console + * @port: Port to operate on - we ignore + * @mctrl: Lines to set/unset - we ignore + * + */ +static void snp_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/** + * snp_get_mctrl - get contorl line info, we just return a static value + * @port: port to operate on - we only have one port so we ignore this + * + */ +static unsigned int snp_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS; +} + +/** + * snp_stop_rx - Stop the receiver - we ignor ethis + * @port: Port to operate on - we ignore + * + */ +static void snp_stop_rx(struct uart_port *port) +{ +} + +/** + * snp_start_tx - Start transmitter + * @port: Port to operate on + * + */ +static void snp_start_tx(struct uart_port *port) +{ + if (sal_console_port.sc_ops->sal_wakeup_transmit) + sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port, + TRANSMIT_BUFFERED); + +} + +/** + * snp_break_ctl - handle breaks - ignored by us + * @port: Port to operate on + * @break_state: Break state + * + */ +static void snp_break_ctl(struct uart_port *port, int break_state) +{ +} + +/** + * snp_startup - Start up the serial port - always return 0 (We're always on) + * @port: Port to operate on + * + */ +static int snp_startup(struct uart_port *port) +{ + return 0; +} + +/** + * snp_set_termios - set termios stuff - we ignore these + * @port: port to operate on + * @termios: New settings + * @termios: Old + * + */ +static void +snp_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ +} + +/** + * snp_request_port - allocate resources for port - ignored by us + * @port: port to operate on + * + */ +static int snp_request_port(struct uart_port *port) +{ + return 0; +} + +/** + * snp_config_port - allocate resources, set up - we ignore, we're always on + * @port: Port to operate on + * @flags: flags used for port setup + * + */ +static void snp_config_port(struct uart_port *port, int flags) +{ +} + +/* Associate the uart functions above - given to serial core */ + +static struct uart_ops sn_console_ops = { + .tx_empty = snp_tx_empty, + .set_mctrl = snp_set_mctrl, + .get_mctrl = snp_get_mctrl, + .stop_tx = snp_stop_tx, + .start_tx = snp_start_tx, + .stop_rx = snp_stop_rx, + .enable_ms = snp_enable_ms, + .break_ctl = snp_break_ctl, + .startup = snp_startup, + .shutdown = snp_shutdown, + .set_termios = snp_set_termios, + .pm = NULL, + .type = snp_type, + .release_port = snp_release_port, + .request_port = snp_request_port, + .config_port = snp_config_port, + .verify_port = NULL, +}; + +/* End of uart struct functions and defines */ + +#ifdef DEBUG + +/** + * sn_debug_printf - close to hardware debugging printf + * @fmt: printf format + * + * This is as "close to the metal" as we can get, used when the driver + * itself may be broken. + * + */ +static int sn_debug_printf(const char *fmt, ...) +{ + static char printk_buf[1024]; + int printed_len; + va_list args; + + va_start(args, fmt); + printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args); + + if (!sal_console_port.sc_ops) { + sal_console_port.sc_ops = &poll_ops; + early_sn_setup(); + } + sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len); + + va_end(args); + return printed_len; +} +#endif /* DEBUG */ + +/* + * Interrupt handling routines. + */ + +/** + * sn_receive_chars - Grab characters, pass them to tty layer + * @port: Port to operate on + * @flags: irq flags + * + * Note: If we're not registered with the serial core infrastructure yet, + * we don't try to send characters to it... + * + */ +static void +sn_receive_chars(struct sn_cons_port *port, unsigned long flags) +{ + int ch; + struct tty_struct *tty; + + if (!port) { + printk(KERN_ERR "sn_receive_chars - port NULL so can't receieve\n"); + return; + } + + if (!port->sc_ops) { + printk(KERN_ERR "sn_receive_chars - port->sc_ops NULL so can't receieve\n"); + return; + } + + if (port->sc_port.state) { + /* The serial_core stuffs are initialized, use them */ + tty = port->sc_port.state->port.tty; + } + else { + /* Not registered yet - can't pass to tty layer. */ + tty = NULL; + } + + while (port->sc_ops->sal_input_pending()) { + ch = port->sc_ops->sal_getc(); + if (ch < 0) { + printk(KERN_ERR "sn_console: An error occured while " + "obtaining data from the console (0x%0x)\n", ch); + break; + } +#ifdef CONFIG_MAGIC_SYSRQ + if (sysrq_requested) { + unsigned long sysrq_timeout = sysrq_requested + HZ*5; + + sysrq_requested = 0; + if (ch && time_before(jiffies, sysrq_timeout)) { + spin_unlock_irqrestore(&port->sc_port.lock, flags); + handle_sysrq(ch); + spin_lock_irqsave(&port->sc_port.lock, flags); + /* ignore actual sysrq command char */ + continue; + } + } + if (ch == *sysrq_serial_ptr) { + if (!(*++sysrq_serial_ptr)) { + sysrq_requested = jiffies; + sysrq_serial_ptr = sysrq_serial_str; + } + /* + * ignore the whole sysrq string except for the + * leading escape + */ + if (ch != '\e') + continue; + } + else + sysrq_serial_ptr = sysrq_serial_str; +#endif /* CONFIG_MAGIC_SYSRQ */ + + /* record the character to pass up to the tty layer */ + if (tty) { + if(tty_insert_flip_char(tty, ch, TTY_NORMAL) == 0) + break; + } + port->sc_port.icount.rx++; + } + + if (tty) + tty_flip_buffer_push(tty); +} + +/** + * sn_transmit_chars - grab characters from serial core, send off + * @port: Port to operate on + * @raw: Transmit raw or buffered + * + * Note: If we're early, before we're registered with serial core, the + * writes are going through sn_sal_console_write because that's how + * register_console has been set up. We currently could have asynch + * polls calling this function due to sn_sal_switch_to_asynch but we can + * ignore them until we register with the serial core stuffs. + * + */ +static void sn_transmit_chars(struct sn_cons_port *port, int raw) +{ + int xmit_count, tail, head, loops, ii; + int result; + char *start; + struct circ_buf *xmit; + + if (!port) + return; + + BUG_ON(!port->sc_is_asynch); + + if (port->sc_port.state) { + /* We're initialized, using serial core infrastructure */ + xmit = &port->sc_port.state->xmit; + } else { + /* Probably sn_sal_switch_to_asynch has been run but serial core isn't + * initialized yet. Just return. Writes are going through + * sn_sal_console_write (due to register_console) at this time. + */ + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) { + /* Nothing to do. */ + ia64_sn_console_intr_disable(SAL_CONSOLE_INTR_XMIT); + return; + } + + head = xmit->head; + tail = xmit->tail; + start = &xmit->buf[tail]; + + /* twice around gets the tail to the end of the buffer and + * then to the head, if needed */ + loops = (head < tail) ? 2 : 1; + + for (ii = 0; ii < loops; ii++) { + xmit_count = (head < tail) ? + (UART_XMIT_SIZE - tail) : (head - tail); + + if (xmit_count > 0) { + if (raw == TRANSMIT_RAW) + result = + port->sc_ops->sal_puts_raw(start, + xmit_count); + else + result = + port->sc_ops->sal_puts(start, xmit_count); +#ifdef DEBUG + if (!result) + DPRINTF("`"); +#endif + if (result > 0) { + xmit_count -= result; + port->sc_port.icount.tx += result; + tail += result; + tail &= UART_XMIT_SIZE - 1; + xmit->tail = tail; + start = &xmit->buf[tail]; + } + } + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&port->sc_port); + + if (uart_circ_empty(xmit)) + snp_stop_tx(&port->sc_port); /* no-op for us */ +} + +/** + * sn_sal_interrupt - Handle console interrupts + * @irq: irq #, useful for debug statements + * @dev_id: our pointer to our port (sn_cons_port which contains the uart port) + * + */ +static irqreturn_t sn_sal_interrupt(int irq, void *dev_id) +{ + struct sn_cons_port *port = (struct sn_cons_port *)dev_id; + unsigned long flags; + int status = ia64_sn_console_intr_status(); + + if (!port) + return IRQ_NONE; + + spin_lock_irqsave(&port->sc_port.lock, flags); + if (status & SAL_CONSOLE_INTR_RECV) { + sn_receive_chars(port, flags); + } + if (status & SAL_CONSOLE_INTR_XMIT) { + sn_transmit_chars(port, TRANSMIT_BUFFERED); + } + spin_unlock_irqrestore(&port->sc_port.lock, flags); + return IRQ_HANDLED; +} + +/** + * sn_sal_timer_poll - this function handles polled console mode + * @data: A pointer to our sn_cons_port (which contains the uart port) + * + * data is the pointer that init_timer will store for us. This function is + * associated with init_timer to see if there is any console traffic. + * Obviously not used in interrupt mode + * + */ +static void sn_sal_timer_poll(unsigned long data) +{ + struct sn_cons_port *port = (struct sn_cons_port *)data; + unsigned long flags; + + if (!port) + return; + + if (!port->sc_port.irq) { + spin_lock_irqsave(&port->sc_port.lock, flags); + if (sn_process_input) + sn_receive_chars(port, flags); + sn_transmit_chars(port, TRANSMIT_RAW); + spin_unlock_irqrestore(&port->sc_port.lock, flags); + mod_timer(&port->sc_timer, + jiffies + port->sc_interrupt_timeout); + } +} + +/* + * Boot-time initialization code + */ + +/** + * sn_sal_switch_to_asynch - Switch to async mode (as opposed to synch) + * @port: Our sn_cons_port (which contains the uart port) + * + * So this is used by sn_sal_serial_console_init (early on, before we're + * registered with serial core). It's also used by sn_sal_module_init + * right after we've registered with serial core. The later only happens + * if we didn't already come through here via sn_sal_serial_console_init. + * + */ +static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port) +{ + unsigned long flags; + + if (!port) + return; + + DPRINTF("sn_console: about to switch to asynchronous console\n"); + + /* without early_printk, we may be invoked late enough to race + * with other cpus doing console IO at this point, however + * console interrupts will never be enabled */ + spin_lock_irqsave(&port->sc_port.lock, flags); + + /* early_printk invocation may have done this for us */ + if (!port->sc_ops) + port->sc_ops = &poll_ops; + + /* we can't turn on the console interrupt (as request_irq + * calls kmalloc, which isn't set up yet), so we rely on a + * timer to poll for input and push data from the console + * buffer. + */ + init_timer(&port->sc_timer); + port->sc_timer.function = sn_sal_timer_poll; + port->sc_timer.data = (unsigned long)port; + + if (IS_RUNNING_ON_SIMULATOR()) + port->sc_interrupt_timeout = 6; + else { + /* 960cps / 16 char FIFO = 60HZ + * HZ / (SN_SAL_FIFO_SPEED_CPS / SN_SAL_FIFO_DEPTH) */ + port->sc_interrupt_timeout = + HZ * SN_SAL_UART_FIFO_DEPTH / SN_SAL_UART_FIFO_SPEED_CPS; + } + mod_timer(&port->sc_timer, jiffies + port->sc_interrupt_timeout); + + port->sc_is_asynch = 1; + spin_unlock_irqrestore(&port->sc_port.lock, flags); +} + +/** + * sn_sal_switch_to_interrupts - Switch to interrupt driven mode + * @port: Our sn_cons_port (which contains the uart port) + * + * In sn_sal_module_init, after we're registered with serial core and + * the port is added, this function is called to switch us to interrupt + * mode. We were previously in asynch/polling mode (using init_timer). + * + * We attempt to switch to interrupt mode here by calling + * request_irq. If that works out, we enable receive interrupts. + */ +static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) +{ + unsigned long flags; + + if (port) { + DPRINTF("sn_console: switching to interrupt driven console\n"); + + if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, + IRQF_DISABLED | IRQF_SHARED, + "SAL console driver", port) >= 0) { + spin_lock_irqsave(&port->sc_port.lock, flags); + port->sc_port.irq = SGI_UART_VECTOR; + port->sc_ops = &intr_ops; + + /* turn on receive interrupts */ + ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV); + spin_unlock_irqrestore(&port->sc_port.lock, flags); + } + else { + printk(KERN_INFO + "sn_console: console proceeding in polled mode\n"); + } + } +} + +/* + * Kernel console definitions + */ + +static void sn_sal_console_write(struct console *, const char *, unsigned); +static int sn_sal_console_setup(struct console *, char *); +static struct uart_driver sal_console_uart; +extern struct tty_driver *uart_console_device(struct console *, int *); + +static struct console sal_console = { + .name = DEVICE_NAME, + .write = sn_sal_console_write, + .device = uart_console_device, + .setup = sn_sal_console_setup, + .index = -1, /* unspecified */ + .data = &sal_console_uart, +}; + +#define SAL_CONSOLE &sal_console + +static struct uart_driver sal_console_uart = { + .owner = THIS_MODULE, + .driver_name = "sn_console", + .dev_name = DEVICE_NAME, + .major = 0, /* major/minor set at registration time per USE_DYNAMIC_MINOR */ + .minor = 0, + .nr = 1, /* one port */ + .cons = SAL_CONSOLE, +}; + +/** + * sn_sal_module_init - When the kernel loads us, get us rolling w/ serial core + * + * Before this is called, we've been printing kernel messages in a special + * early mode not making use of the serial core infrastructure. When our + * driver is loaded for real, we register the driver and port with serial + * core and try to enable interrupt driven mode. + * + */ +static int __init sn_sal_module_init(void) +{ + int retval; + + if (!ia64_platform_is("sn2")) + return 0; + + printk(KERN_INFO "sn_console: Console driver init\n"); + + if (USE_DYNAMIC_MINOR == 1) { + misc.minor = MISC_DYNAMIC_MINOR; + misc.name = DEVICE_NAME_DYNAMIC; + retval = misc_register(&misc); + if (retval != 0) { + printk(KERN_WARNING "Failed to register console " + "device using misc_register.\n"); + return -ENODEV; + } + sal_console_uart.major = MISC_MAJOR; + sal_console_uart.minor = misc.minor; + } else { + sal_console_uart.major = DEVICE_MAJOR; + sal_console_uart.minor = DEVICE_MINOR; + } + + /* We register the driver and the port before switching to interrupts + * or async above so the proper uart structures are populated */ + + if (uart_register_driver(&sal_console_uart) < 0) { + printk + ("ERROR sn_sal_module_init failed uart_register_driver, line %d\n", + __LINE__); + return -ENODEV; + } + + spin_lock_init(&sal_console_port.sc_port.lock); + + /* Setup the port struct with the minimum needed */ + sal_console_port.sc_port.membase = (char *)1; /* just needs to be non-zero */ + sal_console_port.sc_port.type = PORT_16550A; + sal_console_port.sc_port.fifosize = SN_SAL_MAX_CHARS; + sal_console_port.sc_port.ops = &sn_console_ops; + sal_console_port.sc_port.line = 0; + + if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) { + /* error - not sure what I'd do - so I'll do nothing */ + printk(KERN_ERR "%s: unable to add port\n", __func__); + } + + /* when this driver is compiled in, the console initialization + * will have already switched us into asynchronous operation + * before we get here through the module initcalls */ + if (!sal_console_port.sc_is_asynch) { + sn_sal_switch_to_asynch(&sal_console_port); + } + + /* at this point (module_init) we can try to turn on interrupts */ + if (!IS_RUNNING_ON_SIMULATOR()) { + sn_sal_switch_to_interrupts(&sal_console_port); + } + sn_process_input = 1; + return 0; +} + +/** + * sn_sal_module_exit - When we're unloaded, remove the driver/port + * + */ +static void __exit sn_sal_module_exit(void) +{ + del_timer_sync(&sal_console_port.sc_timer); + uart_remove_one_port(&sal_console_uart, &sal_console_port.sc_port); + uart_unregister_driver(&sal_console_uart); + misc_deregister(&misc); +} + +module_init(sn_sal_module_init); +module_exit(sn_sal_module_exit); + +/** + * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required + * @puts_raw : puts function to do the writing + * @s: input string + * @count: length + * + * We need a \r ahead of every \n for direct writes through + * ia64_sn_console_putb (what sal_puts_raw below actually does). + * + */ + +static void puts_raw_fixed(int (*puts_raw) (const char *s, int len), + const char *s, int count) +{ + const char *s1; + + /* Output '\r' before each '\n' */ + while ((s1 = memchr(s, '\n', count)) != NULL) { + puts_raw(s, s1 - s); + puts_raw("\r\n", 2); + count -= s1 + 1 - s; + s = s1 + 1; + } + puts_raw(s, count); +} + +/** + * sn_sal_console_write - Print statements before serial core available + * @console: Console to operate on - we ignore since we have just one + * @s: String to send + * @count: length + * + * This is referenced in the console struct. It is used for early + * console printing before we register with serial core and for things + * such as kdb. The console_lock must be held when we get here. + * + * This function has some code for trying to print output even if the lock + * is held. We try to cover the case where a lock holder could have died. + * We don't use this special case code if we're not registered with serial + * core yet. After we're registered with serial core, the only time this + * function would be used is for high level kernel output like magic sys req, + * kdb, and printk's. + */ +static void +sn_sal_console_write(struct console *co, const char *s, unsigned count) +{ + unsigned long flags = 0; + struct sn_cons_port *port = &sal_console_port; + static int stole_lock = 0; + + BUG_ON(!port->sc_is_asynch); + + /* We can't look at the xmit buffer if we're not registered with serial core + * yet. So only do the fancy recovery after registering + */ + if (!port->sc_port.state) { + /* Not yet registered with serial core - simple case */ + puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); + return; + } + + /* somebody really wants this output, might be an + * oops, kdb, panic, etc. make sure they get it. */ + if (spin_is_locked(&port->sc_port.lock)) { + int lhead = port->sc_port.state->xmit.head; + int ltail = port->sc_port.state->xmit.tail; + int counter, got_lock = 0; + + /* + * We attempt to determine if someone has died with the + * lock. We wait ~20 secs after the head and tail ptrs + * stop moving and assume the lock holder is not functional + * and plow ahead. If the lock is freed within the time out + * period we re-get the lock and go ahead normally. We also + * remember if we have plowed ahead so that we don't have + * to wait out the time out period again - the asumption + * is that we will time out again. + */ + + for (counter = 0; counter < 150; mdelay(125), counter++) { + if (!spin_is_locked(&port->sc_port.lock) + || stole_lock) { + if (!stole_lock) { + spin_lock_irqsave(&port->sc_port.lock, + flags); + got_lock = 1; + } + break; + } else { + /* still locked */ + if ((lhead != port->sc_port.state->xmit.head) + || (ltail != + port->sc_port.state->xmit.tail)) { + lhead = + port->sc_port.state->xmit.head; + ltail = + port->sc_port.state->xmit.tail; + counter = 0; + } + } + } + /* flush anything in the serial core xmit buffer, raw */ + sn_transmit_chars(port, 1); + if (got_lock) { + spin_unlock_irqrestore(&port->sc_port.lock, flags); + stole_lock = 0; + } else { + /* fell thru */ + stole_lock = 1; + } + puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); + } else { + stole_lock = 0; + spin_lock_irqsave(&port->sc_port.lock, flags); + sn_transmit_chars(port, 1); + spin_unlock_irqrestore(&port->sc_port.lock, flags); + + puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); + } +} + + +/** + * sn_sal_console_setup - Set up console for early printing + * @co: Console to work with + * @options: Options to set + * + * Altix console doesn't do anything with baud rates, etc, anyway. + * + * This isn't required since not providing the setup function in the + * console struct is ok. However, other patches like KDB plop something + * here so providing it is easier. + * + */ +static int sn_sal_console_setup(struct console *co, char *options) +{ + return 0; +} + +/** + * sn_sal_console_write_early - simple early output routine + * @co - console struct + * @s - string to print + * @count - count + * + * Simple function to provide early output, before even + * sn_sal_serial_console_init is called. Referenced in the + * console struct registerd in sn_serial_console_early_setup. + * + */ +static void __init +sn_sal_console_write_early(struct console *co, const char *s, unsigned count) +{ + puts_raw_fixed(sal_console_port.sc_ops->sal_puts_raw, s, count); +} + +/* Used for very early console printing - again, before + * sn_sal_serial_console_init is run */ +static struct console sal_console_early __initdata = { + .name = "sn_sal", + .write = sn_sal_console_write_early, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +/** + * sn_serial_console_early_setup - Sets up early console output support + * + * Register a console early on... This is for output before even + * sn_sal_serial_cosnole_init is called. This function is called from + * setup.c. This allows us to do really early polled writes. When + * sn_sal_serial_console_init is called, this console is unregistered + * and a new one registered. + */ +int __init sn_serial_console_early_setup(void) +{ + if (!ia64_platform_is("sn2")) + return -1; + + sal_console_port.sc_ops = &poll_ops; + spin_lock_init(&sal_console_port.sc_port.lock); + early_sn_setup(); /* Find SAL entry points */ + register_console(&sal_console_early); + + return 0; +} + +/** + * sn_sal_serial_console_init - Early console output - set up for register + * + * This function is called when regular console init happens. Because we + * support even earlier console output with sn_serial_console_early_setup + * (called from setup.c directly), this function unregisters the really + * early console. + * + * Note: Even if setup.c doesn't register sal_console_early, unregistering + * it here doesn't hurt anything. + * + */ +static int __init sn_sal_serial_console_init(void) +{ + if (ia64_platform_is("sn2")) { + sn_sal_switch_to_asynch(&sal_console_port); + DPRINTF("sn_sal_serial_console_init : register console\n"); + register_console(&sal_console); + unregister_console(&sal_console_early); + } + return 0; +} + +console_initcall(sn_sal_serial_console_init); diff --git a/drivers/tty/serial/suncore.c b/drivers/tty/serial/suncore.c new file mode 100644 index 000000000000..6381a0282ee7 --- /dev/null +++ b/drivers/tty/serial/suncore.c @@ -0,0 +1,247 @@ +/* suncore.c + * + * Common SUN serial routines. Based entirely + * upon drivers/sbus/char/sunserial.c which is: + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * + * Adaptation to new UART layer is: + * + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "suncore.h" + +static int sunserial_current_minor = 64; + +int sunserial_register_minors(struct uart_driver *drv, int count) +{ + int err = 0; + + drv->minor = sunserial_current_minor; + drv->nr += count; + /* Register the driver on the first call */ + if (drv->nr == count) + err = uart_register_driver(drv); + if (err == 0) { + sunserial_current_minor += count; + drv->tty_driver->name_base = drv->minor - 64; + } + return err; +} +EXPORT_SYMBOL(sunserial_register_minors); + +void sunserial_unregister_minors(struct uart_driver *drv, int count) +{ + drv->nr -= count; + sunserial_current_minor -= count; + + if (drv->nr == 0) + uart_unregister_driver(drv); +} +EXPORT_SYMBOL(sunserial_unregister_minors); + +int sunserial_console_match(struct console *con, struct device_node *dp, + struct uart_driver *drv, int line, bool ignore_line) +{ + if (!con) + return 0; + + drv->cons = con; + + if (of_console_device != dp) + return 0; + + if (!ignore_line) { + int off = 0; + + if (of_console_options && + *of_console_options == 'b') + off = 1; + + if ((line & 1) != off) + return 0; + } + + if (!console_set_on_cmdline) { + con->index = line; + add_preferred_console(con->name, line, NULL); + } + return 1; +} +EXPORT_SYMBOL(sunserial_console_match); + +void sunserial_console_termios(struct console *con, struct device_node *uart_dp) +{ + const char *mode, *s; + char mode_prop[] = "ttyX-mode"; + int baud, bits, stop, cflag; + char parity; + + if (!strcmp(uart_dp->name, "rsc") || + !strcmp(uart_dp->name, "rsc-console") || + !strcmp(uart_dp->name, "rsc-control")) { + mode = of_get_property(uart_dp, + "ssp-console-modes", NULL); + if (!mode) + mode = "115200,8,n,1,-"; + } else if (!strcmp(uart_dp->name, "lom-console")) { + mode = "9600,8,n,1,-"; + } else { + struct device_node *dp; + char c; + + c = 'a'; + if (of_console_options) + c = *of_console_options; + + mode_prop[3] = c; + + dp = of_find_node_by_path("/options"); + mode = of_get_property(dp, mode_prop, NULL); + if (!mode) + mode = "9600,8,n,1,-"; + } + + cflag = CREAD | HUPCL | CLOCAL; + + s = mode; + baud = simple_strtoul(s, NULL, 0); + s = strchr(s, ','); + bits = simple_strtoul(++s, NULL, 0); + s = strchr(s, ','); + parity = *(++s); + s = strchr(s, ','); + stop = simple_strtoul(++s, NULL, 0); + s = strchr(s, ','); + /* XXX handshake is not handled here. */ + + switch (baud) { + case 150: cflag |= B150; break; + case 300: cflag |= B300; break; + case 600: cflag |= B600; break; + case 1200: cflag |= B1200; break; + case 2400: cflag |= B2400; break; + case 4800: cflag |= B4800; break; + case 9600: cflag |= B9600; break; + case 19200: cflag |= B19200; break; + case 38400: cflag |= B38400; break; + case 57600: cflag |= B57600; break; + case 115200: cflag |= B115200; break; + case 230400: cflag |= B230400; break; + case 460800: cflag |= B460800; break; + default: baud = 9600; cflag |= B9600; break; + } + + switch (bits) { + case 5: cflag |= CS5; break; + case 6: cflag |= CS6; break; + case 7: cflag |= CS7; break; + case 8: cflag |= CS8; break; + default: cflag |= CS8; break; + } + + switch (parity) { + case 'o': cflag |= (PARENB | PARODD); break; + case 'e': cflag |= PARENB; break; + case 'n': default: break; + } + + switch (stop) { + case 2: cflag |= CSTOPB; break; + case 1: default: break; + } + + con->cflag = cflag; +} + +/* Sun serial MOUSE auto baud rate detection. */ +static struct mouse_baud_cflag { + int baud; + unsigned int cflag; +} mouse_baud_table[] = { + { 1200, B1200 }, + { 2400, B2400 }, + { 4800, B4800 }, + { 9600, B9600 }, + { -1, ~0 }, + { -1, ~0 }, +}; + +unsigned int suncore_mouse_baud_cflag_next(unsigned int cflag, int *new_baud) +{ + int i; + + for (i = 0; mouse_baud_table[i].baud != -1; i++) + if (mouse_baud_table[i].cflag == (cflag & CBAUD)) + break; + + i += 1; + if (mouse_baud_table[i].baud == -1) + i = 0; + + *new_baud = mouse_baud_table[i].baud; + return mouse_baud_table[i].cflag; +} + +EXPORT_SYMBOL(suncore_mouse_baud_cflag_next); + +/* Basically, when the baud rate is wrong the mouse spits out + * breaks to us. + */ +int suncore_mouse_baud_detection(unsigned char ch, int is_break) +{ + static int mouse_got_break = 0; + static int ctr = 0; + + if (is_break) { + /* Let a few normal bytes go by before we jump the gun + * and say we need to try another baud rate. + */ + if (mouse_got_break && ctr < 8) + return 1; + + /* Ok, we need to try another baud. */ + ctr = 0; + mouse_got_break = 1; + return 2; + } + if (mouse_got_break) { + ctr++; + if (ch == 0x87) { + /* Correct baud rate determined. */ + mouse_got_break = 0; + } + return 1; + } + return 0; +} + +EXPORT_SYMBOL(suncore_mouse_baud_detection); + +static int __init suncore_init(void) +{ + return 0; +} + +static void __exit suncore_exit(void) +{ +} + +module_init(suncore_init); +module_exit(suncore_exit); + +MODULE_AUTHOR("Eddie C. Dost, David S. Miller"); +MODULE_DESCRIPTION("Sun serial common layer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/suncore.h b/drivers/tty/serial/suncore.h new file mode 100644 index 000000000000..db2057936c31 --- /dev/null +++ b/drivers/tty/serial/suncore.h @@ -0,0 +1,33 @@ +/* suncore.h + * + * Generic SUN serial/kbd/ms layer. Based entirely + * upon drivers/sbus/char/sunserial.h which is: + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * + * Port to new UART layer is: + * + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + */ + +#ifndef _SERIAL_SUN_H +#define _SERIAL_SUN_H + +/* Serial keyboard defines for L1-A processing... */ +#define SUNKBD_RESET 0xff +#define SUNKBD_L1 0x01 +#define SUNKBD_UP 0x80 +#define SUNKBD_A 0x4d + +extern unsigned int suncore_mouse_baud_cflag_next(unsigned int, int *); +extern int suncore_mouse_baud_detection(unsigned char, int); + +extern int sunserial_register_minors(struct uart_driver *, int); +extern void sunserial_unregister_minors(struct uart_driver *, int); + +extern int sunserial_console_match(struct console *, struct device_node *, + struct uart_driver *, int, bool); +extern void sunserial_console_termios(struct console *, + struct device_node *); + +#endif /* !(_SERIAL_SUN_H) */ diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c new file mode 100644 index 000000000000..c9014868297d --- /dev/null +++ b/drivers/tty/serial/sunhv.c @@ -0,0 +1,661 @@ +/* sunhv.c: Serial driver for SUN4V hypervisor console. + * + * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include "suncore.h" + +#define CON_BREAK ((long)-1) +#define CON_HUP ((long)-2) + +#define IGNORE_BREAK 0x1 +#define IGNORE_ALL 0x2 + +static char *con_write_page; +static char *con_read_page; + +static int hung_up = 0; + +static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit) +{ + while (!uart_circ_empty(xmit)) { + long status = sun4v_con_putchar(xmit->buf[xmit->tail]); + + if (status != HV_EOK) + break; + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } +} + +static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit) +{ + while (!uart_circ_empty(xmit)) { + unsigned long ra = __pa(xmit->buf + xmit->tail); + unsigned long len, status, sent; + + len = CIRC_CNT_TO_END(xmit->head, xmit->tail, + UART_XMIT_SIZE); + status = sun4v_con_write(ra, len, &sent); + if (status != HV_EOK) + break; + xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1); + port->icount.tx += sent; + } +} + +static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty) +{ + int saw_console_brk = 0; + int limit = 10000; + + while (limit-- > 0) { + long status; + long c = sun4v_con_getchar(&status); + + if (status == HV_EWOULDBLOCK) + break; + + if (c == CON_BREAK) { + if (uart_handle_break(port)) + continue; + saw_console_brk = 1; + c = 0; + } + + if (c == CON_HUP) { + hung_up = 1; + uart_handle_dcd_change(port, 0); + } else if (hung_up) { + hung_up = 0; + uart_handle_dcd_change(port, 1); + } + + if (tty == NULL) { + uart_handle_sysrq_char(port, c); + continue; + } + + port->icount.rx++; + + if (uart_handle_sysrq_char(port, c)) + continue; + + tty_insert_flip_char(tty, c, TTY_NORMAL); + } + + return saw_console_brk; +} + +static int receive_chars_read(struct uart_port *port, struct tty_struct *tty) +{ + int saw_console_brk = 0; + int limit = 10000; + + while (limit-- > 0) { + unsigned long ra = __pa(con_read_page); + unsigned long bytes_read, i; + long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read); + + if (stat != HV_EOK) { + bytes_read = 0; + + if (stat == CON_BREAK) { + if (uart_handle_break(port)) + continue; + saw_console_brk = 1; + *con_read_page = 0; + bytes_read = 1; + } else if (stat == CON_HUP) { + hung_up = 1; + uart_handle_dcd_change(port, 0); + continue; + } else { + /* HV_EWOULDBLOCK, etc. */ + break; + } + } + + if (hung_up) { + hung_up = 0; + uart_handle_dcd_change(port, 1); + } + + for (i = 0; i < bytes_read; i++) + uart_handle_sysrq_char(port, con_read_page[i]); + + if (tty == NULL) + continue; + + port->icount.rx += bytes_read; + + tty_insert_flip_string(tty, con_read_page, bytes_read); + } + + return saw_console_brk; +} + +struct sunhv_ops { + void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit); + int (*receive_chars)(struct uart_port *port, struct tty_struct *tty); +}; + +static struct sunhv_ops bychar_ops = { + .transmit_chars = transmit_chars_putchar, + .receive_chars = receive_chars_getchar, +}; + +static struct sunhv_ops bywrite_ops = { + .transmit_chars = transmit_chars_write, + .receive_chars = receive_chars_read, +}; + +static struct sunhv_ops *sunhv_ops = &bychar_ops; + +static struct tty_struct *receive_chars(struct uart_port *port) +{ + struct tty_struct *tty = NULL; + + if (port->state != NULL) /* Unopened serial console */ + tty = port->state->port.tty; + + if (sunhv_ops->receive_chars(port, tty)) + sun_do_break(); + + return tty; +} + +static void transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit; + + if (!port->state) + return; + + xmit = &port->state->xmit; + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + sunhv_ops->transmit_chars(port, xmit); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static irqreturn_t sunhv_interrupt(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct tty_struct *tty; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + tty = receive_chars(port); + transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); + + if (tty) + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + +/* port->lock is not held. */ +static unsigned int sunhv_tx_empty(struct uart_port *port) +{ + /* Transmitter is always empty for us. If the circ buffer + * is non-empty or there is an x_char pending, our caller + * will do the right thing and ignore what we return here. + */ + return TIOCSER_TEMT; +} + +/* port->lock held by caller. */ +static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + return; +} + +/* port->lock is held by caller and interrupts are disabled. */ +static unsigned int sunhv_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; +} + +/* port->lock held by caller. */ +static void sunhv_stop_tx(struct uart_port *port) +{ + return; +} + +/* port->lock held by caller. */ +static void sunhv_start_tx(struct uart_port *port) +{ + transmit_chars(port); +} + +/* port->lock is not held. */ +static void sunhv_send_xchar(struct uart_port *port, char ch) +{ + unsigned long flags; + int limit = 10000; + + spin_lock_irqsave(&port->lock, flags); + + while (limit-- > 0) { + long status = sun4v_con_putchar(ch); + if (status == HV_EOK) + break; + udelay(1); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* port->lock held by caller. */ +static void sunhv_stop_rx(struct uart_port *port) +{ +} + +/* port->lock held by caller. */ +static void sunhv_enable_ms(struct uart_port *port) +{ +} + +/* port->lock is not held. */ +static void sunhv_break_ctl(struct uart_port *port, int break_state) +{ + if (break_state) { + unsigned long flags; + int limit = 10000; + + spin_lock_irqsave(&port->lock, flags); + + while (limit-- > 0) { + long status = sun4v_con_putchar(CON_BREAK); + if (status == HV_EOK) + break; + udelay(1); + } + + spin_unlock_irqrestore(&port->lock, flags); + } +} + +/* port->lock is not held. */ +static int sunhv_startup(struct uart_port *port) +{ + return 0; +} + +/* port->lock is not held. */ +static void sunhv_shutdown(struct uart_port *port) +{ +} + +/* port->lock is not held. */ +static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + unsigned int quot = uart_get_divisor(port, baud); + unsigned int iflag, cflag; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + iflag = termios->c_iflag; + cflag = termios->c_cflag; + + port->ignore_status_mask = 0; + if (iflag & IGNBRK) + port->ignore_status_mask |= IGNORE_BREAK; + if ((cflag & CREAD) == 0) + port->ignore_status_mask |= IGNORE_ALL; + + /* XXX */ + uart_update_timeout(port, cflag, + (port->uartclk / (16 * quot))); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *sunhv_type(struct uart_port *port) +{ + return "SUN4V HCONS"; +} + +static void sunhv_release_port(struct uart_port *port) +{ +} + +static int sunhv_request_port(struct uart_port *port) +{ + return 0; +} + +static void sunhv_config_port(struct uart_port *port, int flags) +{ +} + +static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static struct uart_ops sunhv_pops = { + .tx_empty = sunhv_tx_empty, + .set_mctrl = sunhv_set_mctrl, + .get_mctrl = sunhv_get_mctrl, + .stop_tx = sunhv_stop_tx, + .start_tx = sunhv_start_tx, + .send_xchar = sunhv_send_xchar, + .stop_rx = sunhv_stop_rx, + .enable_ms = sunhv_enable_ms, + .break_ctl = sunhv_break_ctl, + .startup = sunhv_startup, + .shutdown = sunhv_shutdown, + .set_termios = sunhv_set_termios, + .type = sunhv_type, + .release_port = sunhv_release_port, + .request_port = sunhv_request_port, + .config_port = sunhv_config_port, + .verify_port = sunhv_verify_port, +}; + +static struct uart_driver sunhv_reg = { + .owner = THIS_MODULE, + .driver_name = "sunhv", + .dev_name = "ttyS", + .major = TTY_MAJOR, +}; + +static struct uart_port *sunhv_port; + +/* Copy 's' into the con_write_page, decoding "\n" into + * "\r\n" along the way. We have to return two lengths + * because the caller needs to know how much to advance + * 's' and also how many bytes to output via con_write_page. + */ +static int fill_con_write_page(const char *s, unsigned int n, + unsigned long *page_bytes) +{ + const char *orig_s = s; + char *p = con_write_page; + int left = PAGE_SIZE; + + while (n--) { + if (*s == '\n') { + if (left < 2) + break; + *p++ = '\r'; + left--; + } else if (left < 1) + break; + *p++ = *s++; + left--; + } + *page_bytes = p - con_write_page; + return s - orig_s; +} + +static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n) +{ + struct uart_port *port = sunhv_port; + 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); + + while (n > 0) { + unsigned long ra = __pa(con_write_page); + unsigned long page_bytes; + unsigned int cpy = fill_con_write_page(s, n, + &page_bytes); + + n -= cpy; + s += cpy; + while (page_bytes > 0) { + unsigned long written; + int limit = 1000000; + + while (limit--) { + unsigned long stat; + + stat = sun4v_con_write(ra, page_bytes, + &written); + if (stat == HV_EOK) + break; + udelay(1); + } + if (limit < 0) + break; + page_bytes -= written; + ra += written; + } + } + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static inline void sunhv_console_putchar(struct uart_port *port, char c) +{ + int limit = 1000000; + + while (limit-- > 0) { + long status = sun4v_con_putchar(c); + if (status == HV_EOK) + break; + udelay(1); + } +} + +static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n) +{ + struct uart_port *port = sunhv_port; + unsigned long flags; + int i, 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); + + for (i = 0; i < n; i++) { + if (*s == '\n') + sunhv_console_putchar(port, '\r'); + sunhv_console_putchar(port, *s++); + } + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static struct console sunhv_console = { + .name = "ttyHV", + .write = sunhv_console_write_bychar, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sunhv_reg, +}; + +static int __devinit hv_probe(struct platform_device *op, const struct of_device_id *match) +{ + struct uart_port *port; + unsigned long minor; + int err; + + if (op->archdata.irqs[0] == 0xffffffff) + return -ENODEV; + + port = kzalloc(sizeof(struct uart_port), GFP_KERNEL); + if (unlikely(!port)) + return -ENOMEM; + + minor = 1; + if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 && + minor >= 1) { + err = -ENOMEM; + con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!con_write_page) + goto out_free_port; + + con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!con_read_page) + goto out_free_con_write_page; + + sunhv_console.write = sunhv_console_write_paged; + sunhv_ops = &bywrite_ops; + } + + sunhv_port = port; + + port->line = 0; + port->ops = &sunhv_pops; + port->type = PORT_SUNHV; + port->uartclk = ( 29491200 / 16 ); /* arbitrary */ + + port->membase = (unsigned char __iomem *) __pa(port); + + port->irq = op->archdata.irqs[0]; + + port->dev = &op->dev; + + err = sunserial_register_minors(&sunhv_reg, 1); + if (err) + goto out_free_con_read_page; + + sunserial_console_match(&sunhv_console, op->dev.of_node, + &sunhv_reg, port->line, false); + + err = uart_add_one_port(&sunhv_reg, port); + if (err) + goto out_unregister_driver; + + err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port); + if (err) + goto out_remove_port; + + dev_set_drvdata(&op->dev, port); + + return 0; + +out_remove_port: + uart_remove_one_port(&sunhv_reg, port); + +out_unregister_driver: + sunserial_unregister_minors(&sunhv_reg, 1); + +out_free_con_read_page: + kfree(con_read_page); + +out_free_con_write_page: + kfree(con_write_page); + +out_free_port: + kfree(port); + sunhv_port = NULL; + return err; +} + +static int __devexit hv_remove(struct platform_device *dev) +{ + struct uart_port *port = dev_get_drvdata(&dev->dev); + + free_irq(port->irq, port); + + uart_remove_one_port(&sunhv_reg, port); + + sunserial_unregister_minors(&sunhv_reg, 1); + + kfree(port); + sunhv_port = NULL; + + dev_set_drvdata(&dev->dev, NULL); + + return 0; +} + +static const struct of_device_id hv_match[] = { + { + .name = "console", + .compatible = "qcn", + }, + { + .name = "console", + .compatible = "SUNW,sun4v-console", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, hv_match); + +static struct of_platform_driver hv_driver = { + .driver = { + .name = "hv", + .owner = THIS_MODULE, + .of_match_table = hv_match, + }, + .probe = hv_probe, + .remove = __devexit_p(hv_remove), +}; + +static int __init sunhv_init(void) +{ + if (tlb_type != hypervisor) + return -ENODEV; + + return of_register_platform_driver(&hv_driver); +} + +static void __exit sunhv_exit(void) +{ + of_unregister_platform_driver(&hv_driver); +} + +module_init(sunhv_init); +module_exit(sunhv_exit); + +MODULE_AUTHOR("David S. Miller"); +MODULE_DESCRIPTION("SUN4V Hypervisor console driver"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sunsab.c b/drivers/tty/serial/sunsab.c new file mode 100644 index 000000000000..5b246b18f42f --- /dev/null +++ b/drivers/tty/serial/sunsab.c @@ -0,0 +1,1152 @@ +/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2002, 2006 David S. Miller (davem@davemloft.net) + * + * Rewrote buffer handling to use CIRC(Circular Buffer) macros. + * Maxim Krasnyanskiy + * + * Fixed to use tty_get_baud_rate, and to allow for arbitrary baud + * rates to be programmed into the UART. Also eliminated a lot of + * duplicated code in the console setup. + * Theodore Ts'o , 2001-Oct-12 + * + * Ported to new 2.5.x UART layer. + * David S. Miller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_SERIAL_SUNSAB_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include "suncore.h" +#include "sunsab.h" + +struct uart_sunsab_port { + struct uart_port port; /* Generic UART port */ + union sab82532_async_regs __iomem *regs; /* Chip registers */ + unsigned long irqflags; /* IRQ state flags */ + int dsr; /* Current DSR state */ + unsigned int cec_timeout; /* Chip poll timeout... */ + unsigned int tec_timeout; /* likewise */ + unsigned char interrupt_mask0;/* ISR0 masking */ + unsigned char interrupt_mask1;/* ISR1 masking */ + unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */ + unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */ + unsigned int gis_shift; + int type; /* SAB82532 version */ + + /* Setting configuration bits while the transmitter is active + * can cause garbage characters to get emitted by the chip. + * Therefore, we cache such writes here and do the real register + * write the next time the transmitter becomes idle. + */ + unsigned int cached_ebrg; + unsigned char cached_mode; + unsigned char cached_pvr; + unsigned char cached_dafo; +}; + +/* + * This assumes you have a 29.4912 MHz clock for your UART. + */ +#define SAB_BASE_BAUD ( 29491200 / 16 ) + +static char *sab82532_version[16] = { + "V1.0", "V2.0", "V3.2", "V(0x03)", + "V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)", + "V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)", + "V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)" +}; + +#define SAB82532_MAX_TEC_TIMEOUT 200000 /* 1 character time (at 50 baud) */ +#define SAB82532_MAX_CEC_TIMEOUT 50000 /* 2.5 TX CLKs (at 50 baud) */ + +#define SAB82532_RECV_FIFO_SIZE 32 /* Standard async fifo sizes */ +#define SAB82532_XMIT_FIFO_SIZE 32 + +static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up) +{ + int timeout = up->tec_timeout; + + while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout) + udelay(1); +} + +static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up) +{ + int timeout = up->cec_timeout; + + while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout) + udelay(1); +} + +static struct tty_struct * +receive_chars(struct uart_sunsab_port *up, + union sab82532_irq_status *stat) +{ + struct tty_struct *tty = NULL; + unsigned char buf[32]; + int saw_console_brk = 0; + int free_fifo = 0; + int count = 0; + int i; + + if (up->port.state != NULL) /* Unopened serial console */ + tty = up->port.state->port.tty; + + /* Read number of BYTES (Character + Status) available. */ + if (stat->sreg.isr0 & SAB82532_ISR0_RPF) { + count = SAB82532_RECV_FIFO_SIZE; + free_fifo++; + } + + if (stat->sreg.isr0 & SAB82532_ISR0_TCD) { + count = readb(&up->regs->r.rbcl) & (SAB82532_RECV_FIFO_SIZE - 1); + free_fifo++; + } + + /* Issue a FIFO read command in case we where idle. */ + if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { + sunsab_cec_wait(up); + writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr); + return tty; + } + + if (stat->sreg.isr0 & SAB82532_ISR0_RFO) + free_fifo++; + + /* Read the FIFO. */ + for (i = 0; i < count; i++) + buf[i] = readb(&up->regs->r.rfifo[i]); + + /* Issue Receive Message Complete command. */ + if (free_fifo) { + sunsab_cec_wait(up); + writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr); + } + + /* Count may be zero for BRK, so we check for it here */ + if ((stat->sreg.isr1 & SAB82532_ISR1_BRK) && + (up->port.line == up->port.cons->index)) + saw_console_brk = 1; + + for (i = 0; i < count; i++) { + unsigned char ch = buf[i], flag; + + if (tty == NULL) { + uart_handle_sysrq_char(&up->port, ch); + continue; + } + + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR | + SAB82532_ISR0_FERR | + SAB82532_ISR0_RFO)) || + unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) { + /* + * For statistics only + */ + if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { + stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR | + SAB82532_ISR0_FERR); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + continue; + } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR) + up->port.icount.parity++; + else if (stat->sreg.isr0 & SAB82532_ISR0_FERR) + up->port.icount.frame++; + if (stat->sreg.isr0 & SAB82532_ISR0_RFO) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + stat->sreg.isr0 &= (up->port.read_status_mask & 0xff); + stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff); + + if (stat->sreg.isr1 & SAB82532_ISR1_BRK) { + flag = TTY_BREAK; + } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR) + flag = TTY_PARITY; + else if (stat->sreg.isr0 & SAB82532_ISR0_FERR) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + continue; + + if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 && + (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0) + tty_insert_flip_char(tty, ch, flag); + if (stat->sreg.isr0 & SAB82532_ISR0_RFO) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + if (saw_console_brk) + sun_do_break(); + + return tty; +} + +static void sunsab_stop_tx(struct uart_port *); +static void sunsab_tx_idle(struct uart_sunsab_port *); + +static void transmit_chars(struct uart_sunsab_port *up, + union sab82532_irq_status *stat) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int i; + + if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { + up->interrupt_mask1 |= SAB82532_IMR1_ALLS; + writeb(up->interrupt_mask1, &up->regs->w.imr1); + set_bit(SAB82532_ALLS, &up->irqflags); + } + +#if 0 /* bde@nwlink.com says this check causes problems */ + if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR)) + return; +#endif + + if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW)) + return; + + set_bit(SAB82532_XPR, &up->irqflags); + sunsab_tx_idle(up); + + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + up->interrupt_mask1 |= SAB82532_IMR1_XPR; + writeb(up->interrupt_mask1, &up->regs->w.imr1); + return; + } + + up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); + writeb(up->interrupt_mask1, &up->regs->w.imr1); + clear_bit(SAB82532_ALLS, &up->irqflags); + + /* Stuff 32 bytes into Transmit FIFO. */ + clear_bit(SAB82532_XPR, &up->irqflags); + for (i = 0; i < up->port.fifosize; i++) { + writeb(xmit->buf[xmit->tail], + &up->regs->w.xfifo[i]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + /* Issue a Transmit Frame command. */ + sunsab_cec_wait(up); + writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + sunsab_stop_tx(&up->port); +} + +static void check_status(struct uart_sunsab_port *up, + union sab82532_irq_status *stat) +{ + if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) + uart_handle_dcd_change(&up->port, + !(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD)); + + if (stat->sreg.isr1 & SAB82532_ISR1_CSC) + uart_handle_cts_change(&up->port, + (readb(&up->regs->r.star) & SAB82532_STAR_CTS)); + + if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) { + up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1; + up->port.icount.dsr++; + } + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); +} + +static irqreturn_t sunsab_interrupt(int irq, void *dev_id) +{ + struct uart_sunsab_port *up = dev_id; + struct tty_struct *tty; + union sab82532_irq_status status; + unsigned long flags; + unsigned char gis; + + spin_lock_irqsave(&up->port.lock, flags); + + status.stat = 0; + gis = readb(&up->regs->r.gis) >> up->gis_shift; + if (gis & 1) + status.sreg.isr0 = readb(&up->regs->r.isr0); + if (gis & 2) + status.sreg.isr1 = readb(&up->regs->r.isr1); + + tty = NULL; + if (status.stat) { + if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | + SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) || + (status.sreg.isr1 & SAB82532_ISR1_BRK)) + tty = receive_chars(up, &status); + if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || + (status.sreg.isr1 & SAB82532_ISR1_CSC)) + check_status(up, &status); + if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR)) + transmit_chars(up, &status); + } + + spin_unlock_irqrestore(&up->port.lock, flags); + + if (tty) + tty_flip_buffer_push(tty); + + return IRQ_HANDLED; +} + +/* port->lock is not held. */ +static unsigned int sunsab_tx_empty(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + int ret; + + /* Do not need a lock for a state test like this. */ + if (test_bit(SAB82532_ALLS, &up->irqflags)) + ret = TIOCSER_TEMT; + else + ret = 0; + + return ret; +} + +/* port->lock held by caller. */ +static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + + if (mctrl & TIOCM_RTS) { + up->cached_mode &= ~SAB82532_MODE_FRTS; + up->cached_mode |= SAB82532_MODE_RTS; + } else { + up->cached_mode |= (SAB82532_MODE_FRTS | + SAB82532_MODE_RTS); + } + if (mctrl & TIOCM_DTR) { + up->cached_pvr &= ~(up->pvr_dtr_bit); + } else { + up->cached_pvr |= up->pvr_dtr_bit; + } + + set_bit(SAB82532_REGS_PENDING, &up->irqflags); + if (test_bit(SAB82532_XPR, &up->irqflags)) + sunsab_tx_idle(up); +} + +/* port->lock is held by caller and interrupts are disabled. */ +static unsigned int sunsab_get_mctrl(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + unsigned char val; + unsigned int result; + + result = 0; + + val = readb(&up->regs->r.pvr); + result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR; + + val = readb(&up->regs->r.vstr); + result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR; + + val = readb(&up->regs->r.star); + result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0; + + return result; +} + +/* port->lock held by caller. */ +static void sunsab_stop_tx(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + + up->interrupt_mask1 |= SAB82532_IMR1_XPR; + writeb(up->interrupt_mask1, &up->regs->w.imr1); +} + +/* port->lock held by caller. */ +static void sunsab_tx_idle(struct uart_sunsab_port *up) +{ + if (test_bit(SAB82532_REGS_PENDING, &up->irqflags)) { + u8 tmp; + + clear_bit(SAB82532_REGS_PENDING, &up->irqflags); + writeb(up->cached_mode, &up->regs->rw.mode); + writeb(up->cached_pvr, &up->regs->rw.pvr); + writeb(up->cached_dafo, &up->regs->w.dafo); + + writeb(up->cached_ebrg & 0xff, &up->regs->w.bgr); + tmp = readb(&up->regs->rw.ccr2); + tmp &= ~0xc0; + tmp |= (up->cached_ebrg >> 2) & 0xc0; + writeb(tmp, &up->regs->rw.ccr2); + } +} + +/* port->lock held by caller. */ +static void sunsab_start_tx(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + struct circ_buf *xmit = &up->port.state->xmit; + int i; + + up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); + writeb(up->interrupt_mask1, &up->regs->w.imr1); + + if (!test_bit(SAB82532_XPR, &up->irqflags)) + return; + + clear_bit(SAB82532_ALLS, &up->irqflags); + clear_bit(SAB82532_XPR, &up->irqflags); + + for (i = 0; i < up->port.fifosize; i++) { + writeb(xmit->buf[xmit->tail], + &up->regs->w.xfifo[i]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + /* Issue a Transmit Frame command. */ + sunsab_cec_wait(up); + writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr); +} + +/* port->lock is not held. */ +static void sunsab_send_xchar(struct uart_port *port, char ch) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + sunsab_tec_wait(up); + writeb(ch, &up->regs->w.tic); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* port->lock held by caller. */ +static void sunsab_stop_rx(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + + up->interrupt_mask0 |= SAB82532_IMR0_TCD; + writeb(up->interrupt_mask1, &up->regs->w.imr0); +} + +/* port->lock held by caller. */ +static void sunsab_enable_ms(struct uart_port *port) +{ + /* For now we always receive these interrupts. */ +} + +/* port->lock is not held. */ +static void sunsab_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + unsigned long flags; + unsigned char val; + + spin_lock_irqsave(&up->port.lock, flags); + + val = up->cached_dafo; + if (break_state) + val |= SAB82532_DAFO_XBRK; + else + val &= ~SAB82532_DAFO_XBRK; + up->cached_dafo = val; + + set_bit(SAB82532_REGS_PENDING, &up->irqflags); + if (test_bit(SAB82532_XPR, &up->irqflags)) + sunsab_tx_idle(up); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* port->lock is not held. */ +static int sunsab_startup(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + unsigned long flags; + unsigned char tmp; + int err = request_irq(up->port.irq, sunsab_interrupt, + IRQF_SHARED, "sab", up); + if (err) + return err; + + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Wait for any commands or immediate characters + */ + sunsab_cec_wait(up); + sunsab_tec_wait(up); + + /* + * Clear the FIFO buffers. + */ + writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr); + sunsab_cec_wait(up); + writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr); + + /* + * Clear the interrupt registers. + */ + (void) readb(&up->regs->r.isr0); + (void) readb(&up->regs->r.isr1); + + /* + * Now, initialize the UART + */ + writeb(0, &up->regs->w.ccr0); /* power-down */ + writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | + SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0); + writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1); + writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | + SAB82532_CCR2_TOE, &up->regs->w.ccr2); + writeb(0, &up->regs->w.ccr3); + writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4); + up->cached_mode = (SAB82532_MODE_RTS | SAB82532_MODE_FCTS | + SAB82532_MODE_RAC); + writeb(up->cached_mode, &up->regs->w.mode); + writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc); + + tmp = readb(&up->regs->rw.ccr0); + tmp |= SAB82532_CCR0_PU; /* power-up */ + writeb(tmp, &up->regs->rw.ccr0); + + /* + * Finally, enable interrupts + */ + up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | + SAB82532_IMR0_PLLA); + writeb(up->interrupt_mask0, &up->regs->w.imr0); + up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | + SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | + SAB82532_IMR1_CSC | SAB82532_IMR1_XON | + SAB82532_IMR1_XPR); + writeb(up->interrupt_mask1, &up->regs->w.imr1); + set_bit(SAB82532_ALLS, &up->irqflags); + set_bit(SAB82532_XPR, &up->irqflags); + + spin_unlock_irqrestore(&up->port.lock, flags); + + return 0; +} + +/* port->lock is not held. */ +static void sunsab_shutdown(struct uart_port *port) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + /* Disable Interrupts */ + up->interrupt_mask0 = 0xff; + writeb(up->interrupt_mask0, &up->regs->w.imr0); + up->interrupt_mask1 = 0xff; + writeb(up->interrupt_mask1, &up->regs->w.imr1); + + /* Disable break condition */ + up->cached_dafo = readb(&up->regs->rw.dafo); + up->cached_dafo &= ~SAB82532_DAFO_XBRK; + writeb(up->cached_dafo, &up->regs->rw.dafo); + + /* Disable Receiver */ + up->cached_mode &= ~SAB82532_MODE_RAC; + writeb(up->cached_mode, &up->regs->rw.mode); + + /* + * XXX FIXME + * + * If the chip is powered down here the system hangs/crashes during + * reboot or shutdown. This needs to be investigated further, + * similar behaviour occurs in 2.4 when the driver is configured + * as a module only. One hint may be that data is sometimes + * transmitted at 9600 baud during shutdown (regardless of the + * speed the chip was configured for when the port was open). + */ +#if 0 + /* Power Down */ + tmp = readb(&up->regs->rw.ccr0); + tmp &= ~SAB82532_CCR0_PU; + writeb(tmp, &up->regs->rw.ccr0); +#endif + + spin_unlock_irqrestore(&up->port.lock, flags); + free_irq(up->port.irq, up); +} + +/* + * This is used to figure out the divisor speeds. + * + * The formula is: Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)), + * + * with 0 <= N < 64 and 0 <= M < 16 + */ + +static void calc_ebrg(int baud, int *n_ret, int *m_ret) +{ + int n, m; + + if (baud == 0) { + *n_ret = 0; + *m_ret = 0; + return; + } + + /* + * We scale numbers by 10 so that we get better accuracy + * without having to use floating point. Here we increment m + * until n is within the valid range. + */ + n = (SAB_BASE_BAUD * 10) / baud; + m = 0; + while (n >= 640) { + n = n / 2; + m++; + } + n = (n+5) / 10; + /* + * We try very hard to avoid speeds with M == 0 since they may + * not work correctly for XTAL frequences above 10 MHz. + */ + if ((m == 0) && ((n & 1) == 0)) { + n = n / 2; + m++; + } + *n_ret = n - 1; + *m_ret = m; +} + +/* Internal routine, port->lock is held and local interrupts are disabled. */ +static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag, + unsigned int iflag, unsigned int baud, + unsigned int quot) +{ + unsigned char dafo; + int bits, n, m; + + /* Byte size and parity */ + switch (cflag & CSIZE) { + case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break; + case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break; + case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break; + case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: dafo = SAB82532_DAFO_CHL5; bits = 7; break; + } + + if (cflag & CSTOPB) { + dafo |= SAB82532_DAFO_STOP; + bits++; + } + + if (cflag & PARENB) { + dafo |= SAB82532_DAFO_PARE; + bits++; + } + + if (cflag & PARODD) { + dafo |= SAB82532_DAFO_PAR_ODD; + } else { + dafo |= SAB82532_DAFO_PAR_EVEN; + } + up->cached_dafo = dafo; + + calc_ebrg(baud, &n, &m); + + up->cached_ebrg = n | (m << 6); + + up->tec_timeout = (10 * 1000000) / baud; + up->cec_timeout = up->tec_timeout >> 2; + + /* CTS flow control flags */ + /* We encode read_status_mask and ignore_status_mask like so: + * + * --------------------- + * | ... | ISR1 | ISR0 | + * --------------------- + * .. 15 8 7 0 + */ + + up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | + SAB82532_ISR0_RFO | SAB82532_ISR0_RPF | + SAB82532_ISR0_CDSC); + up->port.read_status_mask |= (SAB82532_ISR1_CSC | + SAB82532_ISR1_ALLS | + SAB82532_ISR1_XPR) << 8; + if (iflag & INPCK) + up->port.read_status_mask |= (SAB82532_ISR0_PERR | + SAB82532_ISR0_FERR); + if (iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8); + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= (SAB82532_ISR0_PERR | + SAB82532_ISR0_FERR); + if (iflag & IGNBRK) { + up->port.ignore_status_mask |= (SAB82532_ISR1_BRK << 8); + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + up->port.ignore_status_mask |= SAB82532_ISR0_RFO; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + up->port.ignore_status_mask |= (SAB82532_ISR0_RPF | + SAB82532_ISR0_TCD); + + uart_update_timeout(&up->port, cflag, + (up->port.uartclk / (16 * quot))); + + /* Now schedule a register update when the chip's + * transmitter is idle. + */ + up->cached_mode |= SAB82532_MODE_RAC; + set_bit(SAB82532_REGS_PENDING, &up->irqflags); + if (test_bit(SAB82532_XPR, &up->irqflags)) + sunsab_tx_idle(up); +} + +/* port->lock is not held. */ +static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; + unsigned long flags; + unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + unsigned int quot = uart_get_divisor(port, baud); + + spin_lock_irqsave(&up->port.lock, flags); + sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static const char *sunsab_type(struct uart_port *port) +{ + struct uart_sunsab_port *up = (void *)port; + static char buf[36]; + + sprintf(buf, "SAB82532 %s", sab82532_version[up->type]); + return buf; +} + +static void sunsab_release_port(struct uart_port *port) +{ +} + +static int sunsab_request_port(struct uart_port *port) +{ + return 0; +} + +static void sunsab_config_port(struct uart_port *port, int flags) +{ +} + +static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static struct uart_ops sunsab_pops = { + .tx_empty = sunsab_tx_empty, + .set_mctrl = sunsab_set_mctrl, + .get_mctrl = sunsab_get_mctrl, + .stop_tx = sunsab_stop_tx, + .start_tx = sunsab_start_tx, + .send_xchar = sunsab_send_xchar, + .stop_rx = sunsab_stop_rx, + .enable_ms = sunsab_enable_ms, + .break_ctl = sunsab_break_ctl, + .startup = sunsab_startup, + .shutdown = sunsab_shutdown, + .set_termios = sunsab_set_termios, + .type = sunsab_type, + .release_port = sunsab_release_port, + .request_port = sunsab_request_port, + .config_port = sunsab_config_port, + .verify_port = sunsab_verify_port, +}; + +static struct uart_driver sunsab_reg = { + .owner = THIS_MODULE, + .driver_name = "sunsab", + .dev_name = "ttyS", + .major = TTY_MAJOR, +}; + +static struct uart_sunsab_port *sunsab_ports; + +#ifdef CONFIG_SERIAL_SUNSAB_CONSOLE + +static void sunsab_console_putchar(struct uart_port *port, int c) +{ + struct uart_sunsab_port *up = (struct uart_sunsab_port *)port; + + sunsab_tec_wait(up); + writeb(c, &up->regs->w.tic); +} + +static void sunsab_console_write(struct console *con, const char *s, unsigned n) +{ + struct uart_sunsab_port *up = &sunsab_ports[con->index]; + unsigned long flags; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + uart_console_write(&up->port, s, n, sunsab_console_putchar); + sunsab_tec_wait(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int sunsab_console_setup(struct console *con, char *options) +{ + struct uart_sunsab_port *up = &sunsab_ports[con->index]; + unsigned long flags; + unsigned int baud, quot; + + /* + * The console framework calls us for each and every port + * registered. Defer the console setup until the requested + * port has been properly discovered. A bit of a hack, + * though... + */ + if (up->port.type != PORT_SUNSAB) + return -1; + + printk("Console: ttyS%d (SAB82532)\n", + (sunsab_reg.minor - 64) + con->index); + + sunserial_console_termios(con, up->port.dev->of_node); + + switch (con->cflag & CBAUD) { + case B150: baud = 150; break; + case B300: baud = 300; break; + case B600: baud = 600; break; + case B1200: baud = 1200; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + default: case B9600: baud = 9600; break; + case B19200: baud = 19200; break; + case B38400: baud = 38400; break; + case B57600: baud = 57600; break; + case B115200: baud = 115200; break; + case B230400: baud = 230400; break; + case B460800: baud = 460800; break; + }; + + /* + * Temporary fix. + */ + spin_lock_init(&up->port.lock); + + /* + * Initialize the hardware + */ + sunsab_startup(&up->port); + + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Finally, enable interrupts + */ + up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR | + SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC; + writeb(up->interrupt_mask0, &up->regs->w.imr0); + up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS | + SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN | + SAB82532_IMR1_CSC | SAB82532_IMR1_XON | + SAB82532_IMR1_XPR; + writeb(up->interrupt_mask1, &up->regs->w.imr1); + + quot = uart_get_divisor(&up->port, baud); + sunsab_convert_to_sab(up, con->cflag, 0, baud, quot); + sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); + + spin_unlock_irqrestore(&up->port.lock, flags); + + return 0; +} + +static struct console sunsab_console = { + .name = "ttyS", + .write = sunsab_console_write, + .device = uart_console_device, + .setup = sunsab_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sunsab_reg, +}; + +static inline struct console *SUNSAB_CONSOLE(void) +{ + return &sunsab_console; +} +#else +#define SUNSAB_CONSOLE() (NULL) +#define sunsab_console_init() do { } while (0) +#endif + +static int __devinit sunsab_init_one(struct uart_sunsab_port *up, + struct platform_device *op, + unsigned long offset, + int line) +{ + up->port.line = line; + up->port.dev = &op->dev; + + up->port.mapbase = op->resource[0].start + offset; + up->port.membase = of_ioremap(&op->resource[0], offset, + sizeof(union sab82532_async_regs), + "sab"); + if (!up->port.membase) + return -ENOMEM; + up->regs = (union sab82532_async_regs __iomem *) up->port.membase; + + up->port.irq = op->archdata.irqs[0]; + + up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; + up->port.iotype = UPIO_MEM; + + writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc); + + up->port.ops = &sunsab_pops; + up->port.type = PORT_SUNSAB; + up->port.uartclk = SAB_BASE_BAUD; + + up->type = readb(&up->regs->r.vstr) & 0x0f; + writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr); + writeb(0xff, &up->regs->w.pim); + if ((up->port.line & 0x1) == 0) { + up->pvr_dsr_bit = (1 << 0); + up->pvr_dtr_bit = (1 << 1); + up->gis_shift = 2; + } else { + up->pvr_dsr_bit = (1 << 3); + up->pvr_dtr_bit = (1 << 2); + up->gis_shift = 0; + } + up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); + writeb(up->cached_pvr, &up->regs->w.pvr); + up->cached_mode = readb(&up->regs->rw.mode); + up->cached_mode |= SAB82532_MODE_FRTS; + writeb(up->cached_mode, &up->regs->rw.mode); + up->cached_mode |= SAB82532_MODE_RTS; + writeb(up->cached_mode, &up->regs->rw.mode); + + up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; + up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; + + return 0; +} + +static int __devinit sab_probe(struct platform_device *op, const struct of_device_id *match) +{ + static int inst; + struct uart_sunsab_port *up; + int err; + + up = &sunsab_ports[inst * 2]; + + err = sunsab_init_one(&up[0], op, + 0, + (inst * 2) + 0); + if (err) + goto out; + + err = sunsab_init_one(&up[1], op, + sizeof(union sab82532_async_regs), + (inst * 2) + 1); + if (err) + goto out1; + + sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node, + &sunsab_reg, up[0].port.line, + false); + + sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node, + &sunsab_reg, up[1].port.line, + false); + + err = uart_add_one_port(&sunsab_reg, &up[0].port); + if (err) + goto out2; + + err = uart_add_one_port(&sunsab_reg, &up[1].port); + if (err) + goto out3; + + dev_set_drvdata(&op->dev, &up[0]); + + inst++; + + return 0; + +out3: + uart_remove_one_port(&sunsab_reg, &up[0].port); +out2: + of_iounmap(&op->resource[0], + up[1].port.membase, + sizeof(union sab82532_async_regs)); +out1: + of_iounmap(&op->resource[0], + up[0].port.membase, + sizeof(union sab82532_async_regs)); +out: + return err; +} + +static int __devexit sab_remove(struct platform_device *op) +{ + struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); + + uart_remove_one_port(&sunsab_reg, &up[1].port); + uart_remove_one_port(&sunsab_reg, &up[0].port); + of_iounmap(&op->resource[0], + up[1].port.membase, + sizeof(union sab82532_async_regs)); + of_iounmap(&op->resource[0], + up[0].port.membase, + sizeof(union sab82532_async_regs)); + + dev_set_drvdata(&op->dev, NULL); + + return 0; +} + +static const struct of_device_id sab_match[] = { + { + .name = "se", + }, + { + .name = "serial", + .compatible = "sab82532", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sab_match); + +static struct of_platform_driver sab_driver = { + .driver = { + .name = "sab", + .owner = THIS_MODULE, + .of_match_table = sab_match, + }, + .probe = sab_probe, + .remove = __devexit_p(sab_remove), +}; + +static int __init sunsab_init(void) +{ + struct device_node *dp; + int err; + int num_channels = 0; + + for_each_node_by_name(dp, "se") + num_channels += 2; + for_each_node_by_name(dp, "serial") { + if (of_device_is_compatible(dp, "sab82532")) + num_channels += 2; + } + + if (num_channels) { + sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) * + num_channels, GFP_KERNEL); + if (!sunsab_ports) + return -ENOMEM; + + err = sunserial_register_minors(&sunsab_reg, num_channels); + if (err) { + kfree(sunsab_ports); + sunsab_ports = NULL; + + return err; + } + } + + return of_register_platform_driver(&sab_driver); +} + +static void __exit sunsab_exit(void) +{ + of_unregister_platform_driver(&sab_driver); + if (sunsab_reg.nr) { + sunserial_unregister_minors(&sunsab_reg, sunsab_reg.nr); + } + + kfree(sunsab_ports); + sunsab_ports = NULL; +} + +module_init(sunsab_init); +module_exit(sunsab_exit); + +MODULE_AUTHOR("Eddie C. Dost and David S. Miller"); +MODULE_DESCRIPTION("Sun SAB82532 serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sunsab.h b/drivers/tty/serial/sunsab.h new file mode 100644 index 000000000000..b78e1f7b8050 --- /dev/null +++ b/drivers/tty/serial/sunsab.h @@ -0,0 +1,322 @@ +/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + */ + +#ifndef _SUNSAB_H +#define _SUNSAB_H + +struct sab82532_async_rd_regs { + u8 rfifo[0x20]; /* Receive FIFO */ + u8 star; /* Status Register */ + u8 __pad1; + u8 mode; /* Mode Register */ + u8 timr; /* Timer Register */ + u8 xon; /* XON Character */ + u8 xoff; /* XOFF Character */ + u8 tcr; /* Termination Character Register */ + u8 dafo; /* Data Format */ + u8 rfc; /* RFIFO Control Register */ + u8 __pad2; + u8 rbcl; /* Receive Byte Count Low */ + u8 rbch; /* Receive Byte Count High */ + u8 ccr0; /* Channel Configuration Register 0 */ + u8 ccr1; /* Channel Configuration Register 1 */ + u8 ccr2; /* Channel Configuration Register 2 */ + u8 ccr3; /* Channel Configuration Register 3 */ + u8 __pad3[4]; + u8 vstr; /* Version Status Register */ + u8 __pad4[3]; + u8 gis; /* Global Interrupt Status */ + u8 ipc; /* Interrupt Port Configuration */ + u8 isr0; /* Interrupt Status 0 */ + u8 isr1; /* Interrupt Status 1 */ + u8 pvr; /* Port Value Register */ + u8 pis; /* Port Interrupt Status */ + u8 pcr; /* Port Configuration Register */ + u8 ccr4; /* Channel Configuration Register 4 */ +}; + +struct sab82532_async_wr_regs { + u8 xfifo[0x20]; /* Transmit FIFO */ + u8 cmdr; /* Command Register */ + u8 __pad1; + u8 mode; + u8 timr; + u8 xon; + u8 xoff; + u8 tcr; + u8 dafo; + u8 rfc; + u8 __pad2; + u8 xbcl; /* Transmit Byte Count Low */ + u8 xbch; /* Transmit Byte Count High */ + u8 ccr0; + u8 ccr1; + u8 ccr2; + u8 ccr3; + u8 tsax; /* Time-Slot Assignment Reg. Transmit */ + u8 tsar; /* Time-Slot Assignment Reg. Receive */ + u8 xccr; /* Transmit Channel Capacity Register */ + u8 rccr; /* Receive Channel Capacity Register */ + u8 bgr; /* Baud Rate Generator Register */ + u8 tic; /* Transmit Immediate Character */ + u8 mxn; /* Mask XON Character */ + u8 mxf; /* Mask XOFF Character */ + u8 iva; /* Interrupt Vector Address */ + u8 ipc; + u8 imr0; /* Interrupt Mask Register 0 */ + u8 imr1; /* Interrupt Mask Register 1 */ + u8 pvr; + u8 pim; /* Port Interrupt Mask */ + u8 pcr; + u8 ccr4; +}; + +struct sab82532_async_rw_regs { /* Read/Write registers */ + u8 __pad1[0x20]; + u8 __pad2; + u8 __pad3; + u8 mode; + u8 timr; + u8 xon; + u8 xoff; + u8 tcr; + u8 dafo; + u8 rfc; + u8 __pad4; + u8 __pad5; + u8 __pad6; + u8 ccr0; + u8 ccr1; + u8 ccr2; + u8 ccr3; + u8 __pad7; + u8 __pad8; + u8 __pad9; + u8 __pad10; + u8 __pad11; + u8 __pad12; + u8 __pad13; + u8 __pad14; + u8 __pad15; + u8 ipc; + u8 __pad16; + u8 __pad17; + u8 pvr; + u8 __pad18; + u8 pcr; + u8 ccr4; +}; + +union sab82532_async_regs { + __volatile__ struct sab82532_async_rd_regs r; + __volatile__ struct sab82532_async_wr_regs w; + __volatile__ struct sab82532_async_rw_regs rw; +}; + +union sab82532_irq_status { + unsigned short stat; + struct { + unsigned char isr0; + unsigned char isr1; + } sreg; +}; + +/* irqflags bits */ +#define SAB82532_ALLS 0x00000001 +#define SAB82532_XPR 0x00000002 +#define SAB82532_REGS_PENDING 0x00000004 + +/* RFIFO Status Byte */ +#define SAB82532_RSTAT_PE 0x80 +#define SAB82532_RSTAT_FE 0x40 +#define SAB82532_RSTAT_PARITY 0x01 + +/* Status Register (STAR) */ +#define SAB82532_STAR_XDOV 0x80 +#define SAB82532_STAR_XFW 0x40 +#define SAB82532_STAR_RFNE 0x20 +#define SAB82532_STAR_FCS 0x10 +#define SAB82532_STAR_TEC 0x08 +#define SAB82532_STAR_CEC 0x04 +#define SAB82532_STAR_CTS 0x02 + +/* Command Register (CMDR) */ +#define SAB82532_CMDR_RMC 0x80 +#define SAB82532_CMDR_RRES 0x40 +#define SAB82532_CMDR_RFRD 0x20 +#define SAB82532_CMDR_STI 0x10 +#define SAB82532_CMDR_XF 0x08 +#define SAB82532_CMDR_XRES 0x01 + +/* Mode Register (MODE) */ +#define SAB82532_MODE_FRTS 0x40 +#define SAB82532_MODE_FCTS 0x20 +#define SAB82532_MODE_FLON 0x10 +#define SAB82532_MODE_RAC 0x08 +#define SAB82532_MODE_RTS 0x04 +#define SAB82532_MODE_TRS 0x02 +#define SAB82532_MODE_TLP 0x01 + +/* Timer Register (TIMR) */ +#define SAB82532_TIMR_CNT_MASK 0xe0 +#define SAB82532_TIMR_VALUE_MASK 0x1f + +/* Data Format (DAFO) */ +#define SAB82532_DAFO_XBRK 0x40 +#define SAB82532_DAFO_STOP 0x20 +#define SAB82532_DAFO_PAR_SPACE 0x00 +#define SAB82532_DAFO_PAR_ODD 0x08 +#define SAB82532_DAFO_PAR_EVEN 0x10 +#define SAB82532_DAFO_PAR_MARK 0x18 +#define SAB82532_DAFO_PARE 0x04 +#define SAB82532_DAFO_CHL8 0x00 +#define SAB82532_DAFO_CHL7 0x01 +#define SAB82532_DAFO_CHL6 0x02 +#define SAB82532_DAFO_CHL5 0x03 + +/* RFIFO Control Register (RFC) */ +#define SAB82532_RFC_DPS 0x40 +#define SAB82532_RFC_DXS 0x20 +#define SAB82532_RFC_RFDF 0x10 +#define SAB82532_RFC_RFTH_1 0x00 +#define SAB82532_RFC_RFTH_4 0x04 +#define SAB82532_RFC_RFTH_16 0x08 +#define SAB82532_RFC_RFTH_32 0x0c +#define SAB82532_RFC_TCDE 0x01 + +/* Received Byte Count High (RBCH) */ +#define SAB82532_RBCH_DMA 0x80 +#define SAB82532_RBCH_CAS 0x20 + +/* Transmit Byte Count High (XBCH) */ +#define SAB82532_XBCH_DMA 0x80 +#define SAB82532_XBCH_CAS 0x20 +#define SAB82532_XBCH_XC 0x10 + +/* Channel Configuration Register 0 (CCR0) */ +#define SAB82532_CCR0_PU 0x80 +#define SAB82532_CCR0_MCE 0x40 +#define SAB82532_CCR0_SC_NRZ 0x00 +#define SAB82532_CCR0_SC_NRZI 0x08 +#define SAB82532_CCR0_SC_FM0 0x10 +#define SAB82532_CCR0_SC_FM1 0x14 +#define SAB82532_CCR0_SC_MANCH 0x18 +#define SAB82532_CCR0_SM_HDLC 0x00 +#define SAB82532_CCR0_SM_SDLC_LOOP 0x01 +#define SAB82532_CCR0_SM_BISYNC 0x02 +#define SAB82532_CCR0_SM_ASYNC 0x03 + +/* Channel Configuration Register 1 (CCR1) */ +#define SAB82532_CCR1_ODS 0x10 +#define SAB82532_CCR1_BCR 0x08 +#define SAB82532_CCR1_CM_MASK 0x07 + +/* Channel Configuration Register 2 (CCR2) */ +#define SAB82532_CCR2_SOC1 0x80 +#define SAB82532_CCR2_SOC0 0x40 +#define SAB82532_CCR2_BR9 0x80 +#define SAB82532_CCR2_BR8 0x40 +#define SAB82532_CCR2_BDF 0x20 +#define SAB82532_CCR2_SSEL 0x10 +#define SAB82532_CCR2_XCS0 0x20 +#define SAB82532_CCR2_RCS0 0x10 +#define SAB82532_CCR2_TOE 0x08 +#define SAB82532_CCR2_RWX 0x04 +#define SAB82532_CCR2_DIV 0x01 + +/* Channel Configuration Register 3 (CCR3) */ +#define SAB82532_CCR3_PSD 0x01 + +/* Time Slot Assignment Register Transmit (TSAX) */ +#define SAB82532_TSAX_TSNX_MASK 0xfc +#define SAB82532_TSAX_XCS2 0x02 /* see also CCR2 */ +#define SAB82532_TSAX_XCS1 0x01 + +/* Time Slot Assignment Register Receive (TSAR) */ +#define SAB82532_TSAR_TSNR_MASK 0xfc +#define SAB82532_TSAR_RCS2 0x02 /* see also CCR2 */ +#define SAB82532_TSAR_RCS1 0x01 + +/* Version Status Register (VSTR) */ +#define SAB82532_VSTR_CD 0x80 +#define SAB82532_VSTR_DPLA 0x40 +#define SAB82532_VSTR_VN_MASK 0x0f +#define SAB82532_VSTR_VN_1 0x00 +#define SAB82532_VSTR_VN_2 0x01 +#define SAB82532_VSTR_VN_3_2 0x02 + +/* Global Interrupt Status Register (GIS) */ +#define SAB82532_GIS_PI 0x80 +#define SAB82532_GIS_ISA1 0x08 +#define SAB82532_GIS_ISA0 0x04 +#define SAB82532_GIS_ISB1 0x02 +#define SAB82532_GIS_ISB0 0x01 + +/* Interrupt Vector Address (IVA) */ +#define SAB82532_IVA_MASK 0xf1 + +/* Interrupt Port Configuration (IPC) */ +#define SAB82532_IPC_VIS 0x80 +#define SAB82532_IPC_SLA1 0x10 +#define SAB82532_IPC_SLA0 0x08 +#define SAB82532_IPC_CASM 0x04 +#define SAB82532_IPC_IC_OPEN_DRAIN 0x00 +#define SAB82532_IPC_IC_ACT_LOW 0x01 +#define SAB82532_IPC_IC_ACT_HIGH 0x03 + +/* Interrupt Status Register 0 (ISR0) */ +#define SAB82532_ISR0_TCD 0x80 +#define SAB82532_ISR0_TIME 0x40 +#define SAB82532_ISR0_PERR 0x20 +#define SAB82532_ISR0_FERR 0x10 +#define SAB82532_ISR0_PLLA 0x08 +#define SAB82532_ISR0_CDSC 0x04 +#define SAB82532_ISR0_RFO 0x02 +#define SAB82532_ISR0_RPF 0x01 + +/* Interrupt Status Register 1 (ISR1) */ +#define SAB82532_ISR1_BRK 0x80 +#define SAB82532_ISR1_BRKT 0x40 +#define SAB82532_ISR1_ALLS 0x20 +#define SAB82532_ISR1_XOFF 0x10 +#define SAB82532_ISR1_TIN 0x08 +#define SAB82532_ISR1_CSC 0x04 +#define SAB82532_ISR1_XON 0x02 +#define SAB82532_ISR1_XPR 0x01 + +/* Interrupt Mask Register 0 (IMR0) */ +#define SAB82532_IMR0_TCD 0x80 +#define SAB82532_IMR0_TIME 0x40 +#define SAB82532_IMR0_PERR 0x20 +#define SAB82532_IMR0_FERR 0x10 +#define SAB82532_IMR0_PLLA 0x08 +#define SAB82532_IMR0_CDSC 0x04 +#define SAB82532_IMR0_RFO 0x02 +#define SAB82532_IMR0_RPF 0x01 + +/* Interrupt Mask Register 1 (IMR1) */ +#define SAB82532_IMR1_BRK 0x80 +#define SAB82532_IMR1_BRKT 0x40 +#define SAB82532_IMR1_ALLS 0x20 +#define SAB82532_IMR1_XOFF 0x10 +#define SAB82532_IMR1_TIN 0x08 +#define SAB82532_IMR1_CSC 0x04 +#define SAB82532_IMR1_XON 0x02 +#define SAB82532_IMR1_XPR 0x01 + +/* Port Interrupt Status Register (PIS) */ +#define SAB82532_PIS_SYNC_B 0x08 +#define SAB82532_PIS_DTR_B 0x04 +#define SAB82532_PIS_DTR_A 0x02 +#define SAB82532_PIS_SYNC_A 0x01 + +/* Channel Configuration Register 4 (CCR4) */ +#define SAB82532_CCR4_MCK4 0x80 +#define SAB82532_CCR4_EBRG 0x40 +#define SAB82532_CCR4_TST1 0x20 +#define SAB82532_CCR4_ICD 0x10 + + +#endif /* !(_SUNSAB_H) */ diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c new file mode 100644 index 000000000000..551ebfe3ccbb --- /dev/null +++ b/drivers/tty/serial/sunsu.c @@ -0,0 +1,1608 @@ +/* + * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI + * + * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1998-1999 Pete Zaitcev (zaitcev@yahoo.com) + * + * This is mainly a variation of 8250.c, credits go to authors mentioned + * therein. In fact this driver should be merged into the generic 8250.c + * infrastructure perhaps using a 8250_sparc.c module. + * + * Fixed to use tty_get_baud_rate(). + * Theodore Ts'o , 2001-Oct-12 + * + * Converted to new 2.5.x UART layer. + * David S. Miller (davem@davemloft.net), 2002-Jul-29 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SERIO +#include +#endif +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include "suncore.h" + +/* We are on a NS PC87303 clocked with 24.0 MHz, which results + * in a UART clock of 1.8462 MHz. + */ +#define SU_BASE_BAUD (1846200 / 16) + +enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT }; +static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" }; + +/* + * Here we define the default xmit fifo size used for each type of UART. + */ +static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "Cirrus", 1, 0 }, + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "Startech", 1, 0 }, + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO } +}; + +struct uart_sunsu_port { + struct uart_port port; + unsigned char acr; + unsigned char ier; + unsigned short rev; + unsigned char lcr; + unsigned int lsr_break_flag; + unsigned int cflag; + + /* Probing information. */ + enum su_type su_type; + unsigned int type_probed; /* XXX Stupid */ + unsigned long reg_size; + +#ifdef CONFIG_SERIO + struct serio serio; + int serio_open; +#endif +}; + +static unsigned int serial_in(struct uart_sunsu_port *up, int offset) +{ + offset <<= up->port.regshift; + + switch (up->port.iotype) { + case UPIO_HUB6: + outb(up->port.hub6 - 1 + offset, up->port.iobase); + return inb(up->port.iobase + 1); + + case UPIO_MEM: + return readb(up->port.membase + offset); + + default: + return inb(up->port.iobase + offset); + } +} + +static void serial_out(struct uart_sunsu_port *up, int offset, int value) +{ +#ifndef CONFIG_SPARC64 + /* + * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are + * connected with a gate then go to SlavIO. When IRQ4 goes tristated + * gate outputs a logical one. Since we use level triggered interrupts + * we have lockup and watchdog reset. We cannot mask IRQ because + * keyboard shares IRQ with us (Word has it as Bob Smelik's design). + * This problem is similar to what Alpha people suffer, see serial.c. + */ + if (offset == UART_MCR) + value |= UART_MCR_OUT2; +#endif + offset <<= up->port.regshift; + + switch (up->port.iotype) { + case UPIO_HUB6: + outb(up->port.hub6 - 1 + offset, up->port.iobase); + outb(value, up->port.iobase + 1); + break; + + case UPIO_MEM: + writeb(value, up->port.membase + offset); + break; + + default: + outb(value, up->port.iobase + offset); + } +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + + +/* + * For the 16C950 + */ +static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value) +{ + serial_out(up, UART_SCR, offset); + serial_out(up, UART_ICR, value); +} + +#if 0 /* Unused currently */ +static unsigned int serial_icr_read(struct uart_sunsu_port *up, int offset) +{ + unsigned int value; + + serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD); + serial_out(up, UART_SCR, offset); + value = serial_in(up, UART_ICR); + serial_icr_write(up, UART_ACR, up->acr); + + return value; +} +#endif + +#ifdef CONFIG_SERIAL_8250_RSA +/* + * Attempts to turn on the RSA FIFO. Returns zero on failure. + * We set the port uart clock rate if we succeed. + */ +static int __enable_rsa(struct uart_sunsu_port *up) +{ + unsigned char mode; + int result; + + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16; + + return result; +} + +static void enable_rsa(struct uart_sunsu_port *up) +{ + if (up->port.type == PORT_RSA) { + if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + __enable_rsa(up); + spin_unlock_irq(&up->port.lock); + } + if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) + serial_outp(up, UART_RSA_FRR, 0); + } +} + +/* + * Attempts to turn off the RSA FIFO. Returns zero on failure. + * It is unknown why interrupts were disabled in here. However, + * the caller is expected to preserve this behaviour by grabbing + * the spinlock before calling this function. + */ +static void disable_rsa(struct uart_sunsu_port *up) +{ + unsigned char mode; + int result; + + if (up->port.type == PORT_RSA && + up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) { + spin_lock_irq(&up->port.lock); + + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(up, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + if (result) + up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16; + spin_unlock_irq(&up->port.lock); + } +} +#endif /* CONFIG_SERIAL_8250_RSA */ + +static inline void __stop_tx(struct uart_sunsu_port *p) +{ + if (p->ier & UART_IER_THRI) { + p->ier &= ~UART_IER_THRI; + serial_out(p, UART_IER, p->ier); + } +} + +static void sunsu_stop_tx(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + + __stop_tx(up); + + /* + * We really want to stop the transmitter from sending. + */ + if (up->port.type == PORT_16C950) { + up->acr |= UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void sunsu_start_tx(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } + + /* + * Re-enable the transmitter if we disabled it. + */ + if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) { + up->acr &= ~UART_ACR_TXDIS; + serial_icr_write(up, UART_ACR, up->acr); + } +} + +static void sunsu_stop_rx(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static void sunsu_enable_ms(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct tty_struct * +receive_chars(struct uart_sunsu_port *up, unsigned char *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch, flag; + int max_count = 256; + int saw_console_brk = 0; + + do { + ch = serial_inp(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + if (up->port.cons != NULL && + up->port.line == up->port.cons->index) + saw_console_brk = 1; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + *status &= up->port.read_status_mask; + + if (up->port.cons != NULL && + up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } + + if (*status & UART_LSR_BI) { + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + if ((*status & up->port.ignore_status_mask) == 0) + tty_insert_flip_char(tty, ch, flag); + if (*status & UART_LSR_OE) + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + ignore_char: + *status = serial_inp(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + + if (saw_console_brk) + sun_do_break(); + + return tty; +} + +static void transmit_chars(struct uart_sunsu_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_outp(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_tx_stopped(&up->port)) { + sunsu_stop_tx(&up->port); + return; + } + if (uart_circ_empty(xmit)) { + __stop_tx(up); + return; + } + + count = up->port.fifosize; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + __stop_tx(up); +} + +static void check_modem_status(struct uart_sunsu_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); +} + +static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) +{ + struct uart_sunsu_port *up = dev_id; + unsigned long flags; + unsigned char status; + + spin_lock_irqsave(&up->port.lock, flags); + + do { + struct tty_struct *tty; + + status = serial_inp(up, UART_LSR); + tty = NULL; + if (status & UART_LSR_DR) + tty = receive_chars(up, &status); + check_modem_status(up); + if (status & UART_LSR_THRE) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + + if (tty) + tty_flip_buffer_push(tty); + + spin_lock_irqsave(&up->port.lock, flags); + + } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)); + + spin_unlock_irqrestore(&up->port.lock, flags); + + return IRQ_HANDLED; +} + +/* Separate interrupt handling path for keyboard/mouse ports. */ + +static void +sunsu_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot); + +static void sunsu_change_mouse_baud(struct uart_sunsu_port *up) +{ + unsigned int cur_cflag = up->cflag; + int quot, new_baud; + + up->cflag &= ~CBAUD; + up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud); + + quot = up->port.uartclk / (16 * new_baud); + + sunsu_change_speed(&up->port, up->cflag, 0, quot); +} + +static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break) +{ + do { + unsigned char ch = serial_inp(up, UART_RX); + + /* Stop-A is handled by drivers/char/keyboard.c now. */ + if (up->su_type == SU_PORT_KBD) { +#ifdef CONFIG_SERIO + serio_interrupt(&up->serio, ch, 0); +#endif + } else if (up->su_type == SU_PORT_MS) { + int ret = suncore_mouse_baud_detection(ch, is_break); + + switch (ret) { + case 2: + sunsu_change_mouse_baud(up); + /* fallthru */ + case 1: + break; + + case 0: +#ifdef CONFIG_SERIO + serio_interrupt(&up->serio, ch, 0); +#endif + break; + }; + } + } while (serial_in(up, UART_LSR) & UART_LSR_DR); +} + +static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id) +{ + struct uart_sunsu_port *up = dev_id; + + if (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)) { + unsigned char status = serial_inp(up, UART_LSR); + + if ((status & UART_LSR_DR) || (status & UART_LSR_BI)) + receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0); + } + + return IRQ_HANDLED; +} + +static unsigned int sunsu_tx_empty(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int sunsu_get_mctrl(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned char status; + unsigned int ret; + + status = serial_in(up, 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 sunsu_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned char mcr = 0; + + 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(up, UART_MCR, mcr); +} + +static void sunsu_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int sunsu_startup(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned long flags; + int retval; + + if (up->port.type == PORT_16C950) { + /* Wake up and initialize UART */ + up->acr = 0; + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_IER, 0); + serial_outp(up, UART_LCR, 0); + serial_icr_write(up, UART_CSR, 0); /* Reset the UART */ + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0); + } + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * If this is an RSA port, see if we can kick it up to the + * higher speed clock. + */ + enable_rsa(up); +#endif + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_FCR, 0); + } + + /* + * Clear the interrupt registers. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + /* + * At this point, there's no way the LSR could still be 0xff; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (!(up->port.flags & UPF_BUGGY_UART) && + (serial_inp(up, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", up->port.line); + return -ENODEV; + } + + if (up->su_type != SU_PORT_PORT) { + retval = request_irq(up->port.irq, sunsu_kbd_ms_interrupt, + IRQF_SHARED, su_typev[up->su_type], up); + } else { + retval = request_irq(up->port.irq, sunsu_serial_interrupt, + IRQF_SHARED, su_typev[up->su_type], up); + } + if (retval) { + printk("su: Cannot register IRQ %d\n", up->port.irq); + return retval; + } + + /* + * Now, initialize the UART + */ + serial_outp(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.mctrl |= TIOCM_OUT2; + + sunsu_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(up, UART_IER, up->ier); + + if (up->port.flags & UPF_FOURPORT) { + unsigned int icp; + /* + * Enable interrupts on the AST Fourport board + */ + icp = (up->port.iobase & 0xfe0) | 0x01f; + outb_p(0x80, icp); + (void) inb_p(icp); + } + + /* + * And clear the interrupt registers again for luck. + */ + (void) serial_inp(up, UART_LSR); + (void) serial_inp(up, UART_RX); + (void) serial_inp(up, UART_IIR); + (void) serial_inp(up, UART_MSR); + + return 0; +} + +static void sunsu_shutdown(struct uart_port *port) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned long flags; + + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_outp(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + if (up->port.flags & UPF_FOURPORT) { + /* reset interrupts on the AST Fourport board */ + inb((up->port.iobase & 0xfe0) | 0x1f); + up->port.mctrl |= TIOCM_OUT1; + } else + up->port.mctrl &= ~TIOCM_OUT2; + + sunsu_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(up, UART_FCR, 0); + +#ifdef CONFIG_SERIAL_8250_RSA + /* + * Reset the RSA board back to 115kbps compat mode. + */ + disable_rsa(up); +#endif + + /* + * Read data port to reset things. + */ + (void) serial_in(up, UART_RX); + + free_irq(up->port.irq, up); +} + +static void +sunsu_change_speed(struct uart_port *port, unsigned int cflag, + unsigned int iflag, unsigned int quot) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + unsigned char cval, fcr = 0; + unsigned long flags; + + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + /* + * Work around a bug in the Oxford Semiconductor 952 rev B + * chip which causes it to seriously miscalculate baud rates + * when DLL is 0. + */ + if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 && + up->rev == 0x5201) + quot ++; + + if (uart_config[up->port.type].flags & UART_USE_FIFO) { + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_8250_RSA + else if (up->port.type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (up->port.type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, cflag, (port->uartclk / (16 * quot))); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + if (uart_config[up->port.type].flags & UART_STARTECH) { + serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0); + } + serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ + if (up->port.type == PORT_16750) + serial_outp(up, UART_FCR, fcr); /* set fcr */ + serial_outp(up, UART_LCR, cval); /* reset DLAB */ + up->lcr = cval; /* Save LCR */ + if (up->port.type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(up, UART_FCR, fcr); /* set fcr */ + } + + up->cflag = cflag; + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +sunsu_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud, quot; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + sunsu_change_speed(port, termios->c_cflag, termios->c_iflag, quot); +} + +static void sunsu_release_port(struct uart_port *port) +{ +} + +static int sunsu_request_port(struct uart_port *port) +{ + return 0; +} + +static void sunsu_config_port(struct uart_port *port, int flags) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *) port; + + if (flags & UART_CONFIG_TYPE) { + /* + * We are supposed to call autoconfig here, but this requires + * splitting all the OBP probing crap from the UART probing. + * We'll do it when we kill sunsu.c altogether. + */ + port->type = up->type_probed; /* XXX */ + } +} + +static int +sunsu_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char * +sunsu_type(struct uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct uart_ops sunsu_pops = { + .tx_empty = sunsu_tx_empty, + .set_mctrl = sunsu_set_mctrl, + .get_mctrl = sunsu_get_mctrl, + .stop_tx = sunsu_stop_tx, + .start_tx = sunsu_start_tx, + .stop_rx = sunsu_stop_rx, + .enable_ms = sunsu_enable_ms, + .break_ctl = sunsu_break_ctl, + .startup = sunsu_startup, + .shutdown = sunsu_shutdown, + .set_termios = sunsu_set_termios, + .type = sunsu_type, + .release_port = sunsu_release_port, + .request_port = sunsu_request_port, + .config_port = sunsu_config_port, + .verify_port = sunsu_verify_port, +}; + +#define UART_NR 4 + +static struct uart_sunsu_port sunsu_ports[UART_NR]; + +#ifdef CONFIG_SERIO + +static DEFINE_SPINLOCK(sunsu_serio_lock); + +static int sunsu_serio_write(struct serio *serio, unsigned char ch) +{ + struct uart_sunsu_port *up = serio->port_data; + unsigned long flags; + int lsr; + + spin_lock_irqsave(&sunsu_serio_lock, flags); + + do { + lsr = serial_in(up, UART_LSR); + } while (!(lsr & UART_LSR_THRE)); + + /* Send the character out. */ + serial_out(up, UART_TX, ch); + + spin_unlock_irqrestore(&sunsu_serio_lock, flags); + + return 0; +} + +static int sunsu_serio_open(struct serio *serio) +{ + struct uart_sunsu_port *up = serio->port_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&sunsu_serio_lock, flags); + if (!up->serio_open) { + up->serio_open = 1; + ret = 0; + } else + ret = -EBUSY; + spin_unlock_irqrestore(&sunsu_serio_lock, flags); + + return ret; +} + +static void sunsu_serio_close(struct serio *serio) +{ + struct uart_sunsu_port *up = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&sunsu_serio_lock, flags); + up->serio_open = 0; + spin_unlock_irqrestore(&sunsu_serio_lock, flags); +} + +#endif /* CONFIG_SERIO */ + +static void sunsu_autoconfig(struct uart_sunsu_port *up) +{ + unsigned char status1, status2, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + if (up->su_type == SU_PORT_NONE) + return; + + up->type_probed = PORT_UNKNOWN; + up->port.iotype = UPIO_MEM; + + spin_lock_irqsave(&up->port.lock, flags); + + if (!(up->port.flags & UPF_BUGGY_UART)) { + /* + * Do a simple existence test first; if we fail this, there's + * no point trying anything else. + * + * 0x80 is used as a nonsense port to prevent against false + * positives due to ISA bus float. The assumption is that + * 0x80 is a non-existent port; which should be safe since + * include/asm/io.h also makes this assumption. + */ + scratch = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, 0x0f); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(up, UART_IER); + serial_outp(up, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) + goto out; /* We failed; there's nothing here */ + } + + save_mcr = serial_in(up, UART_MCR); + save_lcr = serial_in(up, UART_LCR); + + /* + * Check to see if a UART is really there. Certain broken + * internal modems based on the Rockwell chipset fail this + * test, because they apparently don't implement the loopback + * test mode. So this test is skipped on the COM 1 through + * COM 4 ports. This *should* be safe, since no board + * manufacturer would be stupid enough to design a board + * that conflicts with COM 1-4 --- we hope! + */ + if (!(up->port.flags & UPF_SKIP_TEST)) { + serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(up, UART_MSR) & 0xF0; + serial_outp(up, UART_MCR, save_mcr); + if (status1 != 0x90) + goto out; /* We failed loopback test */ + } + serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */ + serial_outp(up, UART_EFR, 0); /* EFR is the same as FCR */ + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(up, UART_IIR) >> 6; + switch (scratch) { + case 0: + up->port.type = PORT_16450; + break; + case 1: + up->port.type = PORT_UNKNOWN; + break; + case 2: + up->port.type = PORT_16550; + break; + case 3: + up->port.type = PORT_16550A; + break; + } + if (up->port.type == PORT_16550A) { + /* Check for Startech UART's */ + serial_outp(up, UART_LCR, UART_LCR_DLAB); + if (serial_in(up, UART_EFR) == 0) { + up->port.type = PORT_16650; + } else { + serial_outp(up, UART_LCR, 0xBF); + if (serial_in(up, UART_EFR) == 0) + up->port.type = PORT_16650V2; + } + } + if (up->port.type == PORT_16550A) { + /* Check for TI 16750 */ + serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(up, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(up, UART_IIR) >> 5; + if (scratch == 7) { + /* + * If this is a 16750, and not a cheap UART + * clone, then it should only go into 64 byte + * mode if the UART_FCR7_64BYTE bit was set + * while UART_LCR_DLAB was latched. + */ + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(up, UART_LCR, 0); + serial_outp(up, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(up, UART_IIR) >> 5; + if (scratch == 6) + up->port.type = PORT_16750; + } + serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(up, UART_LCR, save_lcr); + if (up->port.type == PORT_16450) { + scratch = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0xa5); + status1 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, 0x5a); + status2 = serial_in(up, UART_SCR); + serial_outp(up, UART_SCR, scratch); + + if ((status1 != 0xa5) || (status2 != 0x5a)) + up->port.type = PORT_8250; + } + + up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + + if (up->port.type == PORT_UNKNOWN) + goto out; + up->type_probed = up->port.type; /* XXX */ + + /* + * Reset the UART. + */ +#ifdef CONFIG_SERIAL_8250_RSA + if (up->port.type == PORT_RSA) + serial_outp(up, UART_RSA_FRR, 0); +#endif + serial_outp(up, UART_MCR, save_mcr); + serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(up, UART_FCR, 0); + (void)serial_in(up, UART_RX); + serial_outp(up, UART_IER, 0); + +out: + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver sunsu_reg = { + .owner = THIS_MODULE, + .driver_name = "sunsu", + .dev_name = "ttyS", + .major = TTY_MAJOR, +}; + +static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up) +{ + int quot, baud; +#ifdef CONFIG_SERIO + struct serio *serio; +#endif + + if (up->su_type == SU_PORT_KBD) { + up->cflag = B1200 | CS8 | CLOCAL | CREAD; + baud = 1200; + } else { + up->cflag = B4800 | CS8 | CLOCAL | CREAD; + baud = 4800; + } + quot = up->port.uartclk / (16 * baud); + + sunsu_autoconfig(up); + if (up->port.type == PORT_UNKNOWN) + return -ENODEV; + + printk("%s: %s port at %llx, irq %u\n", + up->port.dev->of_node->full_name, + (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse", + (unsigned long long) up->port.mapbase, + up->port.irq); + +#ifdef CONFIG_SERIO + serio = &up->serio; + serio->port_data = up; + + serio->id.type = SERIO_RS232; + if (up->su_type == SU_PORT_KBD) { + serio->id.proto = SERIO_SUNKBD; + strlcpy(serio->name, "sukbd", sizeof(serio->name)); + } else { + serio->id.proto = SERIO_SUN; + serio->id.extra = 1; + strlcpy(serio->name, "sums", sizeof(serio->name)); + } + strlcpy(serio->phys, + (!(up->port.line & 1) ? "su/serio0" : "su/serio1"), + sizeof(serio->phys)); + + serio->write = sunsu_serio_write; + serio->open = sunsu_serio_open; + serio->close = sunsu_serio_close; + serio->dev.parent = up->port.dev; + + serio_register_port(serio); +#endif + + sunsu_change_speed(&up->port, up->cflag, 0, quot); + + sunsu_startup(&up->port); + return 0; +} + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ + +#ifdef CONFIG_SERIAL_SUNSU_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* + * Wait for transmitter & holding register to empty + */ +static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +static void sunsu_console_putchar(struct uart_port *port, int ch) +{ + struct uart_sunsu_port *up = (struct uart_sunsu_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +static void sunsu_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_sunsu_port *up = &sunsu_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + /* + * First save the UER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, sunsu_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +/* + * Setup initial baud/bits/parity. We do two things here: + * - construct a cflag setting for the first su_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init sunsu_console_setup(struct console *co, char *options) +{ + static struct ktermios dummy; + struct ktermios termios; + struct uart_port *port; + + printk("Console: ttyS%d (SU)\n", + (sunsu_reg.minor - 64) + co->index); + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + port = &sunsu_ports[co->index].port; + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + /* Get firmware console settings. */ + sunserial_console_termios(co, port->dev->of_node); + + memset(&termios, 0, sizeof(struct ktermios)); + termios.c_cflag = co->cflag; + port->mctrl |= TIOCM_DTR; + port->ops->set_termios(port, &termios, &dummy); + + return 0; +} + +static struct console sunsu_console = { + .name = "ttyS", + .write = sunsu_console_write, + .device = uart_console_device, + .setup = sunsu_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sunsu_reg, +}; + +/* + * Register console. + */ + +static inline struct console *SUNSU_CONSOLE(void) +{ + return &sunsu_console; +} +#else +#define SUNSU_CONSOLE() (NULL) +#define sunsu_serial_console_init() do { } while (0) +#endif + +static enum su_type __devinit su_get_type(struct device_node *dp) +{ + struct device_node *ap = of_find_node_by_path("/aliases"); + + if (ap) { + const char *keyb = of_get_property(ap, "keyboard", NULL); + const char *ms = of_get_property(ap, "mouse", NULL); + + if (keyb) { + if (dp == of_find_node_by_path(keyb)) + return SU_PORT_KBD; + } + if (ms) { + if (dp == of_find_node_by_path(ms)) + return SU_PORT_MS; + } + } + + return SU_PORT_PORT; +} + +static int __devinit su_probe(struct platform_device *op, const struct of_device_id *match) +{ + static int inst; + struct device_node *dp = op->dev.of_node; + struct uart_sunsu_port *up; + struct resource *rp; + enum su_type type; + bool ignore_line; + int err; + + type = su_get_type(dp); + if (type == SU_PORT_PORT) { + if (inst >= UART_NR) + return -EINVAL; + up = &sunsu_ports[inst]; + } else { + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (!up) + return -ENOMEM; + } + + up->port.line = inst; + + spin_lock_init(&up->port.lock); + + up->su_type = type; + + rp = &op->resource[0]; + up->port.mapbase = rp->start; + up->reg_size = (rp->end - rp->start) + 1; + up->port.membase = of_ioremap(rp, 0, up->reg_size, "su"); + if (!up->port.membase) { + if (type != SU_PORT_PORT) + kfree(up); + return -ENOMEM; + } + + up->port.irq = op->archdata.irqs[0]; + + up->port.dev = &op->dev; + + up->port.type = PORT_UNKNOWN; + up->port.uartclk = (SU_BASE_BAUD * 16); + + err = 0; + if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) { + err = sunsu_kbd_ms_init(up); + if (err) { + of_iounmap(&op->resource[0], + up->port.membase, up->reg_size); + kfree(up); + return err; + } + dev_set_drvdata(&op->dev, up); + + return 0; + } + + up->port.flags |= UPF_BOOT_AUTOCONF; + + sunsu_autoconfig(up); + + err = -ENODEV; + if (up->port.type == PORT_UNKNOWN) + goto out_unmap; + + up->port.ops = &sunsu_pops; + + ignore_line = false; + if (!strcmp(dp->name, "rsc-console") || + !strcmp(dp->name, "lom-console")) + ignore_line = true; + + sunserial_console_match(SUNSU_CONSOLE(), dp, + &sunsu_reg, up->port.line, + ignore_line); + err = uart_add_one_port(&sunsu_reg, &up->port); + if (err) + goto out_unmap; + + dev_set_drvdata(&op->dev, up); + + inst++; + + return 0; + +out_unmap: + of_iounmap(&op->resource[0], up->port.membase, up->reg_size); + return err; +} + +static int __devexit su_remove(struct platform_device *op) +{ + struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); + bool kbdms = false; + + if (up->su_type == SU_PORT_MS || + up->su_type == SU_PORT_KBD) + kbdms = true; + + if (kbdms) { +#ifdef CONFIG_SERIO + serio_unregister_port(&up->serio); +#endif + } else if (up->port.type != PORT_UNKNOWN) + uart_remove_one_port(&sunsu_reg, &up->port); + + if (up->port.membase) + of_iounmap(&op->resource[0], up->port.membase, up->reg_size); + + if (kbdms) + kfree(up); + + dev_set_drvdata(&op->dev, NULL); + + return 0; +} + +static const struct of_device_id su_match[] = { + { + .name = "su", + }, + { + .name = "su_pnp", + }, + { + .name = "serial", + .compatible = "su", + }, + { + .type = "serial", + .compatible = "su", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, su_match); + +static struct of_platform_driver su_driver = { + .driver = { + .name = "su", + .owner = THIS_MODULE, + .of_match_table = su_match, + }, + .probe = su_probe, + .remove = __devexit_p(su_remove), +}; + +static int __init sunsu_init(void) +{ + struct device_node *dp; + int err; + int num_uart = 0; + + for_each_node_by_name(dp, "su") { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + for_each_node_by_name(dp, "su_pnp") { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + for_each_node_by_name(dp, "serial") { + if (of_device_is_compatible(dp, "su")) { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + } + for_each_node_by_type(dp, "serial") { + if (of_device_is_compatible(dp, "su")) { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + } + + if (num_uart) { + err = sunserial_register_minors(&sunsu_reg, num_uart); + if (err) + return err; + } + + err = of_register_platform_driver(&su_driver); + if (err && num_uart) + sunserial_unregister_minors(&sunsu_reg, num_uart); + + return err; +} + +static void __exit sunsu_exit(void) +{ + if (sunsu_reg.nr) + sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr); +} + +module_init(sunsu_init); +module_exit(sunsu_exit); + +MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller"); +MODULE_DESCRIPTION("Sun SU serial port driver"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c new file mode 100644 index 000000000000..c1967ac1c07f --- /dev/null +++ b/drivers/tty/serial/sunzilog.c @@ -0,0 +1,1655 @@ +/* sunzilog.c: Zilog serial driver for Sparc systems. + * + * Driver for Zilog serial chips found on Sun workstations and + * servers. This driver could actually be made more generic. + * + * This is based on the old drivers/sbus/char/zs.c code. A lot + * of code has been simply moved over directly from there but + * much has been rewritten. Credits therefore go out to Eddie + * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their + * work there. + * + * Copyright (C) 2002, 2006, 2007 David S. Miller (davem@davemloft.net) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SERIO +#include +#endif +#include +#include + +#include +#include +#include + +#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include + +#include "suncore.h" +#include "sunzilog.h" + +/* On 32-bit sparcs we need to delay after register accesses + * to accommodate sun4 systems, but we do not need to flush writes. + * On 64-bit sparc we only need to flush single writes to ensure + * completion. + */ +#ifndef CONFIG_SPARC64 +#define ZSDELAY() udelay(5) +#define ZSDELAY_LONG() udelay(20) +#define ZS_WSYNC(channel) do { } while (0) +#else +#define ZSDELAY() +#define ZSDELAY_LONG() +#define ZS_WSYNC(__channel) \ + readb(&((__channel)->control)) +#endif + +#define ZS_CLOCK 4915200 /* Zilog input clock rate. */ +#define ZS_CLOCK_DIVISOR 16 /* Divisor this driver uses. */ + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_sunzilog_port { + struct uart_port port; + + /* IRQ servicing chain. */ + struct uart_sunzilog_port *next; + + /* Current values of Zilog write registers. */ + unsigned char curregs[NUM_ZSREGS]; + + unsigned int flags; +#define SUNZILOG_FLAG_CONS_KEYB 0x00000001 +#define SUNZILOG_FLAG_CONS_MOUSE 0x00000002 +#define SUNZILOG_FLAG_IS_CONS 0x00000004 +#define SUNZILOG_FLAG_IS_KGDB 0x00000008 +#define SUNZILOG_FLAG_MODEM_STATUS 0x00000010 +#define SUNZILOG_FLAG_IS_CHANNEL_A 0x00000020 +#define SUNZILOG_FLAG_REGS_HELD 0x00000040 +#define SUNZILOG_FLAG_TX_STOPPED 0x00000080 +#define SUNZILOG_FLAG_TX_ACTIVE 0x00000100 +#define SUNZILOG_FLAG_ESCC 0x00000200 +#define SUNZILOG_FLAG_ISR_HANDLER 0x00000400 + + unsigned int cflag; + + unsigned char parity_mask; + unsigned char prev_status; + +#ifdef CONFIG_SERIO + struct serio serio; + int serio_open; +#endif +}; + +static void sunzilog_putchar(struct uart_port *port, int ch); + +#define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase)) +#define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) + +#define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB) +#define ZS_IS_MOUSE(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE) +#define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS) +#define ZS_IS_KGDB(UP) ((UP)->flags & SUNZILOG_FLAG_IS_KGDB) +#define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & SUNZILOG_FLAG_MODEM_STATUS) +#define ZS_IS_CHANNEL_A(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CHANNEL_A) +#define ZS_REGS_HELD(UP) ((UP)->flags & SUNZILOG_FLAG_REGS_HELD) +#define ZS_TX_STOPPED(UP) ((UP)->flags & SUNZILOG_FLAG_TX_STOPPED) +#define ZS_TX_ACTIVE(UP) ((UP)->flags & SUNZILOG_FLAG_TX_ACTIVE) + +/* Reading and writing Zilog8530 registers. The delays are to make this + * driver work on the Sun4 which needs a settling delay after each chip + * register access, other machines handle this in hardware via auxiliary + * flip-flops which implement the settle time we do in software. + * + * The port lock must be held and local IRQs must be disabled + * when {read,write}_zsreg is invoked. + */ +static unsigned char read_zsreg(struct zilog_channel __iomem *channel, + unsigned char reg) +{ + unsigned char retval; + + writeb(reg, &channel->control); + ZSDELAY(); + retval = readb(&channel->control); + ZSDELAY(); + + return retval; +} + +static void write_zsreg(struct zilog_channel __iomem *channel, + unsigned char reg, unsigned char value) +{ + writeb(reg, &channel->control); + ZSDELAY(); + writeb(value, &channel->control); + ZSDELAY(); +} + +static void sunzilog_clear_fifo(struct zilog_channel __iomem *channel) +{ + int i; + + for (i = 0; i < 32; i++) { + unsigned char regval; + + regval = readb(&channel->control); + ZSDELAY(); + if (regval & Rx_CH_AV) + break; + + regval = read_zsreg(channel, R1); + readb(&channel->data); + ZSDELAY(); + + if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + } +} + +/* This function must only be called when the TX is not busy. The UART + * port lock must be held and local interrupts disabled. + */ +static int __load_zsregs(struct zilog_channel __iomem *channel, unsigned char *regs) +{ + int i; + int escc; + unsigned char r15; + + /* Let pending transmits finish. */ + for (i = 0; i < 1000; i++) { + unsigned char stat = read_zsreg(channel, R1); + if (stat & ALL_SNT) + break; + udelay(100); + } + + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + sunzilog_clear_fifo(channel); + + /* Disable all interrupts. */ + write_zsreg(channel, R1, + regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB)); + + /* Set parity, sync config, stop bits, and clock divisor. */ + write_zsreg(channel, R4, regs[R4]); + + /* Set misc. TX/RX control bits. */ + write_zsreg(channel, R10, regs[R10]); + + /* Set TX/RX controls sans the enable bits. */ + write_zsreg(channel, R3, regs[R3] & ~RxENAB); + write_zsreg(channel, R5, regs[R5] & ~TxENAB); + + /* Synchronous mode config. */ + write_zsreg(channel, R6, regs[R6]); + write_zsreg(channel, R7, regs[R7]); + + /* Don't mess with the interrupt vector (R2, unused by us) and + * master interrupt control (R9). We make sure this is setup + * properly at probe time then never touch it again. + */ + + /* Disable baud generator. */ + write_zsreg(channel, R14, regs[R14] & ~BRENAB); + + /* Clock mode control. */ + write_zsreg(channel, R11, regs[R11]); + + /* Lower and upper byte of baud rate generator divisor. */ + write_zsreg(channel, R12, regs[R12]); + write_zsreg(channel, R13, regs[R13]); + + /* Now rewrite R14, with BRENAB (if set). */ + write_zsreg(channel, R14, regs[R14]); + + /* External status interrupt control. */ + write_zsreg(channel, R15, (regs[R15] | WR7pEN) & ~FIFOEN); + + /* ESCC Extension Register */ + r15 = read_zsreg(channel, R15); + if (r15 & 0x01) { + write_zsreg(channel, R7, regs[R7p]); + + /* External status interrupt and FIFO control. */ + write_zsreg(channel, R15, regs[R15] & ~WR7pEN); + escc = 1; + } else { + /* Clear FIFO bit case it is an issue */ + regs[R15] &= ~FIFOEN; + escc = 0; + } + + /* Reset external status interrupts. */ + write_zsreg(channel, R0, RES_EXT_INT); /* First Latch */ + write_zsreg(channel, R0, RES_EXT_INT); /* Second Latch */ + + /* Rewrite R3/R5, this time without enables masked. */ + write_zsreg(channel, R3, regs[R3]); + write_zsreg(channel, R5, regs[R5]); + + /* Rewrite R1, this time without IRQ enabled masked. */ + write_zsreg(channel, R1, regs[R1]); + + return escc; +} + +/* Reprogram the Zilog channel HW registers with the copies found in the + * software state struct. If the transmitter is busy, we defer this update + * until the next TX complete interrupt. Else, we do it right now. + * + * The UART port lock must be held and local interrupts disabled. + */ +static void sunzilog_maybe_update_regs(struct uart_sunzilog_port *up, + struct zilog_channel __iomem *channel) +{ + if (!ZS_REGS_HELD(up)) { + if (ZS_TX_ACTIVE(up)) { + up->flags |= SUNZILOG_FLAG_REGS_HELD; + } else { + __load_zsregs(channel, up->curregs); + } + } +} + +static void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up) +{ + unsigned int cur_cflag = up->cflag; + int brg, new_baud; + + up->cflag &= ~CBAUD; + up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud); + + brg = BPS_TO_BRG(new_baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + up->curregs[R12] = (brg & 0xff); + up->curregs[R13] = (brg >> 8) & 0xff; + sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port)); +} + +static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up, + unsigned char ch, int is_break) +{ + if (ZS_IS_KEYB(up)) { + /* Stop-A is handled by drivers/char/keyboard.c now. */ +#ifdef CONFIG_SERIO + if (up->serio_open) + serio_interrupt(&up->serio, ch, 0); +#endif + } else if (ZS_IS_MOUSE(up)) { + int ret = suncore_mouse_baud_detection(ch, is_break); + + switch (ret) { + case 2: + sunzilog_change_mouse_baud(up); + /* fallthru */ + case 1: + break; + + case 0: +#ifdef CONFIG_SERIO + if (up->serio_open) + serio_interrupt(&up->serio, ch, 0); +#endif + break; + }; + } +} + +static struct tty_struct * +sunzilog_receive_chars(struct uart_sunzilog_port *up, + struct zilog_channel __iomem *channel) +{ + struct tty_struct *tty; + unsigned char ch, r1, flag; + + tty = NULL; + if (up->port.state != NULL && /* Unopened serial console */ + up->port.state->port.tty != NULL) /* Keyboard || mouse */ + tty = up->port.state->port.tty; + + for (;;) { + + r1 = read_zsreg(channel, R1); + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + + ch = readb(&channel->control); + ZSDELAY(); + + /* This funny hack depends upon BRK_ABRT not interfering + * with the other bits we care about in R1. + */ + if (ch & BRK_ABRT) + r1 |= BRK_ABRT; + + if (!(ch & Rx_CH_AV)) + break; + + ch = readb(&channel->data); + ZSDELAY(); + + ch &= up->parity_mask; + + if (unlikely(ZS_IS_KEYB(up)) || unlikely(ZS_IS_MOUSE(up))) { + sunzilog_kbdms_receive_chars(up, ch, 0); + continue; + } + + if (tty == NULL) { + uart_handle_sysrq_char(&up->port, ch); + continue; + } + + /* A real serial line, record the character and status. */ + flag = TTY_NORMAL; + up->port.icount.rx++; + if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) { + if (r1 & BRK_ABRT) { + r1 &= ~(PAR_ERR | CRC_ERR); + up->port.icount.brk++; + if (uart_handle_break(&up->port)) + continue; + } + else if (r1 & PAR_ERR) + up->port.icount.parity++; + else if (r1 & CRC_ERR) + up->port.icount.frame++; + if (r1 & Rx_OVR) + up->port.icount.overrun++; + r1 &= up->port.read_status_mask; + if (r1 & BRK_ABRT) + flag = TTY_BREAK; + else if (r1 & PAR_ERR) + flag = TTY_PARITY; + else if (r1 & CRC_ERR) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch)) + continue; + + if (up->port.ignore_status_mask == 0xff || + (r1 & up->port.ignore_status_mask) == 0) { + tty_insert_flip_char(tty, ch, flag); + } + if (r1 & Rx_OVR) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + return tty; +} + +static void sunzilog_status_handle(struct uart_sunzilog_port *up, + struct zilog_channel __iomem *channel) +{ + unsigned char status; + + status = readb(&channel->control); + ZSDELAY(); + + writeb(RES_EXT_INT, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + if (status & BRK_ABRT) { + if (ZS_IS_MOUSE(up)) + sunzilog_kbdms_receive_chars(up, 0, 1); + if (ZS_IS_CONS(up)) { + /* Wait for BREAK to deassert to avoid potentially + * confusing the PROM. + */ + while (1) { + status = readb(&channel->control); + ZSDELAY(); + if (!(status & BRK_ABRT)) + break; + } + sun_do_break(); + return; + } + } + + if (ZS_WANTS_MODEM_STATUS(up)) { + if (status & SYNC) + up->port.icount.dsr++; + + /* The Zilog just gives us an interrupt when DCD/CTS/etc. change. + * But it does not tell us which bit has changed, we have to keep + * track of this ourselves. + */ + if ((status ^ up->prev_status) ^ DCD) + uart_handle_dcd_change(&up->port, + (status & DCD)); + if ((status ^ up->prev_status) ^ CTS) + uart_handle_cts_change(&up->port, + (status & CTS)); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + up->prev_status = status; +} + +static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, + struct zilog_channel __iomem *channel) +{ + struct circ_buf *xmit; + + if (ZS_IS_CONS(up)) { + unsigned char status = readb(&channel->control); + ZSDELAY(); + + /* TX still busy? Just wait for the next TX done interrupt. + * + * It can occur because of how we do serial console writes. It would + * be nice to transmit console writes just like we normally would for + * a TTY line. (ie. buffered and TX interrupt driven). That is not + * easy because console writes cannot sleep. One solution might be + * to poll on enough port->xmit space becomming free. -DaveM + */ + if (!(status & Tx_BUF_EMP)) + return; + } + + up->flags &= ~SUNZILOG_FLAG_TX_ACTIVE; + + if (ZS_REGS_HELD(up)) { + __load_zsregs(channel, up->curregs); + up->flags &= ~SUNZILOG_FLAG_REGS_HELD; + } + + if (ZS_TX_STOPPED(up)) { + up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; + goto ack_tx_int; + } + + if (up->port.x_char) { + up->flags |= SUNZILOG_FLAG_TX_ACTIVE; + writeb(up->port.x_char, &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + + if (up->port.state == NULL) + goto ack_tx_int; + xmit = &up->port.state->xmit; + if (uart_circ_empty(xmit)) + goto ack_tx_int; + + if (uart_tx_stopped(&up->port)) + goto ack_tx_int; + + up->flags |= SUNZILOG_FLAG_TX_ACTIVE; + writeb(xmit->buf[xmit->tail], &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + return; + +ack_tx_int: + writeb(RES_Tx_P, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); +} + +static irqreturn_t sunzilog_interrupt(int irq, void *dev_id) +{ + struct uart_sunzilog_port *up = dev_id; + + while (up) { + struct zilog_channel __iomem *channel + = ZILOG_CHANNEL_FROM_PORT(&up->port); + struct tty_struct *tty; + unsigned char r3; + + spin_lock(&up->port.lock); + r3 = read_zsreg(channel, R3); + + /* Channel A */ + tty = NULL; + if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { + writeb(RES_H_IUS, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + if (r3 & CHARxIP) + tty = sunzilog_receive_chars(up, channel); + if (r3 & CHAEXT) + sunzilog_status_handle(up, channel); + if (r3 & CHATxIP) + sunzilog_transmit_chars(up, channel); + } + spin_unlock(&up->port.lock); + + if (tty) + tty_flip_buffer_push(tty); + + /* Channel B */ + up = up->next; + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + + spin_lock(&up->port.lock); + tty = NULL; + if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { + writeb(RES_H_IUS, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + + if (r3 & CHBRxIP) + tty = sunzilog_receive_chars(up, channel); + if (r3 & CHBEXT) + sunzilog_status_handle(up, channel); + if (r3 & CHBTxIP) + sunzilog_transmit_chars(up, channel); + } + spin_unlock(&up->port.lock); + + if (tty) + tty_flip_buffer_push(tty); + + up = up->next; + } + + return IRQ_HANDLED; +} + +/* A convenient way to quickly get R0 status. The caller must _not_ hold the + * port lock, it is acquired here. + */ +static __inline__ unsigned char sunzilog_read_channel_status(struct uart_port *port) +{ + struct zilog_channel __iomem *channel; + unsigned char status; + + channel = ZILOG_CHANNEL_FROM_PORT(port); + status = readb(&channel->control); + ZSDELAY(); + + return status; +} + +/* The port lock is not held. */ +static unsigned int sunzilog_tx_empty(struct uart_port *port) +{ + unsigned long flags; + unsigned char status; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + + status = sunzilog_read_channel_status(port); + + spin_unlock_irqrestore(&port->lock, flags); + + if (status & Tx_BUF_EMP) + ret = TIOCSER_TEMT; + else + ret = 0; + + return ret; +} + +/* The port lock is held and interrupts are disabled. */ +static unsigned int sunzilog_get_mctrl(struct uart_port *port) +{ + unsigned char status; + unsigned int ret; + + status = sunzilog_read_channel_status(port); + + ret = 0; + if (status & DCD) + ret |= TIOCM_CAR; + if (status & SYNC) + ret |= TIOCM_DSR; + if (status & CTS) + ret |= TIOCM_CTS; + + return ret; +} + +/* The port lock is held and interrupts are disabled. */ +static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char set_bits, clear_bits; + + set_bits = clear_bits = 0; + + if (mctrl & TIOCM_RTS) + set_bits |= RTS; + else + clear_bits |= RTS; + if (mctrl & TIOCM_DTR) + set_bits |= DTR; + else + clear_bits |= DTR; + + /* NOTE: Not subject to 'transmitter active' rule. */ + up->curregs[R5] |= set_bits; + up->curregs[R5] &= ~clear_bits; + write_zsreg(channel, R5, up->curregs[R5]); +} + +/* The port lock is held and interrupts are disabled. */ +static void sunzilog_stop_tx(struct uart_port *port) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + + up->flags |= SUNZILOG_FLAG_TX_STOPPED; +} + +/* The port lock is held and interrupts are disabled. */ +static void sunzilog_start_tx(struct uart_port *port) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char status; + + up->flags |= SUNZILOG_FLAG_TX_ACTIVE; + up->flags &= ~SUNZILOG_FLAG_TX_STOPPED; + + status = readb(&channel->control); + ZSDELAY(); + + /* TX busy? Just wait for the TX done interrupt. */ + if (!(status & Tx_BUF_EMP)) + return; + + /* Send the first character to jump-start the TX done + * IRQ sending engine. + */ + if (port->x_char) { + writeb(port->x_char, &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + port->icount.tx++; + port->x_char = 0; + } else { + struct circ_buf *xmit = &port->state->xmit; + + writeb(xmit->buf[xmit->tail], &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + } +} + +/* The port lock is held. */ +static void sunzilog_stop_rx(struct uart_port *port) +{ + struct uart_sunzilog_port *up = UART_ZILOG(port); + struct zilog_channel __iomem *channel; + + if (ZS_IS_CONS(up)) + return; + + channel = ZILOG_CHANNEL_FROM_PORT(port); + + /* Disable all RX interrupts. */ + up->curregs[R1] &= ~RxINT_MASK; + sunzilog_maybe_update_regs(up, channel); +} + +/* The port lock is held. */ +static void sunzilog_enable_ms(struct uart_port *port) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char new_reg; + + new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE); + if (new_reg != up->curregs[R15]) { + up->curregs[R15] = new_reg; + + /* NOTE: Not subject to 'transmitter active' rule. */ + write_zsreg(channel, R15, up->curregs[R15] & ~WR7pEN); + } +} + +/* The port lock is not held. */ +static void sunzilog_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); + unsigned char set_bits, clear_bits, new_reg; + unsigned long flags; + + set_bits = clear_bits = 0; + + if (break_state) + set_bits |= SND_BRK; + else + clear_bits |= SND_BRK; + + spin_lock_irqsave(&port->lock, flags); + + new_reg = (up->curregs[R5] | set_bits) & ~clear_bits; + if (new_reg != up->curregs[R5]) { + up->curregs[R5] = new_reg; + + /* NOTE: Not subject to 'transmitter active' rule. */ + write_zsreg(channel, R5, up->curregs[R5]); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __sunzilog_startup(struct uart_sunzilog_port *up) +{ + struct zilog_channel __iomem *channel; + + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + up->prev_status = readb(&channel->control); + + /* Enable receiver and transmitter. */ + up->curregs[R3] |= RxENAB; + up->curregs[R5] |= TxENAB; + + up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; + sunzilog_maybe_update_regs(up, channel); +} + +static int sunzilog_startup(struct uart_port *port) +{ + struct uart_sunzilog_port *up = UART_ZILOG(port); + unsigned long flags; + + if (ZS_IS_CONS(up)) + return 0; + + spin_lock_irqsave(&port->lock, flags); + __sunzilog_startup(up); + spin_unlock_irqrestore(&port->lock, flags); + return 0; +} + +/* + * The test for ZS_IS_CONS is explained by the following e-mail: + ***** + * From: Russell King + * Date: Sun, 8 Dec 2002 10:18:38 +0000 + * + * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote: + * > I boot my 2.5 boxes using "console=ttyS0,9600" argument, + * > and I noticed that something is not right with reference + * > counting in this case. It seems that when the console + * > is open by kernel initially, this is not accounted + * > as an open, and uart_startup is not called. + * + * That is correct. We are unable to call uart_startup when the serial + * console is initialised because it may need to allocate memory (as + * request_irq does) and the memory allocators may not have been + * initialised. + * + * 1. initialise the port into a state where it can send characters in the + * console write method. + * + * 2. don't do the actual hardware shutdown in your shutdown() method (but + * do the normal software shutdown - ie, free irqs etc) + ***** + */ +static void sunzilog_shutdown(struct uart_port *port) +{ + struct uart_sunzilog_port *up = UART_ZILOG(port); + struct zilog_channel __iomem *channel; + unsigned long flags; + + if (ZS_IS_CONS(up)) + return; + + spin_lock_irqsave(&port->lock, flags); + + channel = ZILOG_CHANNEL_FROM_PORT(port); + + /* Disable receiver and transmitter. */ + up->curregs[R3] &= ~RxENAB; + up->curregs[R5] &= ~TxENAB; + + /* Disable all interrupts and BRK assertion. */ + up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); + up->curregs[R5] &= ~SND_BRK; + sunzilog_maybe_update_regs(up, channel); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Shared by TTY driver and serial console setup. The port lock is held + * and local interrupts are disabled. + */ +static void +sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag, + unsigned int iflag, int brg) +{ + + up->curregs[R10] = NRZ; + up->curregs[R11] = TCBR | RCBR; + + /* Program BAUD and clock source. */ + up->curregs[R4] &= ~XCLK_MASK; + up->curregs[R4] |= X16CLK; + up->curregs[R12] = brg & 0xff; + up->curregs[R13] = (brg >> 8) & 0xff; + up->curregs[R14] = BRSRC | BRENAB; + + /* Character size, stop bits, and parity. */ + up->curregs[R3] &= ~RxN_MASK; + up->curregs[R5] &= ~TxN_MASK; + switch (cflag & CSIZE) { + case CS5: + up->curregs[R3] |= Rx5; + up->curregs[R5] |= Tx5; + up->parity_mask = 0x1f; + break; + case CS6: + up->curregs[R3] |= Rx6; + up->curregs[R5] |= Tx6; + up->parity_mask = 0x3f; + break; + case CS7: + up->curregs[R3] |= Rx7; + up->curregs[R5] |= Tx7; + up->parity_mask = 0x7f; + break; + case CS8: + default: + up->curregs[R3] |= Rx8; + up->curregs[R5] |= Tx8; + up->parity_mask = 0xff; + break; + }; + up->curregs[R4] &= ~0x0c; + if (cflag & CSTOPB) + up->curregs[R4] |= SB2; + else + up->curregs[R4] |= SB1; + if (cflag & PARENB) + up->curregs[R4] |= PAR_ENAB; + else + up->curregs[R4] &= ~PAR_ENAB; + if (!(cflag & PARODD)) + up->curregs[R4] |= PAR_EVEN; + else + up->curregs[R4] &= ~PAR_EVEN; + + up->port.read_status_mask = Rx_OVR; + if (iflag & INPCK) + up->port.read_status_mask |= CRC_ERR | PAR_ERR; + if (iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= BRK_ABRT; + + up->port.ignore_status_mask = 0; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= CRC_ERR | PAR_ERR; + if (iflag & IGNBRK) { + up->port.ignore_status_mask |= BRK_ABRT; + if (iflag & IGNPAR) + up->port.ignore_status_mask |= Rx_OVR; + } + + if ((cflag & CREAD) == 0) + up->port.ignore_status_mask = 0xff; +} + +/* The port lock is not held. */ +static void +sunzilog_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + unsigned long flags; + int baud, brg; + + baud = uart_get_baud_rate(port, termios, old, 1200, 76800); + + spin_lock_irqsave(&up->port.lock, flags); + + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + + sunzilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg); + + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->flags |= SUNZILOG_FLAG_MODEM_STATUS; + else + up->flags &= ~SUNZILOG_FLAG_MODEM_STATUS; + + up->cflag = termios->c_cflag; + + sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); + + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static const char *sunzilog_type(struct uart_port *port) +{ + struct uart_sunzilog_port *up = UART_ZILOG(port); + + return (up->flags & SUNZILOG_FLAG_ESCC) ? "zs (ESCC)" : "zs"; +} + +/* We do not request/release mappings of the registers here, this + * happens at early serial probe time. + */ +static void sunzilog_release_port(struct uart_port *port) +{ +} + +static int sunzilog_request_port(struct uart_port *port) +{ + return 0; +} + +/* These do not need to do anything interesting either. */ +static void sunzilog_config_port(struct uart_port *port, int flags) +{ +} + +/* We do not support letting the user mess with the divisor, IRQ, etc. */ +static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +#ifdef CONFIG_CONSOLE_POLL +static int sunzilog_get_poll_char(struct uart_port *port) +{ + unsigned char ch, r1; + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel + = ZILOG_CHANNEL_FROM_PORT(&up->port); + + + r1 = read_zsreg(channel, R1); + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + + ch = readb(&channel->control); + ZSDELAY(); + + /* This funny hack depends upon BRK_ABRT not interfering + * with the other bits we care about in R1. + */ + if (ch & BRK_ABRT) + r1 |= BRK_ABRT; + + if (!(ch & Rx_CH_AV)) + return NO_POLL_CHAR; + + ch = readb(&channel->data); + ZSDELAY(); + + ch &= up->parity_mask; + return ch; +} + +static void sunzilog_put_poll_char(struct uart_port *port, + unsigned char ch) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port; + + sunzilog_putchar(&up->port, ch); +} +#endif /* CONFIG_CONSOLE_POLL */ + +static struct uart_ops sunzilog_pops = { + .tx_empty = sunzilog_tx_empty, + .set_mctrl = sunzilog_set_mctrl, + .get_mctrl = sunzilog_get_mctrl, + .stop_tx = sunzilog_stop_tx, + .start_tx = sunzilog_start_tx, + .stop_rx = sunzilog_stop_rx, + .enable_ms = sunzilog_enable_ms, + .break_ctl = sunzilog_break_ctl, + .startup = sunzilog_startup, + .shutdown = sunzilog_shutdown, + .set_termios = sunzilog_set_termios, + .type = sunzilog_type, + .release_port = sunzilog_release_port, + .request_port = sunzilog_request_port, + .config_port = sunzilog_config_port, + .verify_port = sunzilog_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = sunzilog_get_poll_char, + .poll_put_char = sunzilog_put_poll_char, +#endif +}; + +static int uart_chip_count; +static struct uart_sunzilog_port *sunzilog_port_table; +static struct zilog_layout __iomem **sunzilog_chip_regs; + +static struct uart_sunzilog_port *sunzilog_irq_chain; + +static struct uart_driver sunzilog_reg = { + .owner = THIS_MODULE, + .driver_name = "sunzilog", + .dev_name = "ttyS", + .major = TTY_MAJOR, +}; + +static int __init sunzilog_alloc_tables(int num_sunzilog) +{ + struct uart_sunzilog_port *up; + unsigned long size; + int num_channels = num_sunzilog * 2; + int i; + + size = num_channels * sizeof(struct uart_sunzilog_port); + sunzilog_port_table = kzalloc(size, GFP_KERNEL); + if (!sunzilog_port_table) + return -ENOMEM; + + for (i = 0; i < num_channels; i++) { + up = &sunzilog_port_table[i]; + + spin_lock_init(&up->port.lock); + + if (i == 0) + sunzilog_irq_chain = up; + + if (i < num_channels - 1) + up->next = up + 1; + else + up->next = NULL; + } + + size = num_sunzilog * sizeof(struct zilog_layout __iomem *); + sunzilog_chip_regs = kzalloc(size, GFP_KERNEL); + if (!sunzilog_chip_regs) { + kfree(sunzilog_port_table); + sunzilog_irq_chain = NULL; + return -ENOMEM; + } + + return 0; +} + +static void sunzilog_free_tables(void) +{ + kfree(sunzilog_port_table); + sunzilog_irq_chain = NULL; + kfree(sunzilog_chip_regs); +} + +#define ZS_PUT_CHAR_MAX_DELAY 2000 /* 10 ms */ + +static void sunzilog_putchar(struct uart_port *port, int ch) +{ + struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port); + int loops = ZS_PUT_CHAR_MAX_DELAY; + + /* This is a timed polling loop so do not switch the explicit + * udelay with ZSDELAY as that is a NOP on some platforms. -DaveM + */ + do { + unsigned char val = readb(&channel->control); + if (val & Tx_BUF_EMP) { + ZSDELAY(); + break; + } + udelay(5); + } while (--loops); + + writeb(ch, &channel->data); + ZSDELAY(); + ZS_WSYNC(channel); +} + +#ifdef CONFIG_SERIO + +static DEFINE_SPINLOCK(sunzilog_serio_lock); + +static int sunzilog_serio_write(struct serio *serio, unsigned char ch) +{ + struct uart_sunzilog_port *up = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&sunzilog_serio_lock, flags); + + sunzilog_putchar(&up->port, ch); + + spin_unlock_irqrestore(&sunzilog_serio_lock, flags); + + return 0; +} + +static int sunzilog_serio_open(struct serio *serio) +{ + struct uart_sunzilog_port *up = serio->port_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&sunzilog_serio_lock, flags); + if (!up->serio_open) { + up->serio_open = 1; + ret = 0; + } else + ret = -EBUSY; + spin_unlock_irqrestore(&sunzilog_serio_lock, flags); + + return ret; +} + +static void sunzilog_serio_close(struct serio *serio) +{ + struct uart_sunzilog_port *up = serio->port_data; + unsigned long flags; + + spin_lock_irqsave(&sunzilog_serio_lock, flags); + up->serio_open = 0; + spin_unlock_irqrestore(&sunzilog_serio_lock, flags); +} + +#endif /* CONFIG_SERIO */ + +#ifdef CONFIG_SERIAL_SUNZILOG_CONSOLE +static void +sunzilog_console_write(struct console *con, const char *s, unsigned int count) +{ + struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; + unsigned long flags; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) { + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + uart_console_write(&up->port, s, count, sunzilog_putchar); + udelay(2); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init sunzilog_console_setup(struct console *con, char *options) +{ + struct uart_sunzilog_port *up = &sunzilog_port_table[con->index]; + unsigned long flags; + int baud, brg; + + if (up->port.type != PORT_SUNZILOG) + return -1; + + printk(KERN_INFO "Console: ttyS%d (SunZilog zs%d)\n", + (sunzilog_reg.minor - 64) + con->index, con->index); + + /* Get firmware console settings. */ + sunserial_console_termios(con, up->port.dev->of_node); + + /* Firmware console speed is limited to 150-->38400 baud so + * this hackish cflag thing is OK. + */ + switch (con->cflag & CBAUD) { + case B150: baud = 150; break; + case B300: baud = 300; break; + case B600: baud = 600; break; + case B1200: baud = 1200; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + default: case B9600: baud = 9600; break; + case B19200: baud = 19200; break; + case B38400: baud = 38400; break; + }; + + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + + spin_lock_irqsave(&up->port.lock, flags); + + up->curregs[R15] |= BRKIE; + sunzilog_convert_to_zs(up, con->cflag, 0, brg); + + sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); + __sunzilog_startup(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + + return 0; +} + +static struct console sunzilog_console_ops = { + .name = "ttyS", + .write = sunzilog_console_write, + .device = uart_console_device, + .setup = sunzilog_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sunzilog_reg, +}; + +static inline struct console *SUNZILOG_CONSOLE(void) +{ + return &sunzilog_console_ops; +} + +#else +#define SUNZILOG_CONSOLE() (NULL) +#endif + +static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up) +{ + int baud, brg; + + if (up->flags & SUNZILOG_FLAG_CONS_KEYB) { + up->cflag = B1200 | CS8 | CLOCAL | CREAD; + baud = 1200; + } else { + up->cflag = B4800 | CS8 | CLOCAL | CREAD; + baud = 4800; + } + + up->curregs[R15] |= BRKIE; + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + sunzilog_convert_to_zs(up, up->cflag, 0, brg); + sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS); + __sunzilog_startup(up); +} + +#ifdef CONFIG_SERIO +static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up) +{ + struct serio *serio = &up->serio; + + serio->port_data = up; + + serio->id.type = SERIO_RS232; + if (up->flags & SUNZILOG_FLAG_CONS_KEYB) { + serio->id.proto = SERIO_SUNKBD; + strlcpy(serio->name, "zskbd", sizeof(serio->name)); + } else { + serio->id.proto = SERIO_SUN; + serio->id.extra = 1; + strlcpy(serio->name, "zsms", sizeof(serio->name)); + } + strlcpy(serio->phys, + ((up->flags & SUNZILOG_FLAG_CONS_KEYB) ? + "zs/serio0" : "zs/serio1"), + sizeof(serio->phys)); + + serio->write = sunzilog_serio_write; + serio->open = sunzilog_serio_open; + serio->close = sunzilog_serio_close; + serio->dev.parent = up->port.dev; + + serio_register_port(serio); +} +#endif + +static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) +{ + struct zilog_channel __iomem *channel; + unsigned long flags; + int baud, brg; + + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + + spin_lock_irqsave(&up->port.lock, flags); + if (ZS_IS_CHANNEL_A(up)) { + write_zsreg(channel, R9, FHWRES); + ZSDELAY_LONG(); + (void) read_zsreg(channel, R0); + } + + if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | + SUNZILOG_FLAG_CONS_MOUSE)) { + up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; + up->curregs[R4] = PAR_EVEN | X16CLK | SB1; + up->curregs[R3] = RxENAB | Rx8; + up->curregs[R5] = TxENAB | Tx8; + up->curregs[R6] = 0x00; /* SDLC Address */ + up->curregs[R7] = 0x7E; /* SDLC Flag */ + up->curregs[R9] = NV; + up->curregs[R7p] = 0x00; + sunzilog_init_kbdms(up); + /* Only enable interrupts if an ISR handler available */ + if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) + up->curregs[R9] |= MIE; + write_zsreg(channel, R9, up->curregs[R9]); + } else { + /* Normal serial TTY. */ + up->parity_mask = 0xff; + up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB; + up->curregs[R4] = PAR_EVEN | X16CLK | SB1; + up->curregs[R3] = RxENAB | Rx8; + up->curregs[R5] = TxENAB | Tx8; + up->curregs[R6] = 0x00; /* SDLC Address */ + up->curregs[R7] = 0x7E; /* SDLC Flag */ + up->curregs[R9] = NV; + up->curregs[R10] = NRZ; + up->curregs[R11] = TCBR | RCBR; + baud = 9600; + brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR); + up->curregs[R12] = (brg & 0xff); + up->curregs[R13] = (brg >> 8) & 0xff; + up->curregs[R14] = BRSRC | BRENAB; + up->curregs[R15] = FIFOEN; /* Use FIFO if on ESCC */ + up->curregs[R7p] = TxFIFO_LVL | RxFIFO_LVL; + if (__load_zsregs(channel, up->curregs)) { + up->flags |= SUNZILOG_FLAG_ESCC; + } + /* Only enable interrupts if an ISR handler available */ + if (up->flags & SUNZILOG_FLAG_ISR_HANDLER) + up->curregs[R9] |= MIE; + write_zsreg(channel, R9, up->curregs[R9]); + } + + spin_unlock_irqrestore(&up->port.lock, flags); + +#ifdef CONFIG_SERIO + if (up->flags & (SUNZILOG_FLAG_CONS_KEYB | + SUNZILOG_FLAG_CONS_MOUSE)) + sunzilog_register_serio(up); +#endif +} + +static int zilog_irq = -1; + +static int __devinit zs_probe(struct platform_device *op, const struct of_device_id *match) +{ + static int kbm_inst, uart_inst; + int inst; + struct uart_sunzilog_port *up; + struct zilog_layout __iomem *rp; + int keyboard_mouse = 0; + int err; + + if (of_find_property(op->dev.of_node, "keyboard", NULL)) + keyboard_mouse = 1; + + /* uarts must come before keyboards/mice */ + if (keyboard_mouse) + inst = uart_chip_count + kbm_inst; + else + inst = uart_inst; + + sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0, + sizeof(struct zilog_layout), + "zs"); + if (!sunzilog_chip_regs[inst]) + return -ENOMEM; + + rp = sunzilog_chip_regs[inst]; + + if (zilog_irq == -1) + zilog_irq = op->archdata.irqs[0]; + + up = &sunzilog_port_table[inst * 2]; + + /* Channel A */ + up[0].port.mapbase = op->resource[0].start + 0x00; + up[0].port.membase = (void __iomem *) &rp->channelA; + up[0].port.iotype = UPIO_MEM; + up[0].port.irq = op->archdata.irqs[0]; + up[0].port.uartclk = ZS_CLOCK; + up[0].port.fifosize = 1; + up[0].port.ops = &sunzilog_pops; + up[0].port.type = PORT_SUNZILOG; + up[0].port.flags = 0; + up[0].port.line = (inst * 2) + 0; + up[0].port.dev = &op->dev; + up[0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A; + if (keyboard_mouse) + up[0].flags |= SUNZILOG_FLAG_CONS_KEYB; + sunzilog_init_hw(&up[0]); + + /* Channel B */ + up[1].port.mapbase = op->resource[0].start + 0x04; + up[1].port.membase = (void __iomem *) &rp->channelB; + up[1].port.iotype = UPIO_MEM; + up[1].port.irq = op->archdata.irqs[0]; + up[1].port.uartclk = ZS_CLOCK; + up[1].port.fifosize = 1; + up[1].port.ops = &sunzilog_pops; + up[1].port.type = PORT_SUNZILOG; + up[1].port.flags = 0; + up[1].port.line = (inst * 2) + 1; + up[1].port.dev = &op->dev; + up[1].flags |= 0; + if (keyboard_mouse) + up[1].flags |= SUNZILOG_FLAG_CONS_MOUSE; + sunzilog_init_hw(&up[1]); + + if (!keyboard_mouse) { + if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, + &sunzilog_reg, up[0].port.line, + false)) + up->flags |= SUNZILOG_FLAG_IS_CONS; + err = uart_add_one_port(&sunzilog_reg, &up[0].port); + if (err) { + of_iounmap(&op->resource[0], + rp, sizeof(struct zilog_layout)); + return err; + } + if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, + &sunzilog_reg, up[1].port.line, + false)) + up->flags |= SUNZILOG_FLAG_IS_CONS; + err = uart_add_one_port(&sunzilog_reg, &up[1].port); + if (err) { + uart_remove_one_port(&sunzilog_reg, &up[0].port); + of_iounmap(&op->resource[0], + rp, sizeof(struct zilog_layout)); + return err; + } + uart_inst++; + } else { + printk(KERN_INFO "%s: Keyboard at MMIO 0x%llx (irq = %d) " + "is a %s\n", + dev_name(&op->dev), + (unsigned long long) up[0].port.mapbase, + op->archdata.irqs[0], sunzilog_type(&up[0].port)); + printk(KERN_INFO "%s: Mouse at MMIO 0x%llx (irq = %d) " + "is a %s\n", + dev_name(&op->dev), + (unsigned long long) up[1].port.mapbase, + op->archdata.irqs[0], sunzilog_type(&up[1].port)); + kbm_inst++; + } + + dev_set_drvdata(&op->dev, &up[0]); + + return 0; +} + +static void __devexit zs_remove_one(struct uart_sunzilog_port *up) +{ + if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) { +#ifdef CONFIG_SERIO + serio_unregister_port(&up->serio); +#endif + } else + uart_remove_one_port(&sunzilog_reg, &up->port); +} + +static int __devexit zs_remove(struct platform_device *op) +{ + struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); + struct zilog_layout __iomem *regs; + + zs_remove_one(&up[0]); + zs_remove_one(&up[1]); + + regs = sunzilog_chip_regs[up[0].port.line / 2]; + of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout)); + + dev_set_drvdata(&op->dev, NULL); + + return 0; +} + +static const struct of_device_id zs_match[] = { + { + .name = "zs", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, zs_match); + +static struct of_platform_driver zs_driver = { + .driver = { + .name = "zs", + .owner = THIS_MODULE, + .of_match_table = zs_match, + }, + .probe = zs_probe, + .remove = __devexit_p(zs_remove), +}; + +static int __init sunzilog_init(void) +{ + struct device_node *dp; + int err; + int num_keybms = 0; + int num_sunzilog = 0; + + for_each_node_by_name(dp, "zs") { + num_sunzilog++; + if (of_find_property(dp, "keyboard", NULL)) + num_keybms++; + } + + if (num_sunzilog) { + err = sunzilog_alloc_tables(num_sunzilog); + if (err) + goto out; + + uart_chip_count = num_sunzilog - num_keybms; + + err = sunserial_register_minors(&sunzilog_reg, + uart_chip_count * 2); + if (err) + goto out_free_tables; + } + + err = of_register_platform_driver(&zs_driver); + if (err) + goto out_unregister_uart; + + if (zilog_irq != -1) { + struct uart_sunzilog_port *up = sunzilog_irq_chain; + err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED, + "zs", sunzilog_irq_chain); + if (err) + goto out_unregister_driver; + + /* Enable Interrupts */ + while (up) { + struct zilog_channel __iomem *channel; + + /* printk (KERN_INFO "Enable IRQ for ZILOG Hardware %p\n", up); */ + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + up->flags |= SUNZILOG_FLAG_ISR_HANDLER; + up->curregs[R9] |= MIE; + write_zsreg(channel, R9, up->curregs[R9]); + up = up->next; + } + } + +out: + return err; + +out_unregister_driver: + of_unregister_platform_driver(&zs_driver); + +out_unregister_uart: + if (num_sunzilog) { + sunserial_unregister_minors(&sunzilog_reg, num_sunzilog); + sunzilog_reg.cons = NULL; + } + +out_free_tables: + sunzilog_free_tables(); + goto out; +} + +static void __exit sunzilog_exit(void) +{ + of_unregister_platform_driver(&zs_driver); + + if (zilog_irq != -1) { + struct uart_sunzilog_port *up = sunzilog_irq_chain; + + /* Disable Interrupts */ + while (up) { + struct zilog_channel __iomem *channel; + + /* printk (KERN_INFO "Disable IRQ for ZILOG Hardware %p\n", up); */ + channel = ZILOG_CHANNEL_FROM_PORT(&up->port); + up->flags &= ~SUNZILOG_FLAG_ISR_HANDLER; + up->curregs[R9] &= ~MIE; + write_zsreg(channel, R9, up->curregs[R9]); + up = up->next; + } + + free_irq(zilog_irq, sunzilog_irq_chain); + zilog_irq = -1; + } + + if (sunzilog_reg.nr) { + sunserial_unregister_minors(&sunzilog_reg, sunzilog_reg.nr); + sunzilog_free_tables(); + } +} + +module_init(sunzilog_init); +module_exit(sunzilog_exit); + +MODULE_AUTHOR("David S. Miller"); +MODULE_DESCRIPTION("Sun Zilog serial port driver"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/sunzilog.h b/drivers/tty/serial/sunzilog.h new file mode 100644 index 000000000000..5dec7b47cc38 --- /dev/null +++ b/drivers/tty/serial/sunzilog.h @@ -0,0 +1,289 @@ +#ifndef _SUNZILOG_H +#define _SUNZILOG_H + +struct zilog_channel { + volatile unsigned char control; + volatile unsigned char __pad1; + volatile unsigned char data; + volatile unsigned char __pad2; +}; + +struct zilog_layout { + struct zilog_channel channelB; + struct zilog_channel channelA; +}; + +#define NUM_ZSREGS 17 +#define R7p 16 /* Written as R7 with P15 bit 0 set */ + +/* Conversion routines to/from brg time constants from/to bits + * per second. + */ +#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) +#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) + +/* The Zilog register set */ + +#define FLAG 0x7e + +/* Write Register 0 */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 */ + +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ +#define INT_ERR_Rx 0x18 /* Int on error only */ +#define RxINT_MASK 0x18 + +#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ +#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ +#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ + +/* Write Register #2 (Interrupt Vector) */ + +/* Write Register 3 */ + +#define RxENAB 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxN_MASK 0xc0 + +/* Write Register 4 */ + +#define PAR_ENAB 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xC0 /* x64 clock mode */ +#define XCLK_MASK 0xC0 + +/* Write Register 5 */ + +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENAB 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define TxN_MASK 0x60 +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 7' (ESCC Only) */ +#define AUTO_TxFLAG 1 /* Automatic Tx SDLC Flag */ +#define AUTO_EOM_RST 2 /* Automatic EOM Reset */ +#define AUTOnRTS 4 /* Automatic /RTS pin deactivation */ +#define RxFIFO_LVL 8 /* Receive FIFO interrupt level */ +#define nDTRnREQ 0x10 /* /DTR/REQ timing */ +#define TxFIFO_LVL 0x20 /* Transmit FIFO interrupt level */ +#define EXT_RD_EN 0x40 /* Extended read register enable */ + +/* Write Register 8 (transmit buffer) */ + +/* Write Register 9 (Master interrupt control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define SWIACK 0x20 /* Software Interrupt Ack (not on NMOS) */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (misc control bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (lower byte of baud rate generator time constant) */ + +/* Write Register 13 (upper byte of baud rate generator time constant) */ + +/* Write Register 14 (Misc control bits) */ +#define BRENAB 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (external/status interrupt control) */ +#define WR7pEN 1 /* WR7' Enable (ESCC only) */ +#define ZCIE 2 /* Zero count IE */ +#define FIFOEN 4 /* FIFO Enable (ESCC only) */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define CRC_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (channel b only) - Interrupt vector */ +#define CHB_Tx_EMPTY 0x00 +#define CHB_EXT_STAT 0x02 +#define CHB_Rx_AVAIL 0x04 +#define CHB_SPECIAL 0x06 +#define CHA_Tx_EMPTY 0x08 +#define CHA_EXT_STAT 0x0a +#define CHA_Rx_AVAIL 0x0c +#define CHA_SPECIAL 0x0e +#define STATUS_MASK 0x0e + +/* Read Register 3 (interrupt pending register) ch a only */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 6 (LSB frame byte count [Not on NMOS]) */ + +/* Read Register 7 (MSB frame byte count and FIFO status [Not on NMOS]) */ + +/* Read Register 8 (receive data register) */ + +/* Read Register 10 (misc status bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (lower byte of baud rate generator constant) */ + +/* Read Register 13 (upper byte of baud rate generator constant) */ + +/* Read Register 15 (value of WR 15) */ + +/* Misc macros */ +#define ZS_CLEARERR(channel) do { sbus_writeb(ERR_RES, &channel->control); \ + udelay(5); } while(0) + +#define ZS_CLEARSTAT(channel) do { sbus_writeb(RES_EXT_INT, &channel->control); \ + udelay(5); } while(0) + +#define ZS_CLEARFIFO(channel) do { sbus_readb(&channel->data); \ + udelay(2); \ + sbus_readb(&channel->data); \ + udelay(2); \ + sbus_readb(&channel->data); \ + udelay(2); } while(0) + +#endif /* _SUNZILOG_H */ diff --git a/drivers/tty/serial/timbuart.c b/drivers/tty/serial/timbuart.c new file mode 100644 index 000000000000..1f36b7eb7351 --- /dev/null +++ b/drivers/tty/serial/timbuart.c @@ -0,0 +1,531 @@ +/* + * timbuart.c timberdale FPGA UART driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA UART + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "timbuart.h" + +struct timbuart_port { + struct uart_port port; + struct tasklet_struct tasklet; + int usedma; + u32 last_ier; + struct platform_device *dev; +}; + +static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, + 921600, 1843200, 3250000}; + +static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier); + +static irqreturn_t timbuart_handleinterrupt(int irq, void *devid); + +static void timbuart_stop_rx(struct uart_port *port) +{ + /* spin lock held by upper layer, disable all RX interrupts */ + u32 ier = ioread32(port->membase + TIMBUART_IER) & ~RXFLAGS; + iowrite32(ier, port->membase + TIMBUART_IER); +} + +static void timbuart_stop_tx(struct uart_port *port) +{ + /* spinlock held by upper layer, disable TX interrupt */ + u32 ier = ioread32(port->membase + TIMBUART_IER) & ~TXBAE; + iowrite32(ier, port->membase + TIMBUART_IER); +} + +static void timbuart_start_tx(struct uart_port *port) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + + /* do not transfer anything here -> fire off the tasklet */ + tasklet_schedule(&uart->tasklet); +} + +static unsigned int timbuart_tx_empty(struct uart_port *port) +{ + u32 isr = ioread32(port->membase + TIMBUART_ISR); + + return (isr & TXBE) ? TIOCSER_TEMT : 0; +} + +static void timbuart_flush_buffer(struct uart_port *port) +{ + if (!timbuart_tx_empty(port)) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHTX; + + iowrite8(ctl, port->membase + TIMBUART_CTRL); + iowrite32(TXBF, port->membase + TIMBUART_ISR); + } +} + +static void timbuart_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + + while (ioread32(port->membase + TIMBUART_ISR) & RXDP) { + u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); + port->icount.rx++; + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } + + spin_unlock(&port->lock); + tty_flip_buffer_push(port->state->port.tty); + spin_lock(&port->lock); + + dev_dbg(port->dev, "%s - total read %d bytes\n", + __func__, port->icount.rx); +} + +static void timbuart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && + !uart_circ_empty(xmit)) { + iowrite8(xmit->buf[xmit->tail], + port->membase + TIMBUART_TXFIFO); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + dev_dbg(port->dev, + "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n", + __func__, + port->icount.tx, + ioread8(port->membase + TIMBUART_CTRL), + port->mctrl & TIOCM_RTS, + ioread8(port->membase + TIMBUART_BAUDRATE)); +} + +static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + struct circ_buf *xmit = &port->state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + if (port->x_char) + return; + + if (isr & TXFLAGS) { + timbuart_tx_chars(port); + /* clear all TX interrupts */ + iowrite32(TXFLAGS, port->membase + TIMBUART_ISR); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + } else + /* Re-enable any tx interrupt */ + *ier |= uart->last_ier & TXFLAGS; + + /* enable interrupts if there are chars in the transmit buffer, + * Or if we delivered some bytes and want the almost empty interrupt + * we wake up the upper layer later when we got the interrupt + * to give it some time to go out... + */ + if (!uart_circ_empty(xmit)) + *ier |= TXBAE; + + dev_dbg(port->dev, "%s - leaving\n", __func__); +} + +void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) +{ + if (isr & RXFLAGS) { + /* Some RX status is set */ + if (isr & RXBF) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHRX; + iowrite8(ctl, port->membase + TIMBUART_CTRL); + port->icount.overrun++; + } else if (isr & (RXDP)) + timbuart_rx_chars(port); + + /* ack all RX interrupts */ + iowrite32(RXFLAGS, port->membase + TIMBUART_ISR); + } + + /* always have the RX interrupts enabled */ + *ier |= RXBAF | RXBF | RXTT; + + dev_dbg(port->dev, "%s - leaving\n", __func__); +} + +void timbuart_tasklet(unsigned long arg) +{ + struct timbuart_port *uart = (struct timbuart_port *)arg; + u32 isr, ier = 0; + + spin_lock(&uart->port.lock); + + isr = ioread32(uart->port.membase + TIMBUART_ISR); + dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); + + if (!uart->usedma) + timbuart_handle_tx_port(&uart->port, isr, &ier); + + timbuart_mctrl_check(&uart->port, isr, &ier); + + if (!uart->usedma) + timbuart_handle_rx_port(&uart->port, isr, &ier); + + iowrite32(ier, uart->port.membase + TIMBUART_IER); + + spin_unlock(&uart->port.lock); + dev_dbg(uart->port.dev, "%s leaving\n", __func__); +} + +static unsigned int timbuart_get_mctrl(struct uart_port *port) +{ + u8 cts = ioread8(port->membase + TIMBUART_CTRL); + dev_dbg(port->dev, "%s - cts %x\n", __func__, cts); + + if (cts & TIMBUART_CTRL_CTS) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + dev_dbg(port->dev, "%s - %x\n", __func__, mctrl); + + if (mctrl & TIOCM_RTS) + iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); + else + iowrite8(0, port->membase + TIMBUART_CTRL); +} + +static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) +{ + unsigned int cts; + + if (isr & CTS_DELTA) { + /* ack */ + iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR); + cts = timbuart_get_mctrl(port); + uart_handle_cts_change(port, cts & TIOCM_CTS); + wake_up_interruptible(&port->state->port.delta_msr_wait); + } + + *ier |= CTS_DELTA; +} + +static void timbuart_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void timbuart_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static int timbuart_startup(struct uart_port *port) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + + dev_dbg(port->dev, "%s\n", __func__); + + iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL); + iowrite32(0x1ff, port->membase + TIMBUART_ISR); + /* Enable all but TX interrupts */ + iowrite32(RXBAF | RXBF | RXTT | CTS_DELTA, + port->membase + TIMBUART_IER); + + return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED, + "timb-uart", uart); +} + +static void timbuart_shutdown(struct uart_port *port) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + dev_dbg(port->dev, "%s\n", __func__); + free_irq(port->irq, uart); + iowrite32(0, port->membase + TIMBUART_IER); +} + +static int get_bindex(int baud) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(baudrates); i++) + if (baud <= baudrates[i]) + return i; + + return -1; +} + +static void timbuart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud; + short bindex; + unsigned long flags; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + bindex = get_bindex(baud); + dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex); + + if (bindex < 0) + bindex = 0; + baud = baudrates[bindex]; + + /* The serial layer calls into this once with old = NULL when setting + up initially */ + if (old) + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *timbuart_type(struct uart_port *port) +{ + return port->type == PORT_UNKNOWN ? "timbuart" : NULL; +} + +/* We do not request/release mappings of the registers here, + * currently it's done in the proble function. + */ +static void timbuart_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + int size = + resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); + + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, size); +} + +static int timbuart_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + int size = + resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); + + if (!request_mem_region(port->mapbase, size, "timb-uart")) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, size); + if (port->membase == NULL) { + release_mem_region(port->mapbase, size); + return -ENOMEM; + } + } + + return 0; +} + +static irqreturn_t timbuart_handleinterrupt(int irq, void *devid) +{ + struct timbuart_port *uart = (struct timbuart_port *)devid; + + if (ioread8(uart->port.membase + TIMBUART_IPR)) { + uart->last_ier = ioread32(uart->port.membase + TIMBUART_IER); + + /* disable interrupts, the tasklet enables them again */ + iowrite32(0, uart->port.membase + TIMBUART_IER); + + /* fire off bottom half */ + tasklet_schedule(&uart->tasklet); + + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +/* + * Configure/autoconfigure the port. + */ +static void timbuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_TIMBUART; + timbuart_request_port(port); + } +} + +static int timbuart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + +static struct uart_ops timbuart_ops = { + .tx_empty = timbuart_tx_empty, + .set_mctrl = timbuart_set_mctrl, + .get_mctrl = timbuart_get_mctrl, + .stop_tx = timbuart_stop_tx, + .start_tx = timbuart_start_tx, + .flush_buffer = timbuart_flush_buffer, + .stop_rx = timbuart_stop_rx, + .enable_ms = timbuart_enable_ms, + .break_ctl = timbuart_break_ctl, + .startup = timbuart_startup, + .shutdown = timbuart_shutdown, + .set_termios = timbuart_set_termios, + .type = timbuart_type, + .release_port = timbuart_release_port, + .request_port = timbuart_request_port, + .config_port = timbuart_config_port, + .verify_port = timbuart_verify_port +}; + +static struct uart_driver timbuart_driver = { + .owner = THIS_MODULE, + .driver_name = "timberdale_uart", + .dev_name = "ttyTU", + .major = TIMBUART_MAJOR, + .minor = TIMBUART_MINOR, + .nr = 1 +}; + +static int __devinit timbuart_probe(struct platform_device *dev) +{ + int err, irq; + struct timbuart_port *uart; + struct resource *iomem; + + dev_dbg(&dev->dev, "%s\n", __func__); + + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) { + err = -EINVAL; + goto err_mem; + } + + uart->usedma = 0; + + uart->port.uartclk = 3250000 * 16; + uart->port.fifosize = TIMBUART_FIFO_SIZE; + uart->port.regshift = 2; + uart->port.iotype = UPIO_MEM; + uart->port.ops = &timbuart_ops; + uart->port.irq = 0; + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + uart->port.line = 0; + uart->port.dev = &dev->dev; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -ENOMEM; + goto err_register; + } + uart->port.mapbase = iomem->start; + uart->port.membase = NULL; + + irq = platform_get_irq(dev, 0); + if (irq < 0) { + err = -EINVAL; + goto err_register; + } + uart->port.irq = irq; + + tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); + + err = uart_register_driver(&timbuart_driver); + if (err) + goto err_register; + + err = uart_add_one_port(&timbuart_driver, &uart->port); + if (err) + goto err_add_port; + + platform_set_drvdata(dev, uart); + + return 0; + +err_add_port: + uart_unregister_driver(&timbuart_driver); +err_register: + kfree(uart); +err_mem: + printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n", + err); + + return err; +} + +static int __devexit timbuart_remove(struct platform_device *dev) +{ + struct timbuart_port *uart = platform_get_drvdata(dev); + + tasklet_kill(&uart->tasklet); + uart_remove_one_port(&timbuart_driver, &uart->port); + uart_unregister_driver(&timbuart_driver); + kfree(uart); + + return 0; +} + +static struct platform_driver timbuart_platform_driver = { + .driver = { + .name = "timb-uart", + .owner = THIS_MODULE, + }, + .probe = timbuart_probe, + .remove = __devexit_p(timbuart_remove), +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbuart_init(void) +{ + return platform_driver_register(&timbuart_platform_driver); +} + +static void __exit timbuart_exit(void) +{ + platform_driver_unregister(&timbuart_platform_driver); +} + +module_init(timbuart_init); +module_exit(timbuart_exit); + +MODULE_DESCRIPTION("Timberdale UART driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:timb-uart"); + diff --git a/drivers/tty/serial/timbuart.h b/drivers/tty/serial/timbuart.h new file mode 100644 index 000000000000..7e566766bc43 --- /dev/null +++ b/drivers/tty/serial/timbuart.h @@ -0,0 +1,58 @@ +/* + * timbuart.c timberdale FPGA GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA UART + */ + +#ifndef _TIMBUART_H +#define _TIMBUART_H + +#define TIMBUART_FIFO_SIZE 2048 + +#define TIMBUART_RXFIFO 0x08 +#define TIMBUART_TXFIFO 0x0c +#define TIMBUART_IER 0x10 +#define TIMBUART_IPR 0x14 +#define TIMBUART_ISR 0x18 +#define TIMBUART_CTRL 0x1c +#define TIMBUART_BAUDRATE 0x20 + +#define TIMBUART_CTRL_RTS 0x01 +#define TIMBUART_CTRL_CTS 0x02 +#define TIMBUART_CTRL_FLSHTX 0x40 +#define TIMBUART_CTRL_FLSHRX 0x80 + +#define TXBF 0x01 +#define TXBAE 0x02 +#define CTS_DELTA 0x04 +#define RXDP 0x08 +#define RXBAF 0x10 +#define RXBF 0x20 +#define RXTT 0x40 +#define RXBNAE 0x80 +#define TXBE 0x100 + +#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE) +#define TXFLAGS (TXBF | TXBAE) + +#define TIMBUART_MAJOR 204 +#define TIMBUART_MINOR 192 + +#endif /* _TIMBUART_H */ + diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c new file mode 100644 index 000000000000..d2fce865b731 --- /dev/null +++ b/drivers/tty/serial/uartlite.c @@ -0,0 +1,709 @@ +/* + * uartlite.c: Serial driver for Xilinx uartlite serial controller + * + * Copyright (C) 2006 Peter Korsgaard + * Copyright (C) 2007 Secret Lab Technologies Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) +#include +#include +#include +#include + +/* Match table for of_platform binding */ +static struct of_device_id ulite_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-uartlite-1.00.b", }, + { .compatible = "xlnx,xps-uartlite-1.00.a", }, + {} +}; +MODULE_DEVICE_TABLE(of, ulite_of_match); + +#endif + +#define ULITE_NAME "ttyUL" +#define ULITE_MAJOR 204 +#define ULITE_MINOR 187 +#define ULITE_NR_UARTS 4 + +/* --------------------------------------------------------------------- + * Register definitions + * + * For register details see datasheet: + * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf + */ + +#define ULITE_RX 0x00 +#define ULITE_TX 0x04 +#define ULITE_STATUS 0x08 +#define ULITE_CONTROL 0x0c + +#define ULITE_REGION 16 + +#define ULITE_STATUS_RXVALID 0x01 +#define ULITE_STATUS_RXFULL 0x02 +#define ULITE_STATUS_TXEMPTY 0x04 +#define ULITE_STATUS_TXFULL 0x08 +#define ULITE_STATUS_IE 0x10 +#define ULITE_STATUS_OVERRUN 0x20 +#define ULITE_STATUS_FRAME 0x40 +#define ULITE_STATUS_PARITY 0x80 + +#define ULITE_CONTROL_RST_TX 0x01 +#define ULITE_CONTROL_RST_RX 0x02 +#define ULITE_CONTROL_IE 0x10 + + +static struct uart_port ulite_ports[ULITE_NR_UARTS]; + +/* --------------------------------------------------------------------- + * Core UART driver operations + */ + +static int ulite_receive(struct uart_port *port, int stat) +{ + struct tty_struct *tty = port->state->port.tty; + unsigned char ch = 0; + char flag = TTY_NORMAL; + + if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN + | ULITE_STATUS_FRAME)) == 0) + return 0; + + /* stats */ + if (stat & ULITE_STATUS_RXVALID) { + port->icount.rx++; + ch = ioread32be(port->membase + ULITE_RX); + + if (stat & ULITE_STATUS_PARITY) + port->icount.parity++; + } + + if (stat & ULITE_STATUS_OVERRUN) + port->icount.overrun++; + + if (stat & ULITE_STATUS_FRAME) + port->icount.frame++; + + + /* drop byte with parity error if IGNPAR specificed */ + if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY) + stat &= ~ULITE_STATUS_RXVALID; + + stat &= port->read_status_mask; + + if (stat & ULITE_STATUS_PARITY) + flag = TTY_PARITY; + + + stat &= ~port->ignore_status_mask; + + if (stat & ULITE_STATUS_RXVALID) + tty_insert_flip_char(tty, ch, flag); + + if (stat & ULITE_STATUS_FRAME) + tty_insert_flip_char(tty, 0, TTY_FRAME); + + if (stat & ULITE_STATUS_OVERRUN) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + + return 1; +} + +static int ulite_transmit(struct uart_port *port, int stat) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (stat & ULITE_STATUS_TXFULL) + return 0; + + if (port->x_char) { + iowrite32be(port->x_char, port->membase + ULITE_TX); + port->x_char = 0; + port->icount.tx++; + return 1; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return 0; + + iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + port->icount.tx++; + + /* wake up */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + return 1; +} + +static irqreturn_t ulite_isr(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + int busy, n = 0; + + do { + int stat = ioread32be(port->membase + ULITE_STATUS); + busy = ulite_receive(port, stat); + busy |= ulite_transmit(port, stat); + n++; + } while (busy); + + /* work done? */ + if (n > 1) { + tty_flip_buffer_push(port->state->port.tty); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static unsigned int ulite_tx_empty(struct uart_port *port) +{ + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&port->lock, flags); + ret = ioread32be(port->membase + ULITE_STATUS); + spin_unlock_irqrestore(&port->lock, flags); + + return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; +} + +static unsigned int ulite_get_mctrl(struct uart_port *port) +{ + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; +} + +static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* N/A */ +} + +static void ulite_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + +static void ulite_start_tx(struct uart_port *port) +{ + ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); +} + +static void ulite_stop_rx(struct uart_port *port) +{ + /* don't forward any more data (like !CREAD) */ + port->ignore_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY + | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; +} + +static void ulite_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void ulite_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static int ulite_startup(struct uart_port *port) +{ + int ret; + + ret = request_irq(port->irq, ulite_isr, + IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port); + if (ret) + return ret; + + iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + + return 0; +} + +static void ulite_shutdown(struct uart_port *port) +{ + iowrite32be(0, port->membase + ULITE_CONTROL); + ioread32be(port->membase + ULITE_CONTROL); /* dummy */ + free_irq(port->irq, port); +} + +static void ulite_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud; + + spin_lock_irqsave(&port->lock, flags); + + port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN + | ULITE_STATUS_TXFULL; + + if (termios->c_iflag & INPCK) + port->read_status_mask |= + ULITE_STATUS_PARITY | ULITE_STATUS_FRAME; + + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ULITE_STATUS_PARITY + | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; + + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= + ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY + | ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN; + + /* update timeout */ + baud = uart_get_baud_rate(port, termios, old, 0, 460800); + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *ulite_type(struct uart_port *port) +{ + return port->type == PORT_UARTLITE ? "uartlite" : NULL; +} + +static void ulite_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, ULITE_REGION); + iounmap(port->membase); + port->membase = NULL; +} + +static int ulite_request_port(struct uart_port *port) +{ + pr_debug("ulite console: port=%p; port->mapbase=%llx\n", + port, (unsigned long long) port->mapbase); + + if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + port->membase = ioremap(port->mapbase, ULITE_REGION); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, ULITE_REGION); + return -EBUSY; + } + + return 0; +} + +static void ulite_config_port(struct uart_port *port, int flags) +{ + if (!ulite_request_port(port)) + port->type = PORT_UARTLITE; +} + +static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + +#ifdef CONFIG_CONSOLE_POLL +static int ulite_get_poll_char(struct uart_port *port) +{ + if (!(ioread32be(port->membase + ULITE_STATUS) + & ULITE_STATUS_RXVALID)) + return NO_POLL_CHAR; + + return ioread32be(port->membase + ULITE_RX); +} + +static void ulite_put_poll_char(struct uart_port *port, unsigned char ch) +{ + while (ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_TXFULL) + cpu_relax(); + + /* write char to device */ + iowrite32be(ch, port->membase + ULITE_TX); +} +#endif + +static struct uart_ops ulite_ops = { + .tx_empty = ulite_tx_empty, + .set_mctrl = ulite_set_mctrl, + .get_mctrl = ulite_get_mctrl, + .stop_tx = ulite_stop_tx, + .start_tx = ulite_start_tx, + .stop_rx = ulite_stop_rx, + .enable_ms = ulite_enable_ms, + .break_ctl = ulite_break_ctl, + .startup = ulite_startup, + .shutdown = ulite_shutdown, + .set_termios = ulite_set_termios, + .type = ulite_type, + .release_port = ulite_release_port, + .request_port = ulite_request_port, + .config_port = ulite_config_port, + .verify_port = ulite_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = ulite_get_poll_char, + .poll_put_char = ulite_put_poll_char, +#endif +}; + +/* --------------------------------------------------------------------- + * Console driver operations + */ + +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE +static void ulite_console_wait_tx(struct uart_port *port) +{ + int i; + u8 val; + + /* Spin waiting for TX fifo to have space available */ + for (i = 0; i < 100000; i++) { + val = ioread32be(port->membase + ULITE_STATUS); + if ((val & ULITE_STATUS_TXFULL) == 0) + break; + cpu_relax(); + } +} + +static void ulite_console_putchar(struct uart_port *port, int ch) +{ + ulite_console_wait_tx(port); + iowrite32be(ch, port->membase + ULITE_TX); +} + +static void ulite_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &ulite_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + if (oops_in_progress) { + locked = spin_trylock_irqsave(&port->lock, flags); + } else + spin_lock_irqsave(&port->lock, flags); + + /* save and disable interrupt */ + ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; + iowrite32be(0, port->membase + ULITE_CONTROL); + + uart_console_write(port, s, count, ulite_console_putchar); + + ulite_console_wait_tx(port); + + /* restore interrupt state */ + if (ier) + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + + if (locked) + spin_unlock_irqrestore(&port->lock, flags); +} + +static int __devinit ulite_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= ULITE_NR_UARTS) + return -EINVAL; + + port = &ulite_ports[co->index]; + + /* Has the device been initialized yet? */ + if (!port->mapbase) { + pr_debug("console on ttyUL%i not present\n", co->index); + return -ENODEV; + } + + /* not initialized yet? */ + if (!port->membase) { + if (ulite_request_port(port)) + return -ENODEV; + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver ulite_uart_driver; + +static struct console ulite_console = { + .name = ULITE_NAME, + .write = ulite_console_write, + .device = uart_console_device, + .setup = ulite_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */ + .data = &ulite_uart_driver, +}; + +static int __init ulite_console_init(void) +{ + register_console(&ulite_console); + return 0; +} + +console_initcall(ulite_console_init); + +#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */ + +static struct uart_driver ulite_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "uartlite", + .dev_name = ULITE_NAME, + .major = ULITE_MAJOR, + .minor = ULITE_MINOR, + .nr = ULITE_NR_UARTS, +#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE + .cons = &ulite_console, +#endif +}; + +/* --------------------------------------------------------------------- + * Port assignment functions (mapping devices to uart_port structures) + */ + +/** ulite_assign: register a uartlite device with the driver + * + * @dev: pointer to device structure + * @id: requested id number. Pass -1 for automatic port assignment + * @base: base address of uartlite registers + * @irq: irq number for uartlite + * + * Returns: 0 on success, <0 otherwise + */ +static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq) +{ + struct uart_port *port; + int rc; + + /* if id = -1; then scan for a free id and use that */ + if (id < 0) { + for (id = 0; id < ULITE_NR_UARTS; id++) + if (ulite_ports[id].mapbase == 0) + break; + } + if (id < 0 || id >= ULITE_NR_UARTS) { + dev_err(dev, "%s%i too large\n", ULITE_NAME, id); + return -EINVAL; + } + + if ((ulite_ports[id].mapbase) && (ulite_ports[id].mapbase != base)) { + dev_err(dev, "cannot assign to %s%i; it is already in use\n", + ULITE_NAME, id); + return -EBUSY; + } + + port = &ulite_ports[id]; + + spin_lock_init(&port->lock); + port->fifosize = 16; + port->regshift = 2; + port->iotype = UPIO_MEM; + port->iobase = 1; /* mark port in use */ + port->mapbase = base; + port->membase = NULL; + port->ops = &ulite_ops; + port->irq = irq; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = dev; + port->type = PORT_UNKNOWN; + port->line = id; + + dev_set_drvdata(dev, port); + + /* Register the port */ + rc = uart_add_one_port(&ulite_uart_driver, port); + if (rc) { + dev_err(dev, "uart_add_one_port() failed; err=%i\n", rc); + port->mapbase = 0; + dev_set_drvdata(dev, NULL); + return rc; + } + + return 0; +} + +/** ulite_release: register a uartlite device with the driver + * + * @dev: pointer to device structure + */ +static int __devexit ulite_release(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + int rc = 0; + + if (port) { + rc = uart_remove_one_port(&ulite_uart_driver, port); + dev_set_drvdata(dev, NULL); + port->mapbase = 0; + } + + return rc; +} + +/* --------------------------------------------------------------------- + * Platform bus binding + */ + +static int __devinit ulite_probe(struct platform_device *pdev) +{ + struct resource *res, *res2; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + return ulite_assign(&pdev->dev, pdev->id, res->start, res2->start); +} + +static int __devexit ulite_remove(struct platform_device *pdev) +{ + return ulite_release(&pdev->dev); +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:uartlite"); + +static struct platform_driver ulite_platform_driver = { + .probe = ulite_probe, + .remove = __devexit_p(ulite_remove), + .driver = { + .owner = THIS_MODULE, + .name = "uartlite", + }, +}; + +/* --------------------------------------------------------------------- + * OF bus bindings + */ +#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) +static int __devinit +ulite_of_probe(struct platform_device *op, const struct of_device_id *match) +{ + struct resource res; + const unsigned int *id; + int irq, rc; + + dev_dbg(&op->dev, "%s(%p, %p)\n", __func__, op, match); + + rc = of_address_to_resource(op->dev.of_node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + irq = irq_of_parse_and_map(op->dev.of_node, 0); + + id = of_get_property(op->dev.of_node, "port-number", NULL); + + return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); +} + +static int __devexit ulite_of_remove(struct platform_device *op) +{ + return ulite_release(&op->dev); +} + +static struct of_platform_driver ulite_of_driver = { + .probe = ulite_of_probe, + .remove = __devexit_p(ulite_of_remove), + .driver = { + .name = "uartlite", + .owner = THIS_MODULE, + .of_match_table = ulite_of_match, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __init ulite_of_register(void) +{ + pr_debug("uartlite: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&ulite_of_driver); +} + +static inline void __exit ulite_of_unregister(void) +{ + of_unregister_platform_driver(&ulite_of_driver); +} +#else /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ +/* Appropriate config not enabled; do nothing helpers */ +static inline int __init ulite_of_register(void) { return 0; } +static inline void __exit ulite_of_unregister(void) { } +#endif /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ + +/* --------------------------------------------------------------------- + * Module setup/teardown + */ + +int __init ulite_init(void) +{ + int ret; + + pr_debug("uartlite: calling uart_register_driver()\n"); + ret = uart_register_driver(&ulite_uart_driver); + if (ret) + goto err_uart; + + ret = ulite_of_register(); + if (ret) + goto err_of; + + pr_debug("uartlite: calling platform_driver_register()\n"); + ret = platform_driver_register(&ulite_platform_driver); + if (ret) + goto err_plat; + + return 0; + +err_plat: + ulite_of_unregister(); +err_of: + uart_unregister_driver(&ulite_uart_driver); +err_uart: + printk(KERN_ERR "registering uartlite driver failed: err=%i", ret); + return ret; +} + +void __exit ulite_exit(void) +{ + platform_driver_unregister(&ulite_platform_driver); + ulite_of_unregister(); + uart_unregister_driver(&ulite_uart_driver); +} + +module_init(ulite_init); +module_exit(ulite_exit); + +MODULE_AUTHOR("Peter Korsgaard "); +MODULE_DESCRIPTION("Xilinx uartlite serial driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c new file mode 100644 index 000000000000..3f4848e2174a --- /dev/null +++ b/drivers/tty/serial/ucc_uart.c @@ -0,0 +1,1537 @@ +/* + * Freescale QUICC Engine UART device driver + * + * Author: Timur Tabi + * + * Copyright 2007 Freescale Semiconductor, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * This driver adds support for UART devices via Freescale's QUICC Engine + * found on some Freescale SOCs. + * + * If Soft-UART support is needed but not already present, then this driver + * will request and upload the "Soft-UART" microcode upon probe. The + * filename of the microcode should be fsl_qe_ucode_uart_X_YZ.bin, where "X" + * is the name of the SOC (e.g. 8323), and YZ is the revision of the SOC, + * (e.g. "11" for 1.1). + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +/* + * The GUMR flag for Soft UART. This would normally be defined in qe.h, + * but Soft-UART is a hack and we want to keep everything related to it in + * this file. + */ +#define UCC_SLOW_GUMR_H_SUART 0x00004000 /* Soft-UART */ + +/* + * soft_uart is 1 if we need to use Soft-UART mode + */ +static int soft_uart; +/* + * firmware_loaded is 1 if the firmware has been loaded, 0 otherwise. + */ +static int firmware_loaded; + +/* Enable this macro to configure all serial ports in internal loopback + mode */ +/* #define LOOPBACK */ + +/* The major and minor device numbers are defined in + * http://www.lanana.org/docs/device-list/devices-2.6+.txt. For the QE + * UART, we have major number 204 and minor numbers 46 - 49, which are the + * same as for the CPM2. This decision was made because no Freescale part + * has both a CPM and a QE. + */ +#define SERIAL_QE_MAJOR 204 +#define SERIAL_QE_MINOR 46 + +/* Since we only have minor numbers 46 - 49, there is a hard limit of 4 ports */ +#define UCC_MAX_UART 4 + +/* The number of buffer descriptors for receiving characters. */ +#define RX_NUM_FIFO 4 + +/* The number of buffer descriptors for transmitting characters. */ +#define TX_NUM_FIFO 4 + +/* The maximum size of the character buffer for a single RX BD. */ +#define RX_BUF_SIZE 32 + +/* The maximum size of the character buffer for a single TX BD. */ +#define TX_BUF_SIZE 32 + +/* + * The number of jiffies to wait after receiving a close command before the + * device is actually closed. This allows the last few characters to be + * sent over the wire. + */ +#define UCC_WAIT_CLOSING 100 + +struct ucc_uart_pram { + struct ucc_slow_pram common; + u8 res1[8]; /* reserved */ + __be16 maxidl; /* Maximum idle chars */ + __be16 idlc; /* temp idle counter */ + __be16 brkcr; /* Break count register */ + __be16 parec; /* receive parity error counter */ + __be16 frmec; /* receive framing error counter */ + __be16 nosec; /* receive noise counter */ + __be16 brkec; /* receive break condition counter */ + __be16 brkln; /* last received break length */ + __be16 uaddr[2]; /* UART address character 1 & 2 */ + __be16 rtemp; /* Temp storage */ + __be16 toseq; /* Transmit out of sequence char */ + __be16 cchars[8]; /* control characters 1-8 */ + __be16 rccm; /* receive control character mask */ + __be16 rccr; /* receive control character register */ + __be16 rlbc; /* receive last break character */ + __be16 res2; /* reserved */ + __be32 res3; /* reserved, should be cleared */ + u8 res4; /* reserved, should be cleared */ + u8 res5[3]; /* reserved, should be cleared */ + __be32 res6; /* reserved, should be cleared */ + __be32 res7; /* reserved, should be cleared */ + __be32 res8; /* reserved, should be cleared */ + __be32 res9; /* reserved, should be cleared */ + __be32 res10; /* reserved, should be cleared */ + __be32 res11; /* reserved, should be cleared */ + __be32 res12; /* reserved, should be cleared */ + __be32 res13; /* reserved, should be cleared */ +/* The rest is for Soft-UART only */ + __be16 supsmr; /* 0x90, Shadow UPSMR */ + __be16 res92; /* 0x92, reserved, initialize to 0 */ + __be32 rx_state; /* 0x94, RX state, initialize to 0 */ + __be32 rx_cnt; /* 0x98, RX count, initialize to 0 */ + u8 rx_length; /* 0x9C, Char length, set to 1+CL+PEN+1+SL */ + u8 rx_bitmark; /* 0x9D, reserved, initialize to 0 */ + u8 rx_temp_dlst_qe; /* 0x9E, reserved, initialize to 0 */ + u8 res14[0xBC - 0x9F]; /* reserved */ + __be32 dump_ptr; /* 0xBC, Dump pointer */ + __be32 rx_frame_rem; /* 0xC0, reserved, initialize to 0 */ + u8 rx_frame_rem_size; /* 0xC4, reserved, initialize to 0 */ + u8 tx_mode; /* 0xC5, mode, 0=AHDLC, 1=UART */ + __be16 tx_state; /* 0xC6, TX state */ + u8 res15[0xD0 - 0xC8]; /* reserved */ + __be32 resD0; /* 0xD0, reserved, initialize to 0 */ + u8 resD4; /* 0xD4, reserved, initialize to 0 */ + __be16 resD5; /* 0xD5, reserved, initialize to 0 */ +} __attribute__ ((packed)); + +/* SUPSMR definitions, for Soft-UART only */ +#define UCC_UART_SUPSMR_SL 0x8000 +#define UCC_UART_SUPSMR_RPM_MASK 0x6000 +#define UCC_UART_SUPSMR_RPM_ODD 0x0000 +#define UCC_UART_SUPSMR_RPM_LOW 0x2000 +#define UCC_UART_SUPSMR_RPM_EVEN 0x4000 +#define UCC_UART_SUPSMR_RPM_HIGH 0x6000 +#define UCC_UART_SUPSMR_PEN 0x1000 +#define UCC_UART_SUPSMR_TPM_MASK 0x0C00 +#define UCC_UART_SUPSMR_TPM_ODD 0x0000 +#define UCC_UART_SUPSMR_TPM_LOW 0x0400 +#define UCC_UART_SUPSMR_TPM_EVEN 0x0800 +#define UCC_UART_SUPSMR_TPM_HIGH 0x0C00 +#define UCC_UART_SUPSMR_FRZ 0x0100 +#define UCC_UART_SUPSMR_UM_MASK 0x00c0 +#define UCC_UART_SUPSMR_UM_NORMAL 0x0000 +#define UCC_UART_SUPSMR_UM_MAN_MULTI 0x0040 +#define UCC_UART_SUPSMR_UM_AUTO_MULTI 0x00c0 +#define UCC_UART_SUPSMR_CL_MASK 0x0030 +#define UCC_UART_SUPSMR_CL_8 0x0030 +#define UCC_UART_SUPSMR_CL_7 0x0020 +#define UCC_UART_SUPSMR_CL_6 0x0010 +#define UCC_UART_SUPSMR_CL_5 0x0000 + +#define UCC_UART_TX_STATE_AHDLC 0x00 +#define UCC_UART_TX_STATE_UART 0x01 +#define UCC_UART_TX_STATE_X1 0x00 +#define UCC_UART_TX_STATE_X16 0x80 + +#define UCC_UART_PRAM_ALIGNMENT 0x100 + +#define UCC_UART_SIZE_OF_BD UCC_SLOW_SIZE_OF_BD +#define NUM_CONTROL_CHARS 8 + +/* Private per-port data structure */ +struct uart_qe_port { + struct uart_port port; + struct ucc_slow __iomem *uccp; + struct ucc_uart_pram __iomem *uccup; + struct ucc_slow_info us_info; + struct ucc_slow_private *us_private; + struct device_node *np; + unsigned int ucc_num; /* First ucc is 0, not 1 */ + + u16 rx_nrfifos; + u16 rx_fifosize; + u16 tx_nrfifos; + u16 tx_fifosize; + int wait_closing; + u32 flags; + struct qe_bd *rx_bd_base; + struct qe_bd *rx_cur; + struct qe_bd *tx_bd_base; + struct qe_bd *tx_cur; + unsigned char *tx_buf; + unsigned char *rx_buf; + void *bd_virt; /* virtual address of the BD buffers */ + dma_addr_t bd_dma_addr; /* bus address of the BD buffers */ + unsigned int bd_size; /* size of BD buffer space */ +}; + +static struct uart_driver ucc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "ucc_uart", + .dev_name = "ttyQE", + .major = SERIAL_QE_MAJOR, + .minor = SERIAL_QE_MINOR, + .nr = UCC_MAX_UART, +}; + +/* + * Virtual to physical address translation. + * + * Given the virtual address for a character buffer, this function returns + * the physical (DMA) equivalent. + */ +static inline dma_addr_t cpu2qe_addr(void *addr, struct uart_qe_port *qe_port) +{ + if (likely((addr >= qe_port->bd_virt)) && + (addr < (qe_port->bd_virt + qe_port->bd_size))) + return qe_port->bd_dma_addr + (addr - qe_port->bd_virt); + + /* something nasty happened */ + printk(KERN_ERR "%s: addr=%p\n", __func__, addr); + BUG(); + return 0; +} + +/* + * Physical to virtual address translation. + * + * Given the physical (DMA) address for a character buffer, this function + * returns the virtual equivalent. + */ +static inline void *qe2cpu_addr(dma_addr_t addr, struct uart_qe_port *qe_port) +{ + /* sanity check */ + if (likely((addr >= qe_port->bd_dma_addr) && + (addr < (qe_port->bd_dma_addr + qe_port->bd_size)))) + return qe_port->bd_virt + (addr - qe_port->bd_dma_addr); + + /* something nasty happened */ + printk(KERN_ERR "%s: addr=%x\n", __func__, addr); + BUG(); + return NULL; +} + +/* + * Return 1 if the QE is done transmitting all buffers for this port + * + * This function scans each BD in sequence. If we find a BD that is not + * ready (READY=1), then we return 0 indicating that the QE is still sending + * data. If we reach the last BD (WRAP=1), then we know we've scanned + * the entire list, and all BDs are done. + */ +static unsigned int qe_uart_tx_empty(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + struct qe_bd *bdp = qe_port->tx_bd_base; + + while (1) { + if (in_be16(&bdp->status) & BD_SC_READY) + /* This BD is not done, so return "not done" */ + return 0; + + if (in_be16(&bdp->status) & BD_SC_WRAP) + /* + * This BD is done and it's the last one, so return + * "done" + */ + return 1; + + bdp++; + }; +} + +/* + * Set the modem control lines + * + * Although the QE can control the modem control lines (e.g. CTS), we + * don't need that support. This function must exist, however, otherwise + * the kernel will panic. + */ +void qe_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +/* + * Get the current modem control line status + * + * Although the QE can control the modem control lines (e.g. CTS), this + * driver currently doesn't support that, so we always return Carrier + * Detect, Data Set Ready, and Clear To Send. + */ +static unsigned int qe_uart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/* + * Disable the transmit interrupt. + * + * Although this function is called "stop_tx", it does not actually stop + * transmission of data. Instead, it tells the QE to not generate an + * interrupt when the UCC is finished sending characters. + */ +static void qe_uart_stop_tx(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + + clrbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_TX); +} + +/* + * Transmit as many characters to the HW as possible. + * + * This function will attempt to stuff of all the characters from the + * kernel's transmit buffer into TX BDs. + * + * A return value of non-zero indicates that it successfully stuffed all + * characters from the kernel buffer. + * + * A return value of zero indicates that there are still characters in the + * kernel's buffer that have not been transmitted, but there are no more BDs + * available. This function should be called again after a BD has been made + * available. + */ +static int qe_uart_tx_pump(struct uart_qe_port *qe_port) +{ + struct qe_bd *bdp; + unsigned char *p; + unsigned int count; + struct uart_port *port = &qe_port->port; + struct circ_buf *xmit = &port->state->xmit; + + bdp = qe_port->rx_cur; + + /* Handle xon/xoff */ + if (port->x_char) { + /* Pick next descriptor and fill from buffer */ + bdp = qe_port->tx_cur; + + p = qe2cpu_addr(bdp->buf, qe_port); + + *p++ = port->x_char; + out_be16(&bdp->length, 1); + setbits16(&bdp->status, BD_SC_READY); + /* Get next BD. */ + if (in_be16(&bdp->status) & BD_SC_WRAP) + bdp = qe_port->tx_bd_base; + else + bdp++; + qe_port->tx_cur = bdp; + + port->icount.tx++; + port->x_char = 0; + return 1; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + qe_uart_stop_tx(port); + return 0; + } + + /* Pick next descriptor and fill from buffer */ + bdp = qe_port->tx_cur; + + while (!(in_be16(&bdp->status) & BD_SC_READY) && + (xmit->tail != xmit->head)) { + count = 0; + p = qe2cpu_addr(bdp->buf, qe_port); + while (count < qe_port->tx_fifosize) { + *p++ = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + count++; + if (xmit->head == xmit->tail) + break; + } + + out_be16(&bdp->length, count); + setbits16(&bdp->status, BD_SC_READY); + + /* Get next BD. */ + if (in_be16(&bdp->status) & BD_SC_WRAP) + bdp = qe_port->tx_bd_base; + else + bdp++; + } + qe_port->tx_cur = bdp; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) { + /* The kernel buffer is empty, so turn off TX interrupts. We + don't need to be told when the QE is finished transmitting + the data. */ + qe_uart_stop_tx(port); + return 0; + } + + return 1; +} + +/* + * Start transmitting data + * + * This function will start transmitting any available data, if the port + * isn't already transmitting data. + */ +static void qe_uart_start_tx(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + + /* If we currently are transmitting, then just return */ + if (in_be16(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX) + return; + + /* Otherwise, pump the port and start transmission */ + if (qe_uart_tx_pump(qe_port)) + setbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_TX); +} + +/* + * Stop transmitting data + */ +static void qe_uart_stop_rx(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + + clrbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_RX); +} + +/* + * Enable status change interrupts + * + * We don't support status change interrupts, but we need to define this + * function otherwise the kernel will panic. + */ +static void qe_uart_enable_ms(struct uart_port *port) +{ +} + +/* Start or stop sending break signal + * + * This function controls the sending of a break signal. If break_state=1, + * then we start sending a break signal. If break_state=0, then we stop + * sending the break signal. + */ +static void qe_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + + if (break_state) + ucc_slow_stop_tx(qe_port->us_private); + else + ucc_slow_restart_tx(qe_port->us_private); +} + +/* ISR helper function for receiving character. + * + * This function is called by the ISR to handling receiving characters + */ +static void qe_uart_int_rx(struct uart_qe_port *qe_port) +{ + int i; + unsigned char ch, *cp; + struct uart_port *port = &qe_port->port; + struct tty_struct *tty = port->state->port.tty; + struct qe_bd *bdp; + u16 status; + unsigned int flg; + + /* Just loop through the closed BDs and copy the characters into + * the buffer. + */ + bdp = qe_port->rx_cur; + while (1) { + status = in_be16(&bdp->status); + + /* If this one is empty, then we assume we've read them all */ + if (status & BD_SC_EMPTY) + break; + + /* get number of characters, and check space in RX buffer */ + i = in_be16(&bdp->length); + + /* If we don't have enough room in RX buffer for the entire BD, + * then we try later, which will be the next RX interrupt. + */ + if (tty_buffer_request_room(tty, i) < i) { + dev_dbg(port->dev, "ucc-uart: no room in RX buffer\n"); + return; + } + + /* get pointer */ + cp = qe2cpu_addr(bdp->buf, qe_port); + + /* loop through the buffer */ + while (i-- > 0) { + ch = *cp++; + port->icount.rx++; + flg = TTY_NORMAL; + + if (!i && status & + (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV)) + goto handle_error; + if (uart_handle_sysrq_char(port, ch)) + continue; + +error_return: + tty_insert_flip_char(tty, ch, flg); + + } + + /* This BD is ready to be used again. Clear status. get next */ + clrsetbits_be16(&bdp->status, BD_SC_BR | BD_SC_FR | BD_SC_PR | + BD_SC_OV | BD_SC_ID, BD_SC_EMPTY); + if (in_be16(&bdp->status) & BD_SC_WRAP) + bdp = qe_port->rx_bd_base; + else + bdp++; + + } + + /* Write back buffer pointer */ + qe_port->rx_cur = bdp; + + /* Activate BH processing */ + tty_flip_buffer_push(tty); + + return; + + /* Error processing */ + +handle_error: + /* Statistics */ + if (status & BD_SC_BR) + port->icount.brk++; + if (status & BD_SC_PR) + port->icount.parity++; + if (status & BD_SC_FR) + port->icount.frame++; + if (status & BD_SC_OV) + port->icount.overrun++; + + /* Mask out ignored conditions */ + status &= port->read_status_mask; + + /* Handle the remaining ones */ + if (status & BD_SC_BR) + flg = TTY_BREAK; + else if (status & BD_SC_PR) + flg = TTY_PARITY; + else if (status & BD_SC_FR) + flg = TTY_FRAME; + + /* Overrun does not affect the current character ! */ + if (status & BD_SC_OV) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); +#ifdef SUPPORT_SYSRQ + port->sysrq = 0; +#endif + goto error_return; +} + +/* Interrupt handler + * + * This interrupt handler is called after a BD is processed. + */ +static irqreturn_t qe_uart_int(int irq, void *data) +{ + struct uart_qe_port *qe_port = (struct uart_qe_port *) data; + struct ucc_slow __iomem *uccp = qe_port->uccp; + u16 events; + + /* Clear the interrupts */ + events = in_be16(&uccp->ucce); + out_be16(&uccp->ucce, events); + + if (events & UCC_UART_UCCE_BRKE) + uart_handle_break(&qe_port->port); + + if (events & UCC_UART_UCCE_RX) + qe_uart_int_rx(qe_port); + + if (events & UCC_UART_UCCE_TX) + qe_uart_tx_pump(qe_port); + + return events ? IRQ_HANDLED : IRQ_NONE; +} + +/* Initialize buffer descriptors + * + * This function initializes all of the RX and TX buffer descriptors. + */ +static void qe_uart_initbd(struct uart_qe_port *qe_port) +{ + int i; + void *bd_virt; + struct qe_bd *bdp; + + /* Set the physical address of the host memory buffers in the buffer + * descriptors, and the virtual address for us to work with. + */ + bd_virt = qe_port->bd_virt; + bdp = qe_port->rx_bd_base; + qe_port->rx_cur = qe_port->rx_bd_base; + for (i = 0; i < (qe_port->rx_nrfifos - 1); i++) { + out_be16(&bdp->status, BD_SC_EMPTY | BD_SC_INTRPT); + out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); + out_be16(&bdp->length, 0); + bd_virt += qe_port->rx_fifosize; + bdp++; + } + + /* */ + out_be16(&bdp->status, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT); + out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); + out_be16(&bdp->length, 0); + + /* Set the physical address of the host memory + * buffers in the buffer descriptors, and the + * virtual address for us to work with. + */ + bd_virt = qe_port->bd_virt + + L1_CACHE_ALIGN(qe_port->rx_nrfifos * qe_port->rx_fifosize); + qe_port->tx_cur = qe_port->tx_bd_base; + bdp = qe_port->tx_bd_base; + for (i = 0; i < (qe_port->tx_nrfifos - 1); i++) { + out_be16(&bdp->status, BD_SC_INTRPT); + out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); + out_be16(&bdp->length, 0); + bd_virt += qe_port->tx_fifosize; + bdp++; + } + + /* Loopback requires the preamble bit to be set on the first TX BD */ +#ifdef LOOPBACK + setbits16(&qe_port->tx_cur->status, BD_SC_P); +#endif + + out_be16(&bdp->status, BD_SC_WRAP | BD_SC_INTRPT); + out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port)); + out_be16(&bdp->length, 0); +} + +/* + * Initialize a UCC for UART. + * + * This function configures a given UCC to be used as a UART device. Basic + * UCC initialization is handled in qe_uart_request_port(). This function + * does all the UART-specific stuff. + */ +static void qe_uart_init_ucc(struct uart_qe_port *qe_port) +{ + u32 cecr_subblock; + struct ucc_slow __iomem *uccp = qe_port->uccp; + struct ucc_uart_pram *uccup = qe_port->uccup; + + unsigned int i; + + /* First, disable TX and RX in the UCC */ + ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX); + + /* Program the UCC UART parameter RAM */ + out_8(&uccup->common.rbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); + out_8(&uccup->common.tbmr, UCC_BMR_GBL | UCC_BMR_BO_BE); + out_be16(&uccup->common.mrblr, qe_port->rx_fifosize); + out_be16(&uccup->maxidl, 0x10); + out_be16(&uccup->brkcr, 1); + out_be16(&uccup->parec, 0); + out_be16(&uccup->frmec, 0); + out_be16(&uccup->nosec, 0); + out_be16(&uccup->brkec, 0); + out_be16(&uccup->uaddr[0], 0); + out_be16(&uccup->uaddr[1], 0); + out_be16(&uccup->toseq, 0); + for (i = 0; i < 8; i++) + out_be16(&uccup->cchars[i], 0xC000); + out_be16(&uccup->rccm, 0xc0ff); + + /* Configure the GUMR registers for UART */ + if (soft_uart) { + /* Soft-UART requires a 1X multiplier for TX */ + clrsetbits_be32(&uccp->gumr_l, + UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK | + UCC_SLOW_GUMR_L_RDCR_MASK, + UCC_SLOW_GUMR_L_MODE_UART | UCC_SLOW_GUMR_L_TDCR_1 | + UCC_SLOW_GUMR_L_RDCR_16); + + clrsetbits_be32(&uccp->gumr_h, UCC_SLOW_GUMR_H_RFW, + UCC_SLOW_GUMR_H_TRX | UCC_SLOW_GUMR_H_TTX); + } else { + clrsetbits_be32(&uccp->gumr_l, + UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK | + UCC_SLOW_GUMR_L_RDCR_MASK, + UCC_SLOW_GUMR_L_MODE_UART | UCC_SLOW_GUMR_L_TDCR_16 | + UCC_SLOW_GUMR_L_RDCR_16); + + clrsetbits_be32(&uccp->gumr_h, + UCC_SLOW_GUMR_H_TRX | UCC_SLOW_GUMR_H_TTX, + UCC_SLOW_GUMR_H_RFW); + } + +#ifdef LOOPBACK + clrsetbits_be32(&uccp->gumr_l, UCC_SLOW_GUMR_L_DIAG_MASK, + UCC_SLOW_GUMR_L_DIAG_LOOP); + clrsetbits_be32(&uccp->gumr_h, + UCC_SLOW_GUMR_H_CTSP | UCC_SLOW_GUMR_H_RSYN, + UCC_SLOW_GUMR_H_CDS); +#endif + + /* Disable rx interrupts and clear all pending events. */ + out_be16(&uccp->uccm, 0); + out_be16(&uccp->ucce, 0xffff); + out_be16(&uccp->udsr, 0x7e7e); + + /* Initialize UPSMR */ + out_be16(&uccp->upsmr, 0); + + if (soft_uart) { + out_be16(&uccup->supsmr, 0x30); + out_be16(&uccup->res92, 0); + out_be32(&uccup->rx_state, 0); + out_be32(&uccup->rx_cnt, 0); + out_8(&uccup->rx_bitmark, 0); + out_8(&uccup->rx_length, 10); + out_be32(&uccup->dump_ptr, 0x4000); + out_8(&uccup->rx_temp_dlst_qe, 0); + out_be32(&uccup->rx_frame_rem, 0); + out_8(&uccup->rx_frame_rem_size, 0); + /* Soft-UART requires TX to be 1X */ + out_8(&uccup->tx_mode, + UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1); + out_be16(&uccup->tx_state, 0); + out_8(&uccup->resD4, 0); + out_be16(&uccup->resD5, 0); + + /* Set UART mode. + * Enable receive and transmit. + */ + + /* From the microcode errata: + * 1.GUMR_L register, set mode=0010 (QMC). + * 2.Set GUMR_H[17] bit. (UART/AHDLC mode). + * 3.Set GUMR_H[19:20] (Transparent mode) + * 4.Clear GUMR_H[26] (RFW) + * ... + * 6.Receiver must use 16x over sampling + */ + clrsetbits_be32(&uccp->gumr_l, + UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK | + UCC_SLOW_GUMR_L_RDCR_MASK, + UCC_SLOW_GUMR_L_MODE_QMC | UCC_SLOW_GUMR_L_TDCR_16 | + UCC_SLOW_GUMR_L_RDCR_16); + + clrsetbits_be32(&uccp->gumr_h, + UCC_SLOW_GUMR_H_RFW | UCC_SLOW_GUMR_H_RSYN, + UCC_SLOW_GUMR_H_SUART | UCC_SLOW_GUMR_H_TRX | + UCC_SLOW_GUMR_H_TTX | UCC_SLOW_GUMR_H_TFL); + +#ifdef LOOPBACK + clrsetbits_be32(&uccp->gumr_l, UCC_SLOW_GUMR_L_DIAG_MASK, + UCC_SLOW_GUMR_L_DIAG_LOOP); + clrbits32(&uccp->gumr_h, UCC_SLOW_GUMR_H_CTSP | + UCC_SLOW_GUMR_H_CDS); +#endif + + cecr_subblock = ucc_slow_get_qe_cr_subblock(qe_port->ucc_num); + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + QE_CR_PROTOCOL_UNSPECIFIED, 0); + } else { + cecr_subblock = ucc_slow_get_qe_cr_subblock(qe_port->ucc_num); + qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock, + QE_CR_PROTOCOL_UART, 0); + } +} + +/* + * Initialize the port. + */ +static int qe_uart_startup(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + int ret; + + /* + * If we're using Soft-UART mode, then we need to make sure the + * firmware has been uploaded first. + */ + if (soft_uart && !firmware_loaded) { + dev_err(port->dev, "Soft-UART firmware not uploaded\n"); + return -ENODEV; + } + + qe_uart_initbd(qe_port); + qe_uart_init_ucc(qe_port); + + /* Install interrupt handler. */ + ret = request_irq(port->irq, qe_uart_int, IRQF_SHARED, "ucc-uart", + qe_port); + if (ret) { + dev_err(port->dev, "could not claim IRQ %u\n", port->irq); + return ret; + } + + /* Startup rx-int */ + setbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_RX); + ucc_slow_enable(qe_port->us_private, COMM_DIR_RX_AND_TX); + + return 0; +} + +/* + * Shutdown the port. + */ +static void qe_uart_shutdown(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + struct ucc_slow __iomem *uccp = qe_port->uccp; + unsigned int timeout = 20; + + /* Disable RX and TX */ + + /* Wait for all the BDs marked sent */ + while (!qe_uart_tx_empty(port)) { + if (!--timeout) { + dev_warn(port->dev, "shutdown timeout\n"); + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(2); + } + + if (qe_port->wait_closing) { + /* Wait a bit longer */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(qe_port->wait_closing); + } + + /* Stop uarts */ + ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX); + clrbits16(&uccp->uccm, UCC_UART_UCCE_TX | UCC_UART_UCCE_RX); + + /* Shut them really down and reinit buffer descriptors */ + ucc_slow_graceful_stop_tx(qe_port->us_private); + qe_uart_initbd(qe_port); + + free_irq(port->irq, qe_port); +} + +/* + * Set the serial port parameters. + */ +static void qe_uart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + struct ucc_slow __iomem *uccp = qe_port->uccp; + unsigned int baud; + unsigned long flags; + u16 upsmr = in_be16(&uccp->upsmr); + struct ucc_uart_pram __iomem *uccup = qe_port->uccup; + u16 supsmr = in_be16(&uccup->supsmr); + u8 char_length = 2; /* 1 + CL + PEN + 1 + SL */ + + /* Character length programmed into the mode register is the + * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, + * 1 or 2 stop bits, minus 1. + * The value 'bits' counts this for us. + */ + + /* byte size */ + upsmr &= UCC_UART_UPSMR_CL_MASK; + supsmr &= UCC_UART_SUPSMR_CL_MASK; + + switch (termios->c_cflag & CSIZE) { + case CS5: + upsmr |= UCC_UART_UPSMR_CL_5; + supsmr |= UCC_UART_SUPSMR_CL_5; + char_length += 5; + break; + case CS6: + upsmr |= UCC_UART_UPSMR_CL_6; + supsmr |= UCC_UART_SUPSMR_CL_6; + char_length += 6; + break; + case CS7: + upsmr |= UCC_UART_UPSMR_CL_7; + supsmr |= UCC_UART_SUPSMR_CL_7; + char_length += 7; + break; + default: /* case CS8 */ + upsmr |= UCC_UART_UPSMR_CL_8; + supsmr |= UCC_UART_SUPSMR_CL_8; + char_length += 8; + break; + } + + /* If CSTOPB is set, we want two stop bits */ + if (termios->c_cflag & CSTOPB) { + upsmr |= UCC_UART_UPSMR_SL; + supsmr |= UCC_UART_SUPSMR_SL; + char_length++; /* + SL */ + } + + if (termios->c_cflag & PARENB) { + upsmr |= UCC_UART_UPSMR_PEN; + supsmr |= UCC_UART_SUPSMR_PEN; + char_length++; /* + PEN */ + + if (!(termios->c_cflag & PARODD)) { + upsmr &= ~(UCC_UART_UPSMR_RPM_MASK | + UCC_UART_UPSMR_TPM_MASK); + upsmr |= UCC_UART_UPSMR_RPM_EVEN | + UCC_UART_UPSMR_TPM_EVEN; + supsmr &= ~(UCC_UART_SUPSMR_RPM_MASK | + UCC_UART_SUPSMR_TPM_MASK); + supsmr |= UCC_UART_SUPSMR_RPM_EVEN | + UCC_UART_SUPSMR_TPM_EVEN; + } + } + + /* + * Set up parity check flag + */ + port->read_status_mask = BD_SC_EMPTY | BD_SC_OV; + if (termios->c_iflag & INPCK) + port->read_status_mask |= BD_SC_FR | BD_SC_PR; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= BD_SC_BR; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= BD_SC_PR | BD_SC_FR; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= BD_SC_BR; + /* + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= BD_SC_OV; + } + /* + * !!! ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + port->read_status_mask &= ~BD_SC_EMPTY; + + baud = uart_get_baud_rate(port, termios, old, 0, 115200); + + /* Do we really need a spinlock here? */ + spin_lock_irqsave(&port->lock, flags); + + out_be16(&uccp->upsmr, upsmr); + if (soft_uart) { + out_be16(&uccup->supsmr, supsmr); + out_8(&uccup->rx_length, char_length); + + /* Soft-UART requires a 1X multiplier for TX */ + qe_setbrg(qe_port->us_info.rx_clock, baud, 16); + qe_setbrg(qe_port->us_info.tx_clock, baud, 1); + } else { + qe_setbrg(qe_port->us_info.rx_clock, baud, 16); + qe_setbrg(qe_port->us_info.tx_clock, baud, 16); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * Return a pointer to a string that describes what kind of port this is. + */ +static const char *qe_uart_type(struct uart_port *port) +{ + return "QE"; +} + +/* + * Allocate any memory and I/O resources required by the port. + */ +static int qe_uart_request_port(struct uart_port *port) +{ + int ret; + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + struct ucc_slow_info *us_info = &qe_port->us_info; + struct ucc_slow_private *uccs; + unsigned int rx_size, tx_size; + void *bd_virt; + dma_addr_t bd_dma_addr = 0; + + ret = ucc_slow_init(us_info, &uccs); + if (ret) { + dev_err(port->dev, "could not initialize UCC%u\n", + qe_port->ucc_num); + return ret; + } + + qe_port->us_private = uccs; + qe_port->uccp = uccs->us_regs; + qe_port->uccup = (struct ucc_uart_pram *) uccs->us_pram; + qe_port->rx_bd_base = uccs->rx_bd; + qe_port->tx_bd_base = uccs->tx_bd; + + /* + * Allocate the transmit and receive data buffers. + */ + + rx_size = L1_CACHE_ALIGN(qe_port->rx_nrfifos * qe_port->rx_fifosize); + tx_size = L1_CACHE_ALIGN(qe_port->tx_nrfifos * qe_port->tx_fifosize); + + bd_virt = dma_alloc_coherent(port->dev, rx_size + tx_size, &bd_dma_addr, + GFP_KERNEL); + if (!bd_virt) { + dev_err(port->dev, "could not allocate buffer descriptors\n"); + return -ENOMEM; + } + + qe_port->bd_virt = bd_virt; + qe_port->bd_dma_addr = bd_dma_addr; + qe_port->bd_size = rx_size + tx_size; + + qe_port->rx_buf = bd_virt; + qe_port->tx_buf = qe_port->rx_buf + rx_size; + + return 0; +} + +/* + * Configure the port. + * + * We say we're a CPM-type port because that's mostly true. Once the device + * is configured, this driver operates almost identically to the CPM serial + * driver. + */ +static void qe_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_CPM; + qe_uart_request_port(port); + } +} + +/* + * Release any memory and I/O resources that were allocated in + * qe_uart_request_port(). + */ +static void qe_uart_release_port(struct uart_port *port) +{ + struct uart_qe_port *qe_port = + container_of(port, struct uart_qe_port, port); + struct ucc_slow_private *uccs = qe_port->us_private; + + dma_free_coherent(port->dev, qe_port->bd_size, qe_port->bd_virt, + qe_port->bd_dma_addr); + + ucc_slow_free(uccs); +} + +/* + * Verify that the data in serial_struct is suitable for this device. + */ +static int qe_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM) + return -EINVAL; + + if (ser->irq < 0 || ser->irq >= nr_irqs) + return -EINVAL; + + if (ser->baud_base < 9600) + return -EINVAL; + + return 0; +} +/* UART operations + * + * Details on these functions can be found in Documentation/serial/driver + */ +static struct uart_ops qe_uart_pops = { + .tx_empty = qe_uart_tx_empty, + .set_mctrl = qe_uart_set_mctrl, + .get_mctrl = qe_uart_get_mctrl, + .stop_tx = qe_uart_stop_tx, + .start_tx = qe_uart_start_tx, + .stop_rx = qe_uart_stop_rx, + .enable_ms = qe_uart_enable_ms, + .break_ctl = qe_uart_break_ctl, + .startup = qe_uart_startup, + .shutdown = qe_uart_shutdown, + .set_termios = qe_uart_set_termios, + .type = qe_uart_type, + .release_port = qe_uart_release_port, + .request_port = qe_uart_request_port, + .config_port = qe_uart_config_port, + .verify_port = qe_uart_verify_port, +}; + +/* + * Obtain the SOC model number and revision level + * + * This function parses the device tree to obtain the SOC model. It then + * reads the SVR register to the revision. + * + * The device tree stores the SOC model two different ways. + * + * The new way is: + * + * cpu@0 { + * compatible = "PowerPC,8323"; + * device_type = "cpu"; + * ... + * + * + * The old way is: + * PowerPC,8323@0 { + * device_type = "cpu"; + * ... + * + * This code first checks the new way, and then the old way. + */ +static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l) +{ + struct device_node *np; + const char *soc_string; + unsigned int svr; + unsigned int soc; + + /* Find the CPU node */ + np = of_find_node_by_type(NULL, "cpu"); + if (!np) + return 0; + /* Find the compatible property */ + soc_string = of_get_property(np, "compatible", NULL); + if (!soc_string) + /* No compatible property, so try the name. */ + soc_string = np->name; + + /* Extract the SOC number from the "PowerPC," string */ + if ((sscanf(soc_string, "PowerPC,%u", &soc) != 1) || !soc) + return 0; + + /* Get the revision from the SVR */ + svr = mfspr(SPRN_SVR); + *rev_h = (svr >> 4) & 0xf; + *rev_l = svr & 0xf; + + return soc; +} + +/* + * requst_firmware_nowait() callback function + * + * This function is called by the kernel when a firmware is made available, + * or if it times out waiting for the firmware. + */ +static void uart_firmware_cont(const struct firmware *fw, void *context) +{ + struct qe_firmware *firmware; + struct device *dev = context; + int ret; + + if (!fw) { + dev_err(dev, "firmware not found\n"); + return; + } + + firmware = (struct qe_firmware *) fw->data; + + if (firmware->header.length != fw->size) { + dev_err(dev, "invalid firmware\n"); + goto out; + } + + ret = qe_upload_firmware(firmware); + if (ret) { + dev_err(dev, "could not load firmware\n"); + goto out; + } + + firmware_loaded = 1; + out: + release_firmware(fw); +} + +static int ucc_uart_probe(struct platform_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = ofdev->dev.of_node; + const unsigned int *iprop; /* Integer OF properties */ + const char *sprop; /* String OF properties */ + struct uart_qe_port *qe_port = NULL; + struct resource res; + int ret; + + /* + * Determine if we need Soft-UART mode + */ + if (of_find_property(np, "soft-uart", NULL)) { + dev_dbg(&ofdev->dev, "using Soft-UART mode\n"); + soft_uart = 1; + } + + /* + * If we are using Soft-UART, determine if we need to upload the + * firmware, too. + */ + if (soft_uart) { + struct qe_firmware_info *qe_fw_info; + + qe_fw_info = qe_get_firmware_info(); + + /* Check if the firmware has been uploaded. */ + if (qe_fw_info && strstr(qe_fw_info->id, "Soft-UART")) { + firmware_loaded = 1; + } else { + char filename[32]; + unsigned int soc; + unsigned int rev_h; + unsigned int rev_l; + + soc = soc_info(&rev_h, &rev_l); + if (!soc) { + dev_err(&ofdev->dev, "unknown CPU model\n"); + return -ENXIO; + } + sprintf(filename, "fsl_qe_ucode_uart_%u_%u%u.bin", + soc, rev_h, rev_l); + + dev_info(&ofdev->dev, "waiting for firmware %s\n", + filename); + + /* + * We call request_firmware_nowait instead of + * request_firmware so that the driver can load and + * initialize the ports without holding up the rest of + * the kernel. If hotplug support is enabled in the + * kernel, then we use it. + */ + ret = request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, filename, &ofdev->dev, + GFP_KERNEL, &ofdev->dev, uart_firmware_cont); + if (ret) { + dev_err(&ofdev->dev, + "could not load firmware %s\n", + filename); + return ret; + } + } + } + + qe_port = kzalloc(sizeof(struct uart_qe_port), GFP_KERNEL); + if (!qe_port) { + dev_err(&ofdev->dev, "can't allocate QE port structure\n"); + return -ENOMEM; + } + + /* Search for IRQ and mapbase */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + dev_err(&ofdev->dev, "missing 'reg' property in device tree\n"); + kfree(qe_port); + return ret; + } + if (!res.start) { + dev_err(&ofdev->dev, "invalid 'reg' property in device tree\n"); + kfree(qe_port); + return -EINVAL; + } + qe_port->port.mapbase = res.start; + + /* Get the UCC number (device ID) */ + /* UCCs are numbered 1-7 */ + iprop = of_get_property(np, "cell-index", NULL); + if (!iprop) { + iprop = of_get_property(np, "device-id", NULL); + if (!iprop) { + kfree(qe_port); + dev_err(&ofdev->dev, "UCC is unspecified in " + "device tree\n"); + return -EINVAL; + } + } + + if ((*iprop < 1) || (*iprop > UCC_MAX_NUM)) { + dev_err(&ofdev->dev, "no support for UCC%u\n", *iprop); + kfree(qe_port); + return -ENODEV; + } + qe_port->ucc_num = *iprop - 1; + + /* + * In the future, we should not require the BRG to be specified in the + * device tree. If no clock-source is specified, then just pick a BRG + * to use. This requires a new QE library function that manages BRG + * assignments. + */ + + sprop = of_get_property(np, "rx-clock-name", NULL); + if (!sprop) { + dev_err(&ofdev->dev, "missing rx-clock-name in device tree\n"); + kfree(qe_port); + return -ENODEV; + } + + qe_port->us_info.rx_clock = qe_clock_source(sprop); + if ((qe_port->us_info.rx_clock < QE_BRG1) || + (qe_port->us_info.rx_clock > QE_BRG16)) { + dev_err(&ofdev->dev, "rx-clock-name must be a BRG for UART\n"); + kfree(qe_port); + return -ENODEV; + } + +#ifdef LOOPBACK + /* In internal loopback mode, TX and RX must use the same clock */ + qe_port->us_info.tx_clock = qe_port->us_info.rx_clock; +#else + sprop = of_get_property(np, "tx-clock-name", NULL); + if (!sprop) { + dev_err(&ofdev->dev, "missing tx-clock-name in device tree\n"); + kfree(qe_port); + return -ENODEV; + } + qe_port->us_info.tx_clock = qe_clock_source(sprop); +#endif + if ((qe_port->us_info.tx_clock < QE_BRG1) || + (qe_port->us_info.tx_clock > QE_BRG16)) { + dev_err(&ofdev->dev, "tx-clock-name must be a BRG for UART\n"); + kfree(qe_port); + return -ENODEV; + } + + /* Get the port number, numbered 0-3 */ + iprop = of_get_property(np, "port-number", NULL); + if (!iprop) { + dev_err(&ofdev->dev, "missing port-number in device tree\n"); + kfree(qe_port); + return -EINVAL; + } + qe_port->port.line = *iprop; + if (qe_port->port.line >= UCC_MAX_UART) { + dev_err(&ofdev->dev, "port-number must be 0-%u\n", + UCC_MAX_UART - 1); + kfree(qe_port); + return -EINVAL; + } + + qe_port->port.irq = irq_of_parse_and_map(np, 0); + if (qe_port->port.irq == NO_IRQ) { + dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n", + qe_port->ucc_num + 1); + kfree(qe_port); + return -EINVAL; + } + + /* + * Newer device trees have an "fsl,qe" compatible property for the QE + * node, but we still need to support older device trees. + */ + np = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!np) { + np = of_find_node_by_type(NULL, "qe"); + if (!np) { + dev_err(&ofdev->dev, "could not find 'qe' node\n"); + kfree(qe_port); + return -EINVAL; + } + } + + iprop = of_get_property(np, "brg-frequency", NULL); + if (!iprop) { + dev_err(&ofdev->dev, + "missing brg-frequency in device tree\n"); + kfree(qe_port); + return -EINVAL; + } + + if (*iprop) + qe_port->port.uartclk = *iprop; + else { + /* + * Older versions of U-Boot do not initialize the brg-frequency + * property, so in this case we assume the BRG frequency is + * half the QE bus frequency. + */ + iprop = of_get_property(np, "bus-frequency", NULL); + if (!iprop) { + dev_err(&ofdev->dev, + "missing QE bus-frequency in device tree\n"); + kfree(qe_port); + return -EINVAL; + } + if (*iprop) + qe_port->port.uartclk = *iprop / 2; + else { + dev_err(&ofdev->dev, + "invalid QE bus-frequency in device tree\n"); + kfree(qe_port); + return -EINVAL; + } + } + + spin_lock_init(&qe_port->port.lock); + qe_port->np = np; + qe_port->port.dev = &ofdev->dev; + qe_port->port.ops = &qe_uart_pops; + qe_port->port.iotype = UPIO_MEM; + + qe_port->tx_nrfifos = TX_NUM_FIFO; + qe_port->tx_fifosize = TX_BUF_SIZE; + qe_port->rx_nrfifos = RX_NUM_FIFO; + qe_port->rx_fifosize = RX_BUF_SIZE; + + qe_port->wait_closing = UCC_WAIT_CLOSING; + qe_port->port.fifosize = 512; + qe_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + + qe_port->us_info.ucc_num = qe_port->ucc_num; + qe_port->us_info.regs = (phys_addr_t) res.start; + qe_port->us_info.irq = qe_port->port.irq; + + qe_port->us_info.rx_bd_ring_len = qe_port->rx_nrfifos; + qe_port->us_info.tx_bd_ring_len = qe_port->tx_nrfifos; + + /* Make sure ucc_slow_init() initializes both TX and RX */ + qe_port->us_info.init_tx = 1; + qe_port->us_info.init_rx = 1; + + /* Add the port to the uart sub-system. This will cause + * qe_uart_config_port() to be called, so the us_info structure must + * be initialized. + */ + ret = uart_add_one_port(&ucc_uart_driver, &qe_port->port); + if (ret) { + dev_err(&ofdev->dev, "could not add /dev/ttyQE%u\n", + qe_port->port.line); + kfree(qe_port); + return ret; + } + + dev_set_drvdata(&ofdev->dev, qe_port); + + dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n", + qe_port->ucc_num + 1, qe_port->port.line); + + /* Display the mknod command for this device */ + dev_dbg(&ofdev->dev, "mknod command is 'mknod /dev/ttyQE%u c %u %u'\n", + qe_port->port.line, SERIAL_QE_MAJOR, + SERIAL_QE_MINOR + qe_port->port.line); + + return 0; +} + +static int ucc_uart_remove(struct platform_device *ofdev) +{ + struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); + + dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line); + + uart_remove_one_port(&ucc_uart_driver, &qe_port->port); + + dev_set_drvdata(&ofdev->dev, NULL); + kfree(qe_port); + + return 0; +} + +static struct of_device_id ucc_uart_match[] = { + { + .type = "serial", + .compatible = "ucc_uart", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ucc_uart_match); + +static struct of_platform_driver ucc_uart_of_driver = { + .driver = { + .name = "ucc_uart", + .owner = THIS_MODULE, + .of_match_table = ucc_uart_match, + }, + .probe = ucc_uart_probe, + .remove = ucc_uart_remove, +}; + +static int __init ucc_uart_init(void) +{ + int ret; + + printk(KERN_INFO "Freescale QUICC Engine UART device driver\n"); +#ifdef LOOPBACK + printk(KERN_INFO "ucc-uart: Using loopback mode\n"); +#endif + + ret = uart_register_driver(&ucc_uart_driver); + if (ret) { + printk(KERN_ERR "ucc-uart: could not register UART driver\n"); + return ret; + } + + ret = of_register_platform_driver(&ucc_uart_of_driver); + if (ret) + printk(KERN_ERR + "ucc-uart: could not register platform driver\n"); + + return ret; +} + +static void __exit ucc_uart_exit(void) +{ + printk(KERN_INFO + "Freescale QUICC Engine UART device driver unloading\n"); + + of_unregister_platform_driver(&ucc_uart_of_driver); + uart_unregister_driver(&ucc_uart_driver); +} + +module_init(ucc_uart_init); +module_exit(ucc_uart_exit); + +MODULE_DESCRIPTION("Freescale QUICC Engine (QE) UART"); +MODULE_AUTHOR("Timur Tabi "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_QE_MAJOR); + diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c new file mode 100644 index 000000000000..3beb6ab4fa68 --- /dev/null +++ b/drivers/tty/serial/vr41xx_siu.c @@ -0,0 +1,978 @@ +/* + * Driver for NEC VR4100 series Serial Interface Unit. + * + * Copyright (C) 2004-2008 Yoichi Yuasa + * + * Based on drivers/serial/8250.c, by Russell King. + * + * 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 + */ + +#if defined(CONFIG_SERIAL_VR41XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SIU_BAUD_BASE 1152000 +#define SIU_MAJOR 204 +#define SIU_MINOR_BASE 82 + +#define RX_MAX_COUNT 256 +#define TX_MAX_COUNT 15 + +#define SIUIRSEL 0x08 + #define TMICMODE 0x20 + #define TMICTX 0x10 + #define IRMSEL 0x0c + #define IRMSEL_HP 0x08 + #define IRMSEL_TEMIC 0x04 + #define IRMSEL_SHARP 0x00 + #define IRUSESEL 0x02 + #define SIRSEL 0x01 + +static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = { + [0 ... SIU_PORTS_MAX-1] = { + .lock = __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock), + .irq = -1, + }, +}; + +#ifdef CONFIG_SERIAL_VR41XX_CONSOLE +static uint8_t lsr_break_flag[SIU_PORTS_MAX]; +#endif + +#define siu_read(port, offset) readb((port)->membase + (offset)) +#define siu_write(port, offset, value) writeb((value), (port)->membase + (offset)) + +void vr41xx_select_siu_interface(siu_interface_t interface) +{ + struct uart_port *port; + unsigned long flags; + uint8_t irsel; + + port = &siu_uart_ports[0]; + + spin_lock_irqsave(&port->lock, flags); + + irsel = siu_read(port, SIUIRSEL); + if (interface == SIU_INTERFACE_IRDA) + irsel |= SIRSEL; + else + irsel &= ~SIRSEL; + siu_write(port, SIUIRSEL, irsel); + + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL_GPL(vr41xx_select_siu_interface); + +void vr41xx_use_irda(irda_use_t use) +{ + struct uart_port *port; + unsigned long flags; + uint8_t irsel; + + port = &siu_uart_ports[0]; + + spin_lock_irqsave(&port->lock, flags); + + irsel = siu_read(port, SIUIRSEL); + if (use == FIR_USE_IRDA) + irsel |= IRUSESEL; + else + irsel &= ~IRUSESEL; + siu_write(port, SIUIRSEL, irsel); + + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL_GPL(vr41xx_use_irda); + +void vr41xx_select_irda_module(irda_module_t module, irda_speed_t speed) +{ + struct uart_port *port; + unsigned long flags; + uint8_t irsel; + + port = &siu_uart_ports[0]; + + spin_lock_irqsave(&port->lock, flags); + + irsel = siu_read(port, SIUIRSEL); + irsel &= ~(IRMSEL | TMICTX | TMICMODE); + switch (module) { + case SHARP_IRDA: + irsel |= IRMSEL_SHARP; + break; + case TEMIC_IRDA: + irsel |= IRMSEL_TEMIC | TMICMODE; + if (speed == IRDA_TX_4MBPS) + irsel |= TMICTX; + break; + case HP_IRDA: + irsel |= IRMSEL_HP; + break; + default: + break; + } + siu_write(port, SIUIRSEL, irsel); + + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL_GPL(vr41xx_select_irda_module); + +static inline void siu_clear_fifo(struct uart_port *port) +{ + siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO); + siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + siu_write(port, UART_FCR, 0); +} + +static inline unsigned long siu_port_size(struct uart_port *port) +{ + switch (port->type) { + case PORT_VR41XX_SIU: + return 11UL; + case PORT_VR41XX_DSIU: + return 8UL; + } + + return 0; +} + +static inline unsigned int siu_check_type(struct uart_port *port) +{ + if (port->line == 0) + return PORT_VR41XX_SIU; + if (port->line == 1 && port->irq != -1) + return PORT_VR41XX_DSIU; + + return PORT_UNKNOWN; +} + +static inline const char *siu_type_name(struct uart_port *port) +{ + switch (port->type) { + case PORT_VR41XX_SIU: + return "SIU"; + case PORT_VR41XX_DSIU: + return "DSIU"; + } + + return NULL; +} + +static unsigned int siu_tx_empty(struct uart_port *port) +{ + uint8_t lsr; + + lsr = siu_read(port, UART_LSR); + if (lsr & UART_LSR_TEMT) + return TIOCSER_TEMT; + + return 0; +} + +static void siu_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + uint8_t mcr = 0; + + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + siu_write(port, UART_MCR, mcr); +} + +static unsigned int siu_get_mctrl(struct uart_port *port) +{ + uint8_t msr; + unsigned int mctrl = 0; + + msr = siu_read(port, UART_MSR); + if (msr & UART_MSR_DCD) + mctrl |= TIOCM_CAR; + if (msr & UART_MSR_RI) + mctrl |= TIOCM_RNG; + if (msr & UART_MSR_DSR) + mctrl |= TIOCM_DSR; + if (msr & UART_MSR_CTS) + mctrl |= TIOCM_CTS; + + return mctrl; +} + +static void siu_stop_tx(struct uart_port *port) +{ + unsigned long flags; + uint8_t ier; + + spin_lock_irqsave(&port->lock, flags); + + ier = siu_read(port, UART_IER); + ier &= ~UART_IER_THRI; + siu_write(port, UART_IER, ier); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void siu_start_tx(struct uart_port *port) +{ + unsigned long flags; + uint8_t ier; + + spin_lock_irqsave(&port->lock, flags); + + ier = siu_read(port, UART_IER); + ier |= UART_IER_THRI; + siu_write(port, UART_IER, ier); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void siu_stop_rx(struct uart_port *port) +{ + unsigned long flags; + uint8_t ier; + + spin_lock_irqsave(&port->lock, flags); + + ier = siu_read(port, UART_IER); + ier &= ~UART_IER_RLSI; + siu_write(port, UART_IER, ier); + + port->read_status_mask &= ~UART_LSR_DR; + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void siu_enable_ms(struct uart_port *port) +{ + unsigned long flags; + uint8_t ier; + + spin_lock_irqsave(&port->lock, flags); + + ier = siu_read(port, UART_IER); + ier |= UART_IER_MSI; + siu_write(port, UART_IER, ier); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void siu_break_ctl(struct uart_port *port, int ctl) +{ + unsigned long flags; + uint8_t lcr; + + spin_lock_irqsave(&port->lock, flags); + + lcr = siu_read(port, UART_LCR); + if (ctl == -1) + lcr |= UART_LCR_SBC; + else + lcr &= ~UART_LCR_SBC; + siu_write(port, UART_LCR, lcr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static inline void receive_chars(struct uart_port *port, uint8_t *status) +{ + struct tty_struct *tty; + uint8_t lsr, ch; + char flag; + int max_count = RX_MAX_COUNT; + + tty = port->state->port.tty; + lsr = *status; + + do { + ch = siu_read(port, UART_RX); + port->icount.rx++; + flag = TTY_NORMAL; + +#ifdef CONFIG_SERIAL_VR41XX_CONSOLE + lsr |= lsr_break_flag[port->line]; + lsr_break_flag[port->line] = 0; +#endif + if (unlikely(lsr & (UART_LSR_BI | UART_LSR_FE | + UART_LSR_PE | UART_LSR_OE))) { + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + port->icount.brk++; + + if (uart_handle_break(port)) + goto ignore_char; + } + + if (lsr & UART_LSR_FE) + port->icount.frame++; + if (lsr & UART_LSR_PE) + port->icount.parity++; + if (lsr & UART_LSR_OE) + port->icount.overrun++; + + lsr &= port->read_status_mask; + if (lsr & UART_LSR_BI) + flag = TTY_BREAK; + if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); + + ignore_char: + lsr = siu_read(port, UART_LSR); + } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); + + tty_flip_buffer_push(tty); + + *status = lsr; +} + +static inline void check_modem_status(struct uart_port *port) +{ + uint8_t msr; + + msr = siu_read(port, UART_MSR); + if ((msr & UART_MSR_ANY_DELTA) == 0) + return; + if (msr & UART_MSR_DDCD) + uart_handle_dcd_change(port, msr & UART_MSR_DCD); + if (msr & UART_MSR_TERI) + port->icount.rng++; + if (msr & UART_MSR_DDSR) + port->icount.dsr++; + if (msr & UART_MSR_DCTS) + uart_handle_cts_change(port, msr & UART_MSR_CTS); + + wake_up_interruptible(&port->state->port.delta_msr_wait); +} + +static inline void transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit; + int max_count = TX_MAX_COUNT; + + xmit = &port->state->xmit; + + if (port->x_char) { + siu_write(port, UART_TX, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + siu_stop_tx(port); + return; + } + + do { + siu_write(port, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (max_count-- > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + siu_stop_tx(port); +} + +static irqreturn_t siu_interrupt(int irq, void *dev_id) +{ + struct uart_port *port; + uint8_t iir, lsr; + + port = (struct uart_port *)dev_id; + + iir = siu_read(port, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + + lsr = siu_read(port, UART_LSR); + if (lsr & UART_LSR_DR) + receive_chars(port, &lsr); + + check_modem_status(port); + + if (lsr & UART_LSR_THRE) + transmit_chars(port); + + return IRQ_HANDLED; +} + +static int siu_startup(struct uart_port *port) +{ + int retval; + + if (port->membase == NULL) + return -ENODEV; + + siu_clear_fifo(port); + + (void)siu_read(port, UART_LSR); + (void)siu_read(port, UART_RX); + (void)siu_read(port, UART_IIR); + (void)siu_read(port, UART_MSR); + + if (siu_read(port, UART_LSR) == 0xff) + return -ENODEV; + + retval = request_irq(port->irq, siu_interrupt, 0, siu_type_name(port), port); + if (retval) + return retval; + + if (port->type == PORT_VR41XX_DSIU) + vr41xx_enable_dsiuint(DSIUINT_ALL); + + siu_write(port, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irq(&port->lock); + siu_set_mctrl(port, port->mctrl); + spin_unlock_irq(&port->lock); + + siu_write(port, UART_IER, UART_IER_RLSI | UART_IER_RDI); + + (void)siu_read(port, UART_LSR); + (void)siu_read(port, UART_RX); + (void)siu_read(port, UART_IIR); + (void)siu_read(port, UART_MSR); + + return 0; +} + +static void siu_shutdown(struct uart_port *port) +{ + unsigned long flags; + uint8_t lcr; + + siu_write(port, UART_IER, 0); + + spin_lock_irqsave(&port->lock, flags); + + port->mctrl &= ~TIOCM_OUT2; + siu_set_mctrl(port, port->mctrl); + + spin_unlock_irqrestore(&port->lock, flags); + + lcr = siu_read(port, UART_LCR); + lcr &= ~UART_LCR_SBC; + siu_write(port, UART_LCR, lcr); + + siu_clear_fifo(port); + + (void)siu_read(port, UART_RX); + + if (port->type == PORT_VR41XX_DSIU) + vr41xx_disable_dsiuint(DSIUINT_ALL); + + free_irq(port->irq, port); +} + +static void siu_set_termios(struct uart_port *port, struct ktermios *new, + struct ktermios *old) +{ + tcflag_t c_cflag, c_iflag; + uint8_t lcr, fcr, ier; + unsigned int baud, quot; + unsigned long flags; + + c_cflag = new->c_cflag; + switch (c_cflag & CSIZE) { + case CS5: + lcr = UART_LCR_WLEN5; + break; + case CS6: + lcr = UART_LCR_WLEN6; + break; + case CS7: + lcr = UART_LCR_WLEN7; + break; + default: + lcr = UART_LCR_WLEN8; + break; + } + + if (c_cflag & CSTOPB) + lcr |= UART_LCR_STOP; + if (c_cflag & PARENB) + lcr |= UART_LCR_PARITY; + if ((c_cflag & PARODD) != PARODD) + lcr |= UART_LCR_EPAR; + if (c_cflag & CMSPAR) + lcr |= UART_LCR_SPAR; + + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16); + quot = uart_get_divisor(port, baud); + + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10; + + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, c_cflag, baud); + + c_iflag = new->c_iflag; + + port->read_status_mask = UART_LSR_THRE | UART_LSR_OE | UART_LSR_DR; + if (c_iflag & INPCK) + port->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_LSR_BI; + + port->ignore_status_mask = 0; + if (c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (c_iflag & IGNBRK) { + port->ignore_status_mask |= UART_LSR_BI; + if (c_iflag & IGNPAR) + port->ignore_status_mask |= UART_LSR_OE; + } + + if ((c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_LSR_DR; + + ier = siu_read(port, UART_IER); + ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(port, c_cflag)) + ier |= UART_IER_MSI; + siu_write(port, UART_IER, ier); + + siu_write(port, UART_LCR, lcr | UART_LCR_DLAB); + + siu_write(port, UART_DLL, (uint8_t)quot); + siu_write(port, UART_DLM, (uint8_t)(quot >> 8)); + + siu_write(port, UART_LCR, lcr); + + siu_write(port, UART_FCR, fcr); + + siu_set_mctrl(port, port->mctrl); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void siu_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) +{ + switch (state) { + case 0: + switch (port->type) { + case PORT_VR41XX_SIU: + vr41xx_supply_clock(SIU_CLOCK); + break; + case PORT_VR41XX_DSIU: + vr41xx_supply_clock(DSIU_CLOCK); + break; + } + break; + case 3: + switch (port->type) { + case PORT_VR41XX_SIU: + vr41xx_mask_clock(SIU_CLOCK); + break; + case PORT_VR41XX_DSIU: + vr41xx_mask_clock(DSIU_CLOCK); + break; + } + break; + } +} + +static const char *siu_type(struct uart_port *port) +{ + return siu_type_name(port); +} + +static void siu_release_port(struct uart_port *port) +{ + unsigned long size; + + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + size = siu_port_size(port); + release_mem_region(port->mapbase, size); +} + +static int siu_request_port(struct uart_port *port) +{ + unsigned long size; + struct resource *res; + + size = siu_port_size(port); + res = request_mem_region(port->mapbase, size, siu_type_name(port)); + if (res == NULL) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, size); + if (port->membase == NULL) { + release_resource(res); + return -ENOMEM; + } + } + + return 0; +} + +static void siu_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = siu_check_type(port); + (void)siu_request_port(port); + } +} + +static int siu_verify_port(struct uart_port *port, struct serial_struct *serial) +{ + if (port->type != PORT_VR41XX_SIU && port->type != PORT_VR41XX_DSIU) + return -EINVAL; + if (port->irq != serial->irq) + return -EINVAL; + if (port->iotype != serial->io_type) + return -EINVAL; + if (port->mapbase != (unsigned long)serial->iomem_base) + return -EINVAL; + + return 0; +} + +static struct uart_ops siu_uart_ops = { + .tx_empty = siu_tx_empty, + .set_mctrl = siu_set_mctrl, + .get_mctrl = siu_get_mctrl, + .stop_tx = siu_stop_tx, + .start_tx = siu_start_tx, + .stop_rx = siu_stop_rx, + .enable_ms = siu_enable_ms, + .break_ctl = siu_break_ctl, + .startup = siu_startup, + .shutdown = siu_shutdown, + .set_termios = siu_set_termios, + .pm = siu_pm, + .type = siu_type, + .release_port = siu_release_port, + .request_port = siu_request_port, + .config_port = siu_config_port, + .verify_port = siu_verify_port, +}; + +static int siu_init_ports(struct platform_device *pdev) +{ + struct uart_port *port; + struct resource *res; + int *type = pdev->dev.platform_data; + int i; + + if (!type) + return 0; + + port = siu_uart_ports; + for (i = 0; i < SIU_PORTS_MAX; i++) { + port->type = type[i]; + if (port->type == PORT_UNKNOWN) + continue; + port->irq = platform_get_irq(pdev, i); + port->uartclk = SIU_BAUD_BASE * 16; + port->fifosize = 16; + port->regshift = 0; + port->iotype = UPIO_MEM; + port->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; + port->line = i; + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + port->mapbase = res->start; + port++; + } + + return i; +} + +#ifdef CONFIG_SERIAL_VR41XX_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static void wait_for_xmitr(struct uart_port *port) +{ + int timeout = 10000; + uint8_t lsr, msr; + + do { + lsr = siu_read(port, UART_LSR); + if (lsr & UART_LSR_BI) + lsr_break_flag[port->line] = UART_LSR_BI; + + if ((lsr & BOTH_EMPTY) == BOTH_EMPTY) + break; + } while (timeout-- > 0); + + if (port->flags & UPF_CONS_FLOW) { + timeout = 1000000; + + do { + msr = siu_read(port, UART_MSR); + if ((msr & UART_MSR_CTS) != 0) + break; + } while (timeout-- > 0); + } +} + +static void siu_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + siu_write(port, UART_TX, ch); +} + +static void siu_console_write(struct console *con, const char *s, unsigned count) +{ + struct uart_port *port; + uint8_t ier; + + port = &siu_uart_ports[con->index]; + + ier = siu_read(port, UART_IER); + siu_write(port, UART_IER, 0); + + uart_console_write(port, s, count, siu_console_putchar); + + wait_for_xmitr(port); + siu_write(port, UART_IER, ier); +} + +static int __init siu_console_setup(struct console *con, char *options) +{ + struct uart_port *port; + int baud = 9600; + int parity = 'n'; + int bits = 8; + int flow = 'n'; + + if (con->index >= SIU_PORTS_MAX) + con->index = 0; + + port = &siu_uart_ports[con->index]; + if (port->membase == NULL) { + if (port->mapbase == 0) + return -ENODEV; + port->membase = ioremap(port->mapbase, siu_port_size(port)); + } + + if (port->type == PORT_VR41XX_SIU) + vr41xx_select_siu_interface(SIU_INTERFACE_RS232C); + + if (options != NULL) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, con, baud, parity, bits, flow); +} + +static struct uart_driver siu_uart_driver; + +static struct console siu_console = { + .name = "ttyVR", + .write = siu_console_write, + .device = uart_console_device, + .setup = siu_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &siu_uart_driver, +}; + +static int __devinit siu_console_init(void) +{ + struct uart_port *port; + int i; + + for (i = 0; i < SIU_PORTS_MAX; i++) { + port = &siu_uart_ports[i]; + port->ops = &siu_uart_ops; + } + + register_console(&siu_console); + + return 0; +} + +console_initcall(siu_console_init); + +void __init vr41xx_siu_early_setup(struct uart_port *port) +{ + if (port->type == PORT_UNKNOWN) + return; + + siu_uart_ports[port->line].line = port->line; + siu_uart_ports[port->line].type = port->type; + siu_uart_ports[port->line].uartclk = SIU_BAUD_BASE * 16; + siu_uart_ports[port->line].mapbase = port->mapbase; + siu_uart_ports[port->line].mapbase = port->mapbase; + siu_uart_ports[port->line].ops = &siu_uart_ops; +} + +#define SERIAL_VR41XX_CONSOLE &siu_console +#else +#define SERIAL_VR41XX_CONSOLE NULL +#endif + +static struct uart_driver siu_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "SIU", + .dev_name = "ttyVR", + .major = SIU_MAJOR, + .minor = SIU_MINOR_BASE, + .cons = SERIAL_VR41XX_CONSOLE, +}; + +static int __devinit siu_probe(struct platform_device *dev) +{ + struct uart_port *port; + int num, i, retval; + + num = siu_init_ports(dev); + if (num <= 0) + return -ENODEV; + + siu_uart_driver.nr = num; + retval = uart_register_driver(&siu_uart_driver); + if (retval) + return retval; + + for (i = 0; i < num; i++) { + port = &siu_uart_ports[i]; + port->ops = &siu_uart_ops; + port->dev = &dev->dev; + + retval = uart_add_one_port(&siu_uart_driver, port); + if (retval < 0) { + port->dev = NULL; + break; + } + } + + if (i == 0 && retval < 0) { + uart_unregister_driver(&siu_uart_driver); + return retval; + } + + return 0; +} + +static int __devexit siu_remove(struct platform_device *dev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < siu_uart_driver.nr; i++) { + port = &siu_uart_ports[i]; + if (port->dev == &dev->dev) { + uart_remove_one_port(&siu_uart_driver, port); + port->dev = NULL; + } + } + + uart_unregister_driver(&siu_uart_driver); + + return 0; +} + +static int siu_suspend(struct platform_device *dev, pm_message_t state) +{ + struct uart_port *port; + int i; + + for (i = 0; i < siu_uart_driver.nr; i++) { + port = &siu_uart_ports[i]; + if ((port->type == PORT_VR41XX_SIU || + port->type == PORT_VR41XX_DSIU) && port->dev == &dev->dev) + uart_suspend_port(&siu_uart_driver, port); + + } + + return 0; +} + +static int siu_resume(struct platform_device *dev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < siu_uart_driver.nr; i++) { + port = &siu_uart_ports[i]; + if ((port->type == PORT_VR41XX_SIU || + port->type == PORT_VR41XX_DSIU) && port->dev == &dev->dev) + uart_resume_port(&siu_uart_driver, port); + } + + return 0; +} + +static struct platform_driver siu_device_driver = { + .probe = siu_probe, + .remove = __devexit_p(siu_remove), + .suspend = siu_suspend, + .resume = siu_resume, + .driver = { + .name = "SIU", + .owner = THIS_MODULE, + }, +}; + +static int __init vr41xx_siu_init(void) +{ + return platform_driver_register(&siu_device_driver); +} + +static void __exit vr41xx_siu_exit(void) +{ + platform_driver_unregister(&siu_device_driver); +} + +module_init(vr41xx_siu_init); +module_exit(vr41xx_siu_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:SIU"); diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c new file mode 100644 index 000000000000..322bf56c0d89 --- /dev/null +++ b/drivers/tty/serial/vt8500_serial.c @@ -0,0 +1,648 @@ +/* + * drivers/serial/vt8500_serial.c + * + * Copyright (C) 2010 Alexey Charkov + * + * Based on msm_serial.c, which is: + * Copyright (C) 2007 Google, Inc. + * Author: Robert Love + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +# define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * UART Register offsets + */ + +#define VT8500_URTDR 0x0000 /* Transmit data */ +#define VT8500_URRDR 0x0004 /* Receive data */ +#define VT8500_URDIV 0x0008 /* Clock/Baud rate divisor */ +#define VT8500_URLCR 0x000C /* Line control */ +#define VT8500_URICR 0x0010 /* IrDA control */ +#define VT8500_URIER 0x0014 /* Interrupt enable */ +#define VT8500_URISR 0x0018 /* Interrupt status */ +#define VT8500_URUSR 0x001c /* UART status */ +#define VT8500_URFCR 0x0020 /* FIFO control */ +#define VT8500_URFIDX 0x0024 /* FIFO index */ +#define VT8500_URBKR 0x0028 /* Break signal count */ +#define VT8500_URTOD 0x002c /* Time out divisor */ +#define VT8500_TXFIFO 0x1000 /* Transmit FIFO (16x8) */ +#define VT8500_RXFIFO 0x1020 /* Receive FIFO (16x10) */ + +/* + * Interrupt enable and status bits + */ + +#define TXDE (1 << 0) /* Tx Data empty */ +#define RXDF (1 << 1) /* Rx Data full */ +#define TXFAE (1 << 2) /* Tx FIFO almost empty */ +#define TXFE (1 << 3) /* Tx FIFO empty */ +#define RXFAF (1 << 4) /* Rx FIFO almost full */ +#define RXFF (1 << 5) /* Rx FIFO full */ +#define TXUDR (1 << 6) /* Tx underrun */ +#define RXOVER (1 << 7) /* Rx overrun */ +#define PER (1 << 8) /* Parity error */ +#define FER (1 << 9) /* Frame error */ +#define TCTS (1 << 10) /* Toggle of CTS */ +#define RXTOUT (1 << 11) /* Rx timeout */ +#define BKDONE (1 << 12) /* Break signal done */ +#define ERR (1 << 13) /* AHB error response */ + +#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) +#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) + +struct vt8500_port { + struct uart_port uart; + char name[16]; + struct clk *clk; + unsigned int ier; +}; + +static inline void vt8500_write(struct uart_port *port, unsigned int val, + unsigned int off) +{ + writel(val, port->membase + off); +} + +static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off) +{ + return readl(port->membase + off); +} + +static void vt8500_stop_tx(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier &= ~TX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void vt8500_stop_rx(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier &= ~RX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void vt8500_enable_ms(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier |= TCTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void handle_rx(struct uart_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + if (!tty) { + /* Discard data: no tty available */ + int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8; + u16 ch; + while (count--) + ch = readw(port->membase + VT8500_RXFIFO); + return; + } + + /* + * Handle overrun + */ + if ((vt8500_read(port, VT8500_URISR) & RXOVER)) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + /* and now the main RX loop */ + while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) { + unsigned int c; + char flag = TTY_NORMAL; + + c = readw(port->membase + VT8500_RXFIFO) & 0x3ff; + + /* Mask conditions we're ignorning. */ + c &= ~port->read_status_mask; + + if (c & FER) { + port->icount.frame++; + flag = TTY_FRAME; + } else if (c & PER) { + port->icount.parity++; + flag = TTY_PARITY; + } + port->icount.rx++; + + if (!uart_handle_sysrq_char(port, c)) + tty_insert_flip_char(tty, c, flag); + } + + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static void handle_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + writeb(port->x_char, port->membase + VT8500_TXFIFO); + port->icount.tx++; + port->x_char = 0; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + vt8500_stop_tx(port); + return; + } + + while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) { + if (uart_circ_empty(xmit)) + break; + + writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + vt8500_stop_tx(port); +} + +static void vt8500_start_tx(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier &= ~TX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); + handle_tx(port); + vt8500_port->ier |= TX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void handle_delta_cts(struct uart_port *port) +{ + port->icount.cts++; + wake_up_interruptible(&port->state->port.delta_msr_wait); +} + +static irqreturn_t vt8500_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long isr; + + spin_lock(&port->lock); + isr = vt8500_read(port, VT8500_URISR); + + /* Acknowledge active status bits */ + vt8500_write(port, isr, VT8500_URISR); + + if (isr & RX_FIFO_INTS) + handle_rx(port); + if (isr & TX_FIFO_INTS) + handle_tx(port); + if (isr & TCTS) + handle_delta_cts(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int vt8500_tx_empty(struct uart_port *port) +{ + return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ? + TIOCSER_TEMT : 0; +} + +static unsigned int vt8500_get_mctrl(struct uart_port *port) +{ + unsigned int usr; + + usr = vt8500_read(port, VT8500_URUSR); + if (usr & (1 << 4)) + return TIOCM_CTS; + else + return 0; +} + +static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void vt8500_break_ctl(struct uart_port *port, int break_ctl) +{ + if (break_ctl) + vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9), + VT8500_URLCR); +} + +static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud) +{ + unsigned long div; + unsigned int loops = 1000; + + div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff); + + if (unlikely((baud < 900) || (baud > 921600))) + div |= 7; + else + div |= (921600 / baud) - 1; + + while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops) + cpu_relax(); + vt8500_write(port, div, VT8500_URDIV); + + return baud; +} + +static int vt8500_startup(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + int ret; + + snprintf(vt8500_port->name, sizeof(vt8500_port->name), + "vt8500_serial%d", port->line); + + ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH, + vt8500_port->name, port); + if (unlikely(ret)) + return ret; + + vt8500_write(port, 0x03, VT8500_URLCR); /* enable TX & RX */ + + return 0; +} + +static void vt8500_shutdown(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + + vt8500_port->ier = 0; + + /* disable interrupts and FIFOs */ + vt8500_write(&vt8500_port->uart, 0, VT8500_URIER); + vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR); + free_irq(port->irq, port); +} + +static void vt8500_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + unsigned long flags; + unsigned int baud, lcr; + unsigned int loops = 1000; + + spin_lock_irqsave(&port->lock, flags); + + /* calculate and set baud rate */ + baud = uart_get_baud_rate(port, termios, old, 900, 921600); + baud = vt8500_set_baud_rate(port, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* calculate parity */ + lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR); + lcr &= ~((1 << 5) | (1 << 4)); + if (termios->c_cflag & PARENB) { + lcr |= (1 << 4); + termios->c_cflag &= ~CMSPAR; + if (termios->c_cflag & PARODD) + lcr |= (1 << 5); + } + + /* calculate bits per char */ + lcr &= ~(1 << 2); + switch (termios->c_cflag & CSIZE) { + case CS7: + break; + case CS8: + default: + lcr |= (1 << 2); + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + break; + } + + /* calculate stop bits */ + lcr &= ~(1 << 3); + if (termios->c_cflag & CSTOPB) + lcr |= (1 << 3); + + /* set parity, bits per char, and stop bit */ + vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR); + + /* Configure status bits to ignore based on termio flags. */ + port->read_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->read_status_mask = FER | PER; + + uart_update_timeout(port, termios->c_cflag, baud); + + /* Reset FIFOs */ + vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR); + while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc) + && --loops) + cpu_relax(); + + /* Every possible FIFO-related interrupt */ + vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS; + + /* + * CTS flow control + */ + if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag)) + vt8500_port->ier |= TCTS; + + vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR); + vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *vt8500_type(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + return vt8500_port->name; +} + +static void vt8500_release_port(struct uart_port *port) +{ +} + +static int vt8500_request_port(struct uart_port *port) +{ + return 0; +} + +static void vt8500_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_VT8500; +} + +static int vt8500_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500)) + return -EINVAL; + if (unlikely(port->irq != ser->irq)) + return -EINVAL; + return 0; +} + +static struct vt8500_port *vt8500_uart_ports[4]; +static struct uart_driver vt8500_uart_driver; + +#ifdef CONFIG_SERIAL_VT8500_CONSOLE + +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = vt8500_read(port, VT8500_URFIDX); + + if (--tmout == 0) + break; + udelay(1); + } while (status & 0x10); +} + +static void vt8500_console_putchar(struct uart_port *port, int c) +{ + wait_for_xmitr(port); + writeb(c, port->membase + VT8500_TXFIFO); +} + +static void vt8500_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index]; + unsigned long ier; + + BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr); + + ier = vt8500_read(&vt8500_port->uart, VT8500_URIER); + vt8500_write(&vt8500_port->uart, VT8500_URIER, 0); + + uart_console_write(&vt8500_port->uart, s, count, + vt8500_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and switch back to FIFO + */ + wait_for_xmitr(&vt8500_port->uart); + vt8500_write(&vt8500_port->uart, VT8500_URIER, ier); +} + +static int __init vt8500_console_setup(struct console *co, char *options) +{ + struct vt8500_port *vt8500_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0)) + return -ENXIO; + + vt8500_port = vt8500_uart_ports[co->index]; + + if (!vt8500_port) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&vt8500_port->uart, + co, baud, parity, bits, flow); +} + +static struct console vt8500_console = { + .name = "ttyWMT", + .write = vt8500_console_write, + .device = uart_console_device, + .setup = vt8500_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &vt8500_uart_driver, +}; + +#define VT8500_CONSOLE (&vt8500_console) + +#else +#define VT8500_CONSOLE NULL +#endif + +static struct uart_ops vt8500_uart_pops = { + .tx_empty = vt8500_tx_empty, + .set_mctrl = vt8500_set_mctrl, + .get_mctrl = vt8500_get_mctrl, + .stop_tx = vt8500_stop_tx, + .start_tx = vt8500_start_tx, + .stop_rx = vt8500_stop_rx, + .enable_ms = vt8500_enable_ms, + .break_ctl = vt8500_break_ctl, + .startup = vt8500_startup, + .shutdown = vt8500_shutdown, + .set_termios = vt8500_set_termios, + .type = vt8500_type, + .release_port = vt8500_release_port, + .request_port = vt8500_request_port, + .config_port = vt8500_config_port, + .verify_port = vt8500_verify_port, +}; + +static struct uart_driver vt8500_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "vt8500_serial", + .dev_name = "ttyWMT", + .nr = 6, + .cons = VT8500_CONSOLE, +}; + +static int __init vt8500_serial_probe(struct platform_device *pdev) +{ + struct vt8500_port *vt8500_port; + struct resource *mmres, *irqres; + int ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mmres || !irqres) + return -ENODEV; + + 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; + vt8500_port->uart.irq = irqres->start; + vt8500_port->uart.fifosize = 16; + vt8500_port->uart.ops = &vt8500_uart_pops; + vt8500_port->uart.line = pdev->id; + vt8500_port->uart.dev = &pdev->dev; + vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; + vt8500_port->uart.uartclk = 24000000; + + snprintf(vt8500_port->name, sizeof(vt8500_port->name), + "VT8500 UART%d", pdev->id); + + vt8500_port->uart.membase = ioremap(mmres->start, + mmres->end - mmres->start + 1); + if (!vt8500_port->uart.membase) { + ret = -ENOMEM; + goto err; + } + + vt8500_uart_ports[pdev->id] = vt8500_port; + + uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); + + platform_set_drvdata(pdev, vt8500_port); + + return 0; + +err: + kfree(vt8500_port); + return ret; +} + +static int __devexit vt8500_serial_remove(struct platform_device *pdev) +{ + struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart); + kfree(vt8500_port); + + return 0; +} + +static struct platform_driver vt8500_platform_driver = { + .probe = vt8500_serial_probe, + .remove = vt8500_serial_remove, + .driver = { + .name = "vt8500_serial", + .owner = THIS_MODULE, + }, +}; + +static int __init vt8500_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&vt8500_uart_driver); + if (unlikely(ret)) + return ret; + + ret = platform_driver_register(&vt8500_platform_driver); + + if (unlikely(ret)) + uart_unregister_driver(&vt8500_uart_driver); + + return ret; +} + +static void __exit vt8500_serial_exit(void) +{ +#ifdef CONFIG_SERIAL_VT8500_CONSOLE + unregister_console(&vt8500_console); +#endif + platform_driver_unregister(&vt8500_platform_driver); + uart_unregister_driver(&vt8500_uart_driver); +} + +module_init(vt8500_serial_init); +module_exit(vt8500_serial_exit); + +MODULE_AUTHOR("Alexey Charkov "); +MODULE_DESCRIPTION("Driver for vt8500 serial device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c new file mode 100644 index 000000000000..1a7fd3e70315 --- /dev/null +++ b/drivers/tty/serial/zs.c @@ -0,0 +1,1304 @@ +/* + * zs.c: Serial port driver for IOASIC DECstations. + * + * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras. + * Derived from drivers/macintosh/macserial.c by Harald Koerfgen. + * + * DECstation changes + * Copyright (C) 1998-2000 Harald Koerfgen + * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki + * + * For the rest of the code the original Copyright applies: + * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * + * Note: for IOASIC systems the wiring is as follows: + * + * mouse/keyboard: + * DIN-7 MJ-4 signal SCC + * 2 1 TxD <- A.TxD + * 3 4 RxD -> A.RxD + * + * EIA-232/EIA-423: + * DB-25 MMJ-6 signal SCC + * 2 2 TxD <- B.TxD + * 3 5 RxD -> B.RxD + * 4 RTS <- ~A.RTS + * 5 CTS -> ~B.CTS + * 6 6 DSR -> ~A.SYNC + * 8 CD -> ~B.DCD + * 12 DSRS(DCE) -> ~A.CTS (*) + * 15 TxC -> B.TxC + * 17 RxC -> B.RxC + * 20 1 DTR <- ~A.DTR + * 22 RI -> ~A.DCD + * 23 DSRS(DTE) <- ~B.RTS + * + * (*) EIA-232 defines the signal at this pin to be SCD, while DSRS(DCE) + * is shared with DSRS(DTE) at pin 23. + * + * As you can immediately notice the wiring of the RTS, DTR and DSR signals + * is a bit odd. This makes the handling of port B unnecessarily + * complicated and prevents the use of some automatic modes of operation. + */ + +#if defined(CONFIG_SERIAL_ZS_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "zs.h" + + +MODULE_AUTHOR("Maciej W. Rozycki "); +MODULE_DESCRIPTION("DECstation Z85C30 serial driver"); +MODULE_LICENSE("GPL"); + + +static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; +static char zs_version[] __initdata = "0.10"; + +/* + * It would be nice to dynamically allocate everything that + * depends on ZS_NUM_SCCS, so we could support any number of + * Z85C30s, but for now... + */ +#define ZS_NUM_SCCS 2 /* Max # of ZS chips supported. */ +#define ZS_NUM_CHAN 2 /* 2 channels per chip. */ +#define ZS_CHAN_A 0 /* Index of the channel A. */ +#define ZS_CHAN_B 1 /* Index of the channel B. */ +#define ZS_CHAN_IO_SIZE 8 /* IOMEM space size. */ +#define ZS_CHAN_IO_STRIDE 4 /* Register alignment. */ +#define ZS_CHAN_IO_OFFSET 1 /* The SCC resides on the high byte + of the 16-bit IOBUS. */ +#define ZS_CLOCK 7372800 /* Z85C30 PCLK input clock rate. */ + +#define to_zport(uport) container_of(uport, struct zs_port, port) + +struct zs_parms { + resource_size_t scc[ZS_NUM_SCCS]; + int irq[ZS_NUM_SCCS]; +}; + +static struct zs_scc zs_sccs[ZS_NUM_SCCS]; + +static u8 zs_init_regs[ZS_NUM_REGS] __initdata = { + 0, /* write 0 */ + PAR_SPEC, /* write 1 */ + 0, /* write 2 */ + 0, /* write 3 */ + X16CLK | SB1, /* write 4 */ + 0, /* write 5 */ + 0, 0, 0, /* write 6, 7, 8 */ + MIE | DLC | NV, /* write 9 */ + NRZ, /* write 10 */ + TCBR | RCBR, /* write 11 */ + 0, 0, /* BRG time constant, write 12 + 13 */ + BRSRC | BRENABL, /* write 14 */ + 0, /* write 15 */ +}; + +/* + * Debugging. + */ +#undef ZS_DEBUG_REGS + + +/* + * Reading and writing Z85C30 registers. + */ +static void recovery_delay(void) +{ + udelay(2); +} + +static u8 read_zsreg(struct zs_port *zport, int reg) +{ + void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET; + u8 retval; + + if (reg != 0) { + writeb(reg & 0xf, control); + fast_iob(); + recovery_delay(); + } + retval = readb(control); + recovery_delay(); + return retval; +} + +static void write_zsreg(struct zs_port *zport, int reg, u8 value) +{ + void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET; + + if (reg != 0) { + writeb(reg & 0xf, control); + fast_iob(); recovery_delay(); + } + writeb(value, control); + fast_iob(); + recovery_delay(); + return; +} + +static u8 read_zsdata(struct zs_port *zport) +{ + void __iomem *data = zport->port.membase + + ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET; + u8 retval; + + retval = readb(data); + recovery_delay(); + return retval; +} + +static void write_zsdata(struct zs_port *zport, u8 value) +{ + void __iomem *data = zport->port.membase + + ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET; + + writeb(value, data); + fast_iob(); + recovery_delay(); + return; +} + +#ifdef ZS_DEBUG_REGS +void zs_dump(void) +{ + struct zs_port *zport; + int i, j; + + for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { + zport = &zs_sccs[i / ZS_NUM_CHAN].zport[i % ZS_NUM_CHAN]; + + if (!zport->scc) + continue; + + for (j = 0; j < 16; j++) + printk("W%-2d = 0x%02x\t", j, zport->regs[j]); + printk("\n"); + for (j = 0; j < 16; j++) + printk("R%-2d = 0x%02x\t", j, read_zsreg(zport, j)); + printk("\n\n"); + } +} +#endif + + +static void zs_spin_lock_cond_irq(spinlock_t *lock, int irq) +{ + if (irq) + spin_lock_irq(lock); + else + spin_lock(lock); +} + +static void zs_spin_unlock_cond_irq(spinlock_t *lock, int irq) +{ + if (irq) + spin_unlock_irq(lock); + else + spin_unlock(lock); +} + +static int zs_receive_drain(struct zs_port *zport) +{ + int loops = 10000; + + while ((read_zsreg(zport, R0) & Rx_CH_AV) && --loops) + read_zsdata(zport); + return loops; +} + +static int zs_transmit_drain(struct zs_port *zport, int irq) +{ + struct zs_scc *scc = zport->scc; + int loops = 10000; + + while (!(read_zsreg(zport, R0) & Tx_BUF_EMP) && --loops) { + zs_spin_unlock_cond_irq(&scc->zlock, irq); + udelay(2); + zs_spin_lock_cond_irq(&scc->zlock, irq); + } + return loops; +} + +static int zs_line_drain(struct zs_port *zport, int irq) +{ + struct zs_scc *scc = zport->scc; + int loops = 10000; + + while (!(read_zsreg(zport, R1) & ALL_SNT) && --loops) { + zs_spin_unlock_cond_irq(&scc->zlock, irq); + udelay(2); + zs_spin_lock_cond_irq(&scc->zlock, irq); + } + return loops; +} + + +static void load_zsregs(struct zs_port *zport, u8 *regs, int irq) +{ + /* Let the current transmission finish. */ + zs_line_drain(zport, irq); + /* Load 'em up. */ + write_zsreg(zport, R3, regs[3] & ~RxENABLE); + write_zsreg(zport, R5, regs[5] & ~TxENAB); + write_zsreg(zport, R4, regs[4]); + write_zsreg(zport, R9, regs[9]); + write_zsreg(zport, R1, regs[1]); + write_zsreg(zport, R2, regs[2]); + write_zsreg(zport, R10, regs[10]); + write_zsreg(zport, R14, regs[14] & ~BRENABL); + write_zsreg(zport, R11, regs[11]); + write_zsreg(zport, R12, regs[12]); + write_zsreg(zport, R13, regs[13]); + write_zsreg(zport, R14, regs[14]); + write_zsreg(zport, R15, regs[15]); + if (regs[3] & RxENABLE) + write_zsreg(zport, R3, regs[3]); + if (regs[5] & TxENAB) + write_zsreg(zport, R5, regs[5]); + return; +} + + +/* + * Status handling routines. + */ + +/* + * zs_tx_empty() -- get the transmitter empty status + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static unsigned int zs_tx_empty(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + unsigned long flags; + u8 status; + + spin_lock_irqsave(&scc->zlock, flags); + status = read_zsreg(zport, R1); + spin_unlock_irqrestore(&scc->zlock, flags); + + return status & ALL_SNT ? TIOCSER_TEMT : 0; +} + +static unsigned int zs_raw_get_ab_mctrl(struct zs_port *zport_a, + struct zs_port *zport_b) +{ + u8 status_a, status_b; + unsigned int mctrl; + + status_a = read_zsreg(zport_a, R0); + status_b = read_zsreg(zport_b, R0); + + mctrl = ((status_b & CTS) ? TIOCM_CTS : 0) | + ((status_b & DCD) ? TIOCM_CAR : 0) | + ((status_a & DCD) ? TIOCM_RNG : 0) | + ((status_a & SYNC_HUNT) ? TIOCM_DSR : 0); + + return mctrl; +} + +static unsigned int zs_raw_get_mctrl(struct zs_port *zport) +{ + struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; + + return zport != zport_a ? zs_raw_get_ab_mctrl(zport_a, zport) : 0; +} + +static unsigned int zs_raw_xor_mctrl(struct zs_port *zport) +{ + struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; + unsigned int mmask, mctrl, delta; + u8 mask_a, mask_b; + + if (zport == zport_a) + return 0; + + mask_a = zport_a->regs[15]; + mask_b = zport->regs[15]; + + mmask = ((mask_b & CTSIE) ? TIOCM_CTS : 0) | + ((mask_b & DCDIE) ? TIOCM_CAR : 0) | + ((mask_a & DCDIE) ? TIOCM_RNG : 0) | + ((mask_a & SYNCIE) ? TIOCM_DSR : 0); + + mctrl = zport->mctrl; + if (mmask) { + mctrl &= ~mmask; + mctrl |= zs_raw_get_ab_mctrl(zport_a, zport) & mmask; + } + + delta = mctrl ^ zport->mctrl; + if (delta) + zport->mctrl = mctrl; + + return delta; +} + +static unsigned int zs_get_mctrl(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + unsigned int mctrl; + + spin_lock(&scc->zlock); + mctrl = zs_raw_get_mctrl(zport); + spin_unlock(&scc->zlock); + + return mctrl; +} + +static void zs_set_mctrl(struct uart_port *uport, unsigned int mctrl) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; + u8 oldloop, newloop; + + spin_lock(&scc->zlock); + if (zport != zport_a) { + if (mctrl & TIOCM_DTR) + zport_a->regs[5] |= DTR; + else + zport_a->regs[5] &= ~DTR; + if (mctrl & TIOCM_RTS) + zport_a->regs[5] |= RTS; + else + zport_a->regs[5] &= ~RTS; + write_zsreg(zport_a, R5, zport_a->regs[5]); + } + + /* Rarely modified, so don't poke at hardware unless necessary. */ + oldloop = zport->regs[14]; + newloop = oldloop; + if (mctrl & TIOCM_LOOP) + newloop |= LOOPBAK; + else + newloop &= ~LOOPBAK; + if (newloop != oldloop) { + zport->regs[14] = newloop; + write_zsreg(zport, R14, zport->regs[14]); + } + spin_unlock(&scc->zlock); +} + +static void zs_raw_stop_tx(struct zs_port *zport) +{ + write_zsreg(zport, R0, RES_Tx_P); + zport->tx_stopped = 1; +} + +static void zs_stop_tx(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + + spin_lock(&scc->zlock); + zs_raw_stop_tx(zport); + spin_unlock(&scc->zlock); +} + +static void zs_raw_transmit_chars(struct zs_port *); + +static void zs_start_tx(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + + spin_lock(&scc->zlock); + if (zport->tx_stopped) { + zs_transmit_drain(zport, 0); + zport->tx_stopped = 0; + zs_raw_transmit_chars(zport); + } + spin_unlock(&scc->zlock); +} + +static void zs_stop_rx(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; + + spin_lock(&scc->zlock); + zport->regs[15] &= ~BRKIE; + zport->regs[1] &= ~(RxINT_MASK | TxINT_ENAB); + zport->regs[1] |= RxINT_DISAB; + + if (zport != zport_a) { + /* A-side DCD tracks RI and SYNC tracks DSR. */ + zport_a->regs[15] &= ~(DCDIE | SYNCIE); + write_zsreg(zport_a, R15, zport_a->regs[15]); + if (!(zport_a->regs[15] & BRKIE)) { + zport_a->regs[1] &= ~EXT_INT_ENAB; + write_zsreg(zport_a, R1, zport_a->regs[1]); + } + + /* This-side DCD tracks DCD and CTS tracks CTS. */ + zport->regs[15] &= ~(DCDIE | CTSIE); + zport->regs[1] &= ~EXT_INT_ENAB; + } else { + /* DCD tracks RI and SYNC tracks DSR for the B side. */ + if (!(zport->regs[15] & (DCDIE | SYNCIE))) + zport->regs[1] &= ~EXT_INT_ENAB; + } + + write_zsreg(zport, R15, zport->regs[15]); + write_zsreg(zport, R1, zport->regs[1]); + spin_unlock(&scc->zlock); +} + +static void zs_enable_ms(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; + + if (zport == zport_a) + return; + + spin_lock(&scc->zlock); + + /* Clear Ext interrupts if not being handled already. */ + if (!(zport_a->regs[1] & EXT_INT_ENAB)) + write_zsreg(zport_a, R0, RES_EXT_INT); + + /* A-side DCD tracks RI and SYNC tracks DSR. */ + zport_a->regs[1] |= EXT_INT_ENAB; + zport_a->regs[15] |= DCDIE | SYNCIE; + + /* This-side DCD tracks DCD and CTS tracks CTS. */ + zport->regs[15] |= DCDIE | CTSIE; + + zs_raw_xor_mctrl(zport); + + write_zsreg(zport_a, R1, zport_a->regs[1]); + write_zsreg(zport_a, R15, zport_a->regs[15]); + write_zsreg(zport, R15, zport->regs[15]); + spin_unlock(&scc->zlock); +} + +static void zs_break_ctl(struct uart_port *uport, int break_state) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + unsigned long flags; + + spin_lock_irqsave(&scc->zlock, flags); + if (break_state == -1) + zport->regs[5] |= SND_BRK; + else + zport->regs[5] &= ~SND_BRK; + write_zsreg(zport, R5, zport->regs[5]); + spin_unlock_irqrestore(&scc->zlock, flags); +} + + +/* + * Interrupt handling routines. + */ +#define Rx_BRK 0x0100 /* BREAK event software flag. */ +#define Rx_SYS 0x0200 /* SysRq event software flag. */ + +static void zs_receive_chars(struct zs_port *zport) +{ + struct uart_port *uport = &zport->port; + struct zs_scc *scc = zport->scc; + struct uart_icount *icount; + unsigned int avail, status, ch, flag; + int count; + + for (count = 16; count; count--) { + spin_lock(&scc->zlock); + avail = read_zsreg(zport, R0) & Rx_CH_AV; + spin_unlock(&scc->zlock); + if (!avail) + break; + + spin_lock(&scc->zlock); + status = read_zsreg(zport, R1) & (Rx_OVR | FRM_ERR | PAR_ERR); + ch = read_zsdata(zport); + spin_unlock(&scc->zlock); + + flag = TTY_NORMAL; + + icount = &uport->icount; + icount->rx++; + + /* Handle the null char got when BREAK is removed. */ + if (!ch) + status |= zport->tty_break; + if (unlikely(status & + (Rx_OVR | FRM_ERR | PAR_ERR | Rx_SYS | Rx_BRK))) { + zport->tty_break = 0; + + /* Reset the error indication. */ + if (status & (Rx_OVR | FRM_ERR | PAR_ERR)) { + spin_lock(&scc->zlock); + write_zsreg(zport, R0, ERR_RES); + spin_unlock(&scc->zlock); + } + + if (status & (Rx_SYS | Rx_BRK)) { + icount->brk++; + /* SysRq discards the null char. */ + if (status & Rx_SYS) + continue; + } else if (status & FRM_ERR) + icount->frame++; + else if (status & PAR_ERR) + icount->parity++; + if (status & Rx_OVR) + icount->overrun++; + + status &= uport->read_status_mask; + if (status & Rx_BRK) + flag = TTY_BREAK; + else if (status & FRM_ERR) + flag = TTY_FRAME; + else if (status & PAR_ERR) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(uport, ch)) + continue; + + uart_insert_char(uport, status, Rx_OVR, ch, flag); + } + + tty_flip_buffer_push(uport->state->port.tty); +} + +static void zs_raw_transmit_chars(struct zs_port *zport) +{ + struct circ_buf *xmit = &zport->port.state->xmit; + + /* XON/XOFF chars. */ + if (zport->port.x_char) { + write_zsdata(zport, zport->port.x_char); + zport->port.icount.tx++; + zport->port.x_char = 0; + return; + } + + /* If nothing to do or stopped or hardware stopped. */ + if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) { + zs_raw_stop_tx(zport); + return; + } + + /* Send char. */ + write_zsdata(zport, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + zport->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&zport->port); + + /* Are we are done? */ + if (uart_circ_empty(xmit)) + zs_raw_stop_tx(zport); +} + +static void zs_transmit_chars(struct zs_port *zport) +{ + struct zs_scc *scc = zport->scc; + + spin_lock(&scc->zlock); + zs_raw_transmit_chars(zport); + spin_unlock(&scc->zlock); +} + +static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) +{ + struct uart_port *uport = &zport->port; + struct zs_scc *scc = zport->scc; + unsigned int delta; + u8 status, brk; + + spin_lock(&scc->zlock); + + /* Get status from Read Register 0. */ + status = read_zsreg(zport, R0); + + if (zport->regs[15] & BRKIE) { + brk = status & BRK_ABRT; + if (brk && !zport->brk) { + spin_unlock(&scc->zlock); + if (uart_handle_break(uport)) + zport->tty_break = Rx_SYS; + else + zport->tty_break = Rx_BRK; + spin_lock(&scc->zlock); + } + zport->brk = brk; + } + + if (zport != zport_a) { + delta = zs_raw_xor_mctrl(zport); + spin_unlock(&scc->zlock); + + if (delta & TIOCM_CTS) + uart_handle_cts_change(uport, + zport->mctrl & TIOCM_CTS); + if (delta & TIOCM_CAR) + uart_handle_dcd_change(uport, + zport->mctrl & TIOCM_CAR); + if (delta & TIOCM_RNG) + uport->icount.dsr++; + if (delta & TIOCM_DSR) + uport->icount.rng++; + + if (delta) + wake_up_interruptible(&uport->state->port.delta_msr_wait); + + spin_lock(&scc->zlock); + } + + /* Clear the status condition... */ + write_zsreg(zport, R0, RES_EXT_INT); + + spin_unlock(&scc->zlock); +} + +/* + * This is the Z85C30 driver's generic interrupt routine. + */ +static irqreturn_t zs_interrupt(int irq, void *dev_id) +{ + struct zs_scc *scc = dev_id; + struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; + struct zs_port *zport_b = &scc->zport[ZS_CHAN_B]; + irqreturn_t status = IRQ_NONE; + u8 zs_intreg; + int count; + + /* + * NOTE: The read register 3, which holds the irq status, + * does so for both channels on each chip. Although + * the status value itself must be read from the A + * channel and is only valid when read from channel A. + * Yes... broken hardware... + */ + for (count = 16; count; count--) { + spin_lock(&scc->zlock); + zs_intreg = read_zsreg(zport_a, R3); + spin_unlock(&scc->zlock); + if (!zs_intreg) + break; + + /* + * We do not like losing characters, so we prioritise + * interrupt sources a little bit differently than + * the SCC would, was it allowed to. + */ + if (zs_intreg & CHBRxIP) + zs_receive_chars(zport_b); + if (zs_intreg & CHARxIP) + zs_receive_chars(zport_a); + if (zs_intreg & CHBEXT) + zs_status_handle(zport_b, zport_a); + if (zs_intreg & CHAEXT) + zs_status_handle(zport_a, zport_a); + if (zs_intreg & CHBTxIP) + zs_transmit_chars(zport_b); + if (zs_intreg & CHATxIP) + zs_transmit_chars(zport_a); + + status = IRQ_HANDLED; + } + + return status; +} + + +/* + * Finally, routines used to initialize the serial port. + */ +static int zs_startup(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + unsigned long flags; + int irq_guard; + int ret; + + irq_guard = atomic_add_return(1, &scc->irq_guard); + if (irq_guard == 1) { + ret = request_irq(zport->port.irq, zs_interrupt, + IRQF_SHARED, "scc", scc); + if (ret) { + atomic_add(-1, &scc->irq_guard); + printk(KERN_ERR "zs: can't get irq %d\n", + zport->port.irq); + return ret; + } + } + + spin_lock_irqsave(&scc->zlock, flags); + + /* Clear the receive FIFO. */ + zs_receive_drain(zport); + + /* Clear the interrupt registers. */ + write_zsreg(zport, R0, ERR_RES); + write_zsreg(zport, R0, RES_Tx_P); + /* But Ext only if not being handled already. */ + if (!(zport->regs[1] & EXT_INT_ENAB)) + write_zsreg(zport, R0, RES_EXT_INT); + + /* Finally, enable sequencing and interrupts. */ + zport->regs[1] &= ~RxINT_MASK; + zport->regs[1] |= RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB; + zport->regs[3] |= RxENABLE; + zport->regs[15] |= BRKIE; + write_zsreg(zport, R1, zport->regs[1]); + write_zsreg(zport, R3, zport->regs[3]); + write_zsreg(zport, R5, zport->regs[5]); + write_zsreg(zport, R15, zport->regs[15]); + + /* Record the current state of RR0. */ + zport->mctrl = zs_raw_get_mctrl(zport); + zport->brk = read_zsreg(zport, R0) & BRK_ABRT; + + zport->tx_stopped = 1; + + spin_unlock_irqrestore(&scc->zlock, flags); + + return 0; +} + +static void zs_shutdown(struct uart_port *uport) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + unsigned long flags; + int irq_guard; + + spin_lock_irqsave(&scc->zlock, flags); + + zport->regs[3] &= ~RxENABLE; + write_zsreg(zport, R5, zport->regs[5]); + write_zsreg(zport, R3, zport->regs[3]); + + spin_unlock_irqrestore(&scc->zlock, flags); + + irq_guard = atomic_add_return(-1, &scc->irq_guard); + if (!irq_guard) + free_irq(zport->port.irq, scc); +} + + +static void zs_reset(struct zs_port *zport) +{ + struct zs_scc *scc = zport->scc; + int irq; + unsigned long flags; + + spin_lock_irqsave(&scc->zlock, flags); + irq = !irqs_disabled_flags(flags); + if (!scc->initialised) { + /* Reset the pointer first, just in case... */ + read_zsreg(zport, R0); + /* And let the current transmission finish. */ + zs_line_drain(zport, irq); + write_zsreg(zport, R9, FHWRES); + udelay(10); + write_zsreg(zport, R9, 0); + scc->initialised = 1; + } + load_zsregs(zport, zport->regs, irq); + spin_unlock_irqrestore(&scc->zlock, flags); +} + +static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, + struct ktermios *old_termios) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + struct zs_port *zport_a = &scc->zport[ZS_CHAN_A]; + int irq; + unsigned int baud, brg; + unsigned long flags; + + spin_lock_irqsave(&scc->zlock, flags); + irq = !irqs_disabled_flags(flags); + + /* Byte size. */ + zport->regs[3] &= ~RxNBITS_MASK; + zport->regs[5] &= ~TxNBITS_MASK; + switch (termios->c_cflag & CSIZE) { + case CS5: + zport->regs[3] |= Rx5; + zport->regs[5] |= Tx5; + break; + case CS6: + zport->regs[3] |= Rx6; + zport->regs[5] |= Tx6; + break; + case CS7: + zport->regs[3] |= Rx7; + zport->regs[5] |= Tx7; + break; + case CS8: + default: + zport->regs[3] |= Rx8; + zport->regs[5] |= Tx8; + break; + } + + /* Parity and stop bits. */ + zport->regs[4] &= ~(XCLK_MASK | SB_MASK | PAR_ENA | PAR_EVEN); + if (termios->c_cflag & CSTOPB) + zport->regs[4] |= SB2; + else + zport->regs[4] |= SB1; + if (termios->c_cflag & PARENB) + zport->regs[4] |= PAR_ENA; + if (!(termios->c_cflag & PARODD)) + zport->regs[4] |= PAR_EVEN; + switch (zport->clk_mode) { + case 64: + zport->regs[4] |= X64CLK; + break; + case 32: + zport->regs[4] |= X32CLK; + break; + case 16: + zport->regs[4] |= X16CLK; + break; + case 1: + zport->regs[4] |= X1CLK; + break; + default: + BUG(); + } + + baud = uart_get_baud_rate(uport, termios, old_termios, 0, + uport->uartclk / zport->clk_mode / 4); + + brg = ZS_BPS_TO_BRG(baud, uport->uartclk / zport->clk_mode); + zport->regs[12] = brg & 0xff; + zport->regs[13] = (brg >> 8) & 0xff; + + uart_update_timeout(uport, termios->c_cflag, baud); + + uport->read_status_mask = Rx_OVR; + if (termios->c_iflag & INPCK) + uport->read_status_mask |= FRM_ERR | PAR_ERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + uport->read_status_mask |= Rx_BRK; + + uport->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + uport->ignore_status_mask |= FRM_ERR | PAR_ERR; + if (termios->c_iflag & IGNBRK) { + uport->ignore_status_mask |= Rx_BRK; + if (termios->c_iflag & IGNPAR) + uport->ignore_status_mask |= Rx_OVR; + } + + if (termios->c_cflag & CREAD) + zport->regs[3] |= RxENABLE; + else + zport->regs[3] &= ~RxENABLE; + + if (zport != zport_a) { + if (!(termios->c_cflag & CLOCAL)) { + zport->regs[15] |= DCDIE; + } else + zport->regs[15] &= ~DCDIE; + if (termios->c_cflag & CRTSCTS) { + zport->regs[15] |= CTSIE; + } else + zport->regs[15] &= ~CTSIE; + zs_raw_xor_mctrl(zport); + } + + /* Load up the new values. */ + load_zsregs(zport, zport->regs, irq); + + spin_unlock_irqrestore(&scc->zlock, flags); +} + +/* + * Hack alert! + * Required solely so that the initial PROM-based console + * works undisturbed in parallel with this one. + */ +static void zs_pm(struct uart_port *uport, unsigned int state, + unsigned int oldstate) +{ + struct zs_port *zport = to_zport(uport); + + if (state < 3) + zport->regs[5] |= TxENAB; + else + zport->regs[5] &= ~TxENAB; + write_zsreg(zport, R5, zport->regs[5]); +} + + +static const char *zs_type(struct uart_port *uport) +{ + return "Z85C30 SCC"; +} + +static void zs_release_port(struct uart_port *uport) +{ + iounmap(uport->membase); + uport->membase = 0; + release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE); +} + +static int zs_map_port(struct uart_port *uport) +{ + if (!uport->membase) + uport->membase = ioremap_nocache(uport->mapbase, + ZS_CHAN_IO_SIZE); + if (!uport->membase) { + printk(KERN_ERR "zs: Cannot map MMIO\n"); + return -ENOMEM; + } + return 0; +} + +static int zs_request_port(struct uart_port *uport) +{ + int ret; + + if (!request_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE, "scc")) { + printk(KERN_ERR "zs: Unable to reserve MMIO resource\n"); + return -EBUSY; + } + ret = zs_map_port(uport); + if (ret) { + release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE); + return ret; + } + return 0; +} + +static void zs_config_port(struct uart_port *uport, int flags) +{ + struct zs_port *zport = to_zport(uport); + + if (flags & UART_CONFIG_TYPE) { + if (zs_request_port(uport)) + return; + + uport->type = PORT_ZS; + + zs_reset(zport); + } +} + +static int zs_verify_port(struct uart_port *uport, struct serial_struct *ser) +{ + struct zs_port *zport = to_zport(uport); + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ZS) + ret = -EINVAL; + if (ser->irq != uport->irq) + ret = -EINVAL; + if (ser->baud_base != uport->uartclk / zport->clk_mode / 4) + ret = -EINVAL; + return ret; +} + + +static struct uart_ops zs_ops = { + .tx_empty = zs_tx_empty, + .set_mctrl = zs_set_mctrl, + .get_mctrl = zs_get_mctrl, + .stop_tx = zs_stop_tx, + .start_tx = zs_start_tx, + .stop_rx = zs_stop_rx, + .enable_ms = zs_enable_ms, + .break_ctl = zs_break_ctl, + .startup = zs_startup, + .shutdown = zs_shutdown, + .set_termios = zs_set_termios, + .pm = zs_pm, + .type = zs_type, + .release_port = zs_release_port, + .request_port = zs_request_port, + .config_port = zs_config_port, + .verify_port = zs_verify_port, +}; + +/* + * Initialize Z85C30 port structures. + */ +static int __init zs_probe_sccs(void) +{ + static int probed; + struct zs_parms zs_parms; + int chip, side, irq; + int n_chips = 0; + int i; + + if (probed) + return 0; + + irq = dec_interrupt[DEC_IRQ_SCC0]; + if (irq >= 0) { + zs_parms.scc[n_chips] = IOASIC_SCC0; + zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; + n_chips++; + } + irq = dec_interrupt[DEC_IRQ_SCC1]; + if (irq >= 0) { + zs_parms.scc[n_chips] = IOASIC_SCC1; + zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; + n_chips++; + } + if (!n_chips) + return -ENXIO; + + probed = 1; + + for (chip = 0; chip < n_chips; chip++) { + spin_lock_init(&zs_sccs[chip].zlock); + for (side = 0; side < ZS_NUM_CHAN; side++) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + zport->scc = &zs_sccs[chip]; + zport->clk_mode = 16; + + uport->irq = zs_parms.irq[chip]; + uport->uartclk = ZS_CLOCK; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &zs_ops; + uport->line = chip * ZS_NUM_CHAN + side; + uport->mapbase = dec_kn_slot_base + + zs_parms.scc[chip] + + (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; + + for (i = 0; i < ZS_NUM_REGS; i++) + zport->regs[i] = zs_init_regs[i]; + } + } + + return 0; +} + + +#ifdef CONFIG_SERIAL_ZS_CONSOLE +static void zs_console_putchar(struct uart_port *uport, int ch) +{ + struct zs_port *zport = to_zport(uport); + struct zs_scc *scc = zport->scc; + int irq; + unsigned long flags; + + spin_lock_irqsave(&scc->zlock, flags); + irq = !irqs_disabled_flags(flags); + if (zs_transmit_drain(zport, irq)) + write_zsdata(zport, ch); + spin_unlock_irqrestore(&scc->zlock, flags); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ +static void zs_console_write(struct console *co, const char *s, + unsigned int count) +{ + int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN; + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct zs_scc *scc = zport->scc; + unsigned long flags; + u8 txint, txenb; + int irq; + + /* Disable transmit interrupts and enable the transmitter. */ + spin_lock_irqsave(&scc->zlock, flags); + txint = zport->regs[1]; + txenb = zport->regs[5]; + if (txint & TxINT_ENAB) { + zport->regs[1] = txint & ~TxINT_ENAB; + write_zsreg(zport, R1, zport->regs[1]); + } + if (!(txenb & TxENAB)) { + zport->regs[5] = txenb | TxENAB; + write_zsreg(zport, R5, zport->regs[5]); + } + spin_unlock_irqrestore(&scc->zlock, flags); + + uart_console_write(&zport->port, s, count, zs_console_putchar); + + /* Restore transmit interrupts and the transmitter enable. */ + spin_lock_irqsave(&scc->zlock, flags); + irq = !irqs_disabled_flags(flags); + zs_line_drain(zport, irq); + if (!(txenb & TxENAB)) { + zport->regs[5] &= ~TxENAB; + write_zsreg(zport, R5, zport->regs[5]); + } + if (txint & TxINT_ENAB) { + zport->regs[1] |= TxINT_ENAB; + write_zsreg(zport, R1, zport->regs[1]); + } + spin_unlock_irqrestore(&scc->zlock, flags); +} + +/* + * Setup serial console baud/bits/parity. We do two things here: + * - construct a cflag setting for the first uart_open() + * - initialise the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init zs_console_setup(struct console *co, char *options) +{ + int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN; + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + ret = zs_map_port(uport); + if (ret) + return ret; + + zs_reset(zport); + zs_pm(uport, 0, -1); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(uport, co, baud, parity, bits, flow); +} + +static struct uart_driver zs_reg; +static struct console zs_console = { + .name = "ttyS", + .write = zs_console_write, + .device = uart_console_device, + .setup = zs_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &zs_reg, +}; + +/* + * Register console. + */ +static int __init zs_serial_console_init(void) +{ + int ret; + + ret = zs_probe_sccs(); + if (ret) + return ret; + register_console(&zs_console); + + return 0; +} + +console_initcall(zs_serial_console_init); + +#define SERIAL_ZS_CONSOLE &zs_console +#else +#define SERIAL_ZS_CONSOLE NULL +#endif /* CONFIG_SERIAL_ZS_CONSOLE */ + +static struct uart_driver zs_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = ZS_NUM_SCCS * ZS_NUM_CHAN, + .cons = SERIAL_ZS_CONSOLE, +}; + +/* zs_init inits the driver. */ +static int __init zs_init(void) +{ + int i, ret; + + pr_info("%s%s\n", zs_name, zs_version); + + /* Find out how many Z85C30 SCCs we have. */ + ret = zs_probe_sccs(); + if (ret) + return ret; + + ret = uart_register_driver(&zs_reg); + if (ret) + return ret; + + for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { + struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; + struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; + struct uart_port *uport = &zport->port; + + if (zport->scc) + uart_add_one_port(&zs_reg, uport); + } + + return 0; +} + +static void __exit zs_exit(void) +{ + int i; + + for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { + struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; + struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; + struct uart_port *uport = &zport->port; + + if (zport->scc) + uart_remove_one_port(&zs_reg, uport); + } + + uart_unregister_driver(&zs_reg); +} + +module_init(zs_init); +module_exit(zs_exit); diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h new file mode 100644 index 000000000000..aa921b57d827 --- /dev/null +++ b/drivers/tty/serial/zs.h @@ -0,0 +1,284 @@ +/* + * zs.h: Definitions for the DECstation Z85C30 serial driver. + * + * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras. + * Adapted from drivers/macintosh/macserial.h by Harald Koerfgen. + * + * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au) + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 2004, 2005, 2007 Maciej W. Rozycki + */ +#ifndef _SERIAL_ZS_H +#define _SERIAL_ZS_H + +#ifdef __KERNEL__ + +#define ZS_NUM_REGS 16 + +/* + * This is our internal structure for each serial port's state. + */ +struct zs_port { + struct zs_scc *scc; /* Containing SCC. */ + struct uart_port port; /* Underlying UART. */ + + int clk_mode; /* May be 1, 16, 32, or 64. */ + + unsigned int tty_break; /* Set on BREAK condition. */ + int tx_stopped; /* Output is suspended. */ + + unsigned int mctrl; /* State of modem lines. */ + u8 brk; /* BREAK state from RR0. */ + + u8 regs[ZS_NUM_REGS]; /* Channel write registers. */ +}; + +/* + * Per-SCC state for locking and the interrupt handler. + */ +struct zs_scc { + struct zs_port zport[2]; + spinlock_t zlock; + atomic_t irq_guard; + int initialised; +}; + +#endif /* __KERNEL__ */ + +/* + * Conversion routines to/from brg time constants from/to bits per second. + */ +#define ZS_BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) +#define ZS_BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) + +/* + * The Zilog register set. + */ + +/* Write Register 0 (Command) */ +#define R0 0 /* Register selects */ +#define R1 1 +#define R2 2 +#define R3 3 +#define R4 4 +#define R5 5 +#define R6 6 +#define R7 7 +#define R8 8 +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 + +#define NULLCODE 0 /* Null Code */ +#define POINT_HIGH 0x8 /* Select upper half of registers */ +#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ +#define SEND_ABORT 0x18 /* HDLC Abort */ +#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ +#define RES_Tx_P 0x28 /* Reset TxINT Pending */ +#define ERR_RES 0x30 /* Error Reset */ +#define RES_H_IUS 0x38 /* Reset highest IUS */ + +#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ +#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ +#define RES_EOM_L 0xC0 /* Reset EOM latch */ + +/* Write Register 1 (Tx/Rx/Ext Int Enable and WAIT/DMA Commands) */ +#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ +#define TxINT_ENAB 0x2 /* Tx Int Enable */ +#define PAR_SPEC 0x4 /* Parity is special condition */ + +#define RxINT_DISAB 0 /* Rx Int Disable */ +#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ +#define RxINT_ALL 0x10 /* Int on all Rx Characters or error */ +#define RxINT_ERR 0x18 /* Int on error only */ +#define RxINT_MASK 0x18 + +#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ +#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ +#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ + +/* Write Register 2 (Interrupt Vector) */ + +/* Write Register 3 (Receive Parameters and Control) */ +#define RxENABLE 0x1 /* Rx Enable */ +#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ +#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ +#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ +#define ENT_HM 0x10 /* Enter Hunt Mode */ +#define AUTO_ENAB 0x20 /* Auto Enables */ +#define Rx5 0x0 /* Rx 5 Bits/Character */ +#define Rx7 0x40 /* Rx 7 Bits/Character */ +#define Rx6 0x80 /* Rx 6 Bits/Character */ +#define Rx8 0xc0 /* Rx 8 Bits/Character */ +#define RxNBITS_MASK 0xc0 + +/* Write Register 4 (Transmit/Receive Miscellaneous Parameters and Modes) */ +#define PAR_ENA 0x1 /* Parity Enable */ +#define PAR_EVEN 0x2 /* Parity Even/Odd* */ + +#define SYNC_ENAB 0 /* Sync Modes Enable */ +#define SB1 0x4 /* 1 stop bit/char */ +#define SB15 0x8 /* 1.5 stop bits/char */ +#define SB2 0xc /* 2 stop bits/char */ +#define SB_MASK 0xc + +#define MONSYNC 0 /* 8 Bit Sync character */ +#define BISYNC 0x10 /* 16 bit sync character */ +#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ +#define EXTSYNC 0x30 /* External Sync Mode */ + +#define X1CLK 0x0 /* x1 clock mode */ +#define X16CLK 0x40 /* x16 clock mode */ +#define X32CLK 0x80 /* x32 clock mode */ +#define X64CLK 0xc0 /* x64 clock mode */ +#define XCLK_MASK 0xc0 + +/* Write Register 5 (Transmit Parameters and Controls) */ +#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ +#define RTS 0x2 /* RTS */ +#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ +#define TxENAB 0x8 /* Tx Enable */ +#define SND_BRK 0x10 /* Send Break */ +#define Tx5 0x0 /* Tx 5 bits (or less)/character */ +#define Tx7 0x20 /* Tx 7 bits/character */ +#define Tx6 0x40 /* Tx 6 bits/character */ +#define Tx8 0x60 /* Tx 8 bits/character */ +#define TxNBITS_MASK 0x60 +#define DTR 0x80 /* DTR */ + +/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ + +/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ + +/* Write Register 8 (Transmit Buffer) */ + +/* Write Register 9 (Master Interrupt Control) */ +#define VIS 1 /* Vector Includes Status */ +#define NV 2 /* No Vector */ +#define DLC 4 /* Disable Lower Chain */ +#define MIE 8 /* Master Interrupt Enable */ +#define STATHI 0x10 /* Status high */ +#define SOFTACK 0x20 /* Software Interrupt Acknowledge */ +#define NORESET 0 /* No reset on write to R9 */ +#define CHRB 0x40 /* Reset channel B */ +#define CHRA 0x80 /* Reset channel A */ +#define FHWRES 0xc0 /* Force hardware reset */ + +/* Write Register 10 (Miscellaneous Transmitter/Receiver Control Bits) */ +#define BIT6 1 /* 6 bit/8bit sync */ +#define LOOPMODE 2 /* SDLC Loop mode */ +#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ +#define MARKIDLE 8 /* Mark/flag on idle */ +#define GAOP 0x10 /* Go active on poll */ +#define NRZ 0 /* NRZ mode */ +#define NRZI 0x20 /* NRZI mode */ +#define FM1 0x40 /* FM1 (transition = 1) */ +#define FM0 0x60 /* FM0 (transition = 0) */ +#define CRCPS 0x80 /* CRC Preset I/O */ + +/* Write Register 11 (Clock Mode Control) */ +#define TRxCXT 0 /* TRxC = Xtal output */ +#define TRxCTC 1 /* TRxC = Transmit clock */ +#define TRxCBR 2 /* TRxC = BR Generator Output */ +#define TRxCDP 3 /* TRxC = DPLL output */ +#define TRxCOI 4 /* TRxC O/I */ +#define TCRTxCP 0 /* Transmit clock = RTxC pin */ +#define TCTRxCP 8 /* Transmit clock = TRxC pin */ +#define TCBR 0x10 /* Transmit clock = BR Generator output */ +#define TCDPLL 0x18 /* Transmit clock = DPLL output */ +#define RCRTxCP 0 /* Receive clock = RTxC pin */ +#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ +#define RCBR 0x40 /* Receive clock = BR Generator output */ +#define RCDPLL 0x60 /* Receive clock = DPLL output */ +#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ + +/* Write Register 12 (Lower Byte of Baud Rate Generator Time Constant) */ + +/* Write Register 13 (Upper Byte of Baud Rate Generator Time Constant) */ + +/* Write Register 14 (Miscellaneous Control Bits) */ +#define BRENABL 1 /* Baud rate generator enable */ +#define BRSRC 2 /* Baud rate generator source */ +#define DTRREQ 4 /* DTR/Request function */ +#define AUTOECHO 8 /* Auto Echo */ +#define LOOPBAK 0x10 /* Local loopback */ +#define SEARCH 0x20 /* Enter search mode */ +#define RMC 0x40 /* Reset missing clock */ +#define DISDPLL 0x60 /* Disable DPLL */ +#define SSBR 0x80 /* Set DPLL source = BR generator */ +#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ +#define SFMM 0xc0 /* Set FM mode */ +#define SNRZI 0xe0 /* Set NRZI mode */ + +/* Write Register 15 (External/Status Interrupt Control) */ +#define WR7P_EN 1 /* WR7 Prime SDLC Feature Enable */ +#define ZCIE 2 /* Zero count IE */ +#define DCDIE 8 /* DCD IE */ +#define SYNCIE 0x10 /* Sync/hunt IE */ +#define CTSIE 0x20 /* CTS IE */ +#define TxUIE 0x40 /* Tx Underrun/EOM IE */ +#define BRKIE 0x80 /* Break/Abort IE */ + + +/* Read Register 0 (Transmit/Receive Buffer Status and External Status) */ +#define Rx_CH_AV 0x1 /* Rx Character Available */ +#define ZCOUNT 0x2 /* Zero count */ +#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ +#define DCD 0x8 /* DCD */ +#define SYNC_HUNT 0x10 /* Sync/hunt */ +#define CTS 0x20 /* CTS */ +#define TxEOM 0x40 /* Tx underrun */ +#define BRK_ABRT 0x80 /* Break/Abort */ + +/* Read Register 1 (Special Receive Condition Status) */ +#define ALL_SNT 0x1 /* All sent */ +/* Residue Data for 8 Rx bits/char programmed */ +#define RES3 0x8 /* 0/3 */ +#define RES4 0x4 /* 0/4 */ +#define RES5 0xc /* 0/5 */ +#define RES6 0x2 /* 0/6 */ +#define RES7 0xa /* 0/7 */ +#define RES8 0x6 /* 0/8 */ +#define RES18 0xe /* 1/8 */ +#define RES28 0x0 /* 2/8 */ +/* Special Rx Condition Interrupts */ +#define PAR_ERR 0x10 /* Parity Error */ +#define Rx_OVR 0x20 /* Rx Overrun Error */ +#define FRM_ERR 0x40 /* CRC/Framing Error */ +#define END_FR 0x80 /* End of Frame (SDLC) */ + +/* Read Register 2 (Interrupt Vector (WR2) -- channel A). */ + +/* Read Register 2 (Modified Interrupt Vector -- channel B). */ + +/* Read Register 3 (Interrupt Pending Bits -- channel A only). */ +#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ +#define CHBTxIP 0x2 /* Channel B Tx IP */ +#define CHBRxIP 0x4 /* Channel B Rx IP */ +#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ +#define CHATxIP 0x10 /* Channel A Tx IP */ +#define CHARxIP 0x20 /* Channel A Rx IP */ + +/* Read Register 6 (SDLC FIFO Status and Byte Count LSB) */ + +/* Read Register 7 (SDLC FIFO Status and Byte Count MSB) */ + +/* Read Register 8 (Receive Data) */ + +/* Read Register 10 (Miscellaneous Status Bits) */ +#define ONLOOP 2 /* On loop */ +#define LOOPSEND 0x10 /* Loop sending */ +#define CLK2MIS 0x40 /* Two clocks missing */ +#define CLK1MIS 0x80 /* One clock missing */ + +/* Read Register 12 (Lower Byte of Baud Rate Generator Constant (WR12)) */ + +/* Read Register 13 (Upper Byte of Baud Rate Generator Constant (WR13) */ + +/* Read Register 15 (External/Status Interrupt Control (WR15)) */ + +#endif /* _SERIAL_ZS_H */ -- cgit v1.2.3 From df6212529c646710502657b18d8f42927f3dda81 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Jan 2011 14:47:04 -0800 Subject: tty: update MAINTAINERS file due to driver movement This fixes up the MAINTAINERS file due to moving the serial drivers to the drivers/tty/ directory. Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 78162c4f9544..fd05bb773494 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -162,7 +162,7 @@ L: linux-serial@vger.kernel.org W: http://serial.sourceforge.net S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6.git -F: drivers/serial/8250* +F: drivers/tty/serial/8250* F: include/linux/serial_8250.h 8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.] @@ -888,8 +888,8 @@ F: arch/arm/mach-msm/ F: drivers/video/msm/ F: drivers/mmc/host/msm_sdcc.c F: drivers/mmc/host/msm_sdcc.h -F: drivers/serial/msm_serial.h -F: drivers/serial/msm_serial.c +F: drivers/tty/serial/msm_serial.h +F: drivers/tty/serial/msm_serial.c T: git git://codeaurora.org/quic/kernel/davidb/linux-msm.git S: Maintained @@ -1256,7 +1256,7 @@ F: drivers/mmc/host/atmel-mci-regs.h ATMEL AT91 / AT32 SERIAL DRIVER M: Nicolas Ferre S: Supported -F: drivers/serial/atmel_serial.c +F: drivers/tty/serial/atmel_serial.c ATMEL LCDFB DRIVER M: Nicolas Ferre @@ -1412,7 +1412,7 @@ M: Sonic Zhang L: uclinux-dist-devel@blackfin.uclinux.org W: http://blackfin.uclinux.org S: Supported -F: drivers/serial/bfin_5xx.c +F: drivers/tty/serial/bfin_5xx.c BLACKFIN WATCHDOG DRIVER M: Mike Frysinger @@ -1862,7 +1862,7 @@ L: linux-cris-kernel@axis.com W: http://developer.axis.com S: Maintained F: arch/cris/ -F: drivers/serial/crisv10.* +F: drivers/tty/serial/crisv10.* CRYPTO API M: Herbert Xu @@ -2201,7 +2201,7 @@ F: drivers/net/wan/dscc4.c DZ DECSTATION DZ11 SERIAL DRIVER M: "Maciej W. Rozycki" S: Maintained -F: drivers/serial/dz.* +F: drivers/tty/serial/dz.* EATA-DMA SCSI DRIVER M: Michael Neuffer @@ -2621,7 +2621,7 @@ FREESCALE QUICC ENGINE UCC UART DRIVER M: Timur Tabi L: linuxppc-dev@lists.ozlabs.org S: Supported -F: drivers/serial/ucc_uart.c +F: drivers/tty/serial/ucc_uart.c FREESCALE SOC SOUND DRIVERS M: Timur Tabi @@ -3328,7 +3328,7 @@ IOC3 SERIAL DRIVER M: Pat Gefre L: linux-serial@vger.kernel.org S: Maintained -F: drivers/serial/ioc3_serial.c +F: drivers/tty/serial/ioc3_serial.c IP MASQUERADING M: Juanjo Ciarlante @@ -3505,7 +3505,7 @@ JSM Neo PCI based serial card M: Breno Leitao L: linux-serial@vger.kernel.org S: Maintained -F: drivers/serial/jsm/ +F: drivers/tty/serial/jsm/ K8TEMP HARDWARE MONITORING DRIVER M: Rudolf Marek @@ -3648,7 +3648,7 @@ L: kgdb-bugreport@lists.sourceforge.net S: Maintained F: Documentation/DocBook/kgdb.tmpl F: drivers/misc/kgdbts.c -F: drivers/serial/kgdboc.c +F: drivers/tty/serial/kgdboc.c F: include/linux/kdb.h F: include/linux/kgdb.h F: kernel/debug/ @@ -5510,7 +5510,7 @@ M: Pat Gefre L: linux-ia64@vger.kernel.org S: Supported F: Documentation/ia64/serial.txt -F: drivers/serial/ioc?_serial.c +F: drivers/tty/serial/ioc?_serial.c F: include/linux/ioc?.h SGI VISUAL WORKSTATION 320 AND 540 @@ -5532,7 +5532,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/arm/Sharp-LH/ADC-LH7-Touchscreen F: arch/arm/mach-lh7a40x/ -F: drivers/serial/serial_lh7a40x.c +F: drivers/tty/serial/serial_lh7a40x.c F: drivers/usb/gadget/lh7a40* F: drivers/usb/host/ohci-lh7a40* @@ -5752,14 +5752,14 @@ L: sparclinux@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-next-2.6.git S: Maintained -F: drivers/serial/suncore.c -F: drivers/serial/suncore.h -F: drivers/serial/sunhv.c -F: drivers/serial/sunsab.c -F: drivers/serial/sunsab.h -F: drivers/serial/sunsu.c -F: drivers/serial/sunzilog.c -F: drivers/serial/sunzilog.h +F: drivers/tty/serial/suncore.c +F: drivers/tty/serial/suncore.h +F: drivers/tty/serial/sunhv.c +F: drivers/tty/serial/sunsab.c +F: drivers/tty/serial/sunsab.h +F: drivers/tty/serial/sunsu.c +F: drivers/tty/serial/sunzilog.c +F: drivers/tty/serial/sunzilog.h SPEAR PLATFORM SUPPORT M: Viresh Kumar @@ -6089,8 +6089,8 @@ TTY LAYER M: Greg Kroah-Hartman S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6.git -F: drivers/char/tty_* -F: drivers/serial/serial_core.c +F: drivers/tty/* +F: drivers/tty/serial/serial_core.c F: include/linux/serial_core.h F: include/linux/serial.h F: include/linux/tty.h @@ -6829,7 +6829,7 @@ XILINX UARTLITE SERIAL DRIVER M: Peter Korsgaard L: linux-serial@vger.kernel.org S: Maintained -F: drivers/serial/uartlite.c +F: drivers/tty/serial/uartlite.c YAM DRIVER FOR AX.25 M: Jean-Paul Roubelat @@ -6875,7 +6875,7 @@ F: drivers/media/video/zoran/ ZS DECSTATION Z85C30 SERIAL DRIVER M: "Maciej W. Rozycki" S: Maintained -F: drivers/serial/zs.* +F: drivers/tty/serial/zs.* GRE DEMULTIPLEXER DRIVER M: Dmitry Kozlov -- cgit v1.2.3 From 40c1001792de63e0f90e977eb05393fd71f78692 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 20 Dec 2010 12:37:18 -0500 Subject: trusted-keys: free memory bugfix Add missing kfree(td) in tpm_seal() before the return, freeing td on error paths as well. Reported-by: Dan Carpenter Signed-off-by: Mimi Zohar Acked-by: David Safford Acked-by: David Howells Signed-off-by: Serge Hallyn Signed-off-by: James Morris --- security/keys/trusted_defined.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c index 975e9f29a52c..932f8687df16 100644 --- a/security/keys/trusted_defined.c +++ b/security/keys/trusted_defined.c @@ -511,7 +511,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, /* get session for sealing key */ ret = osap(tb, &sess, keyauth, keytype, keyhandle); if (ret < 0) - return ret; + goto out; dump_sess(&sess); /* calculate encrypted authorization value */ @@ -519,11 +519,11 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); if (ret < 0) - return ret; + goto out; ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE); if (ret < 0) - return ret; + goto out; ordinal = htonl(TPM_ORD_SEAL); datsize = htonl(datalen); pcrsize = htonl(pcrinfosize); @@ -552,7 +552,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, &datsize, datalen, data, 0, 0); } if (ret < 0) - return ret; + goto out; /* build and send the TPM request packet */ INIT_BUF(tb); @@ -572,7 +572,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); if (ret < 0) - return ret; + goto out; /* calculate the size of the returned Blob */ sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t)); @@ -591,6 +591,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize); *bloblen = storedsize; } +out: + kfree(td); return ret; } -- cgit v1.2.3 From 977dda7c9b540f48b228174346d8b31542c1e99f Mon Sep 17 00:00:00 2001 From: Paul Turner Date: Fri, 14 Jan 2011 17:57:50 -0800 Subject: sched: Update effective_load() to use global share weights Previously effective_load would approximate the global load weight present on a group taking advantage of: entity_weight = tg->shares ( lw / global_lw ), where entity_weight was provided by tg_shares_up. This worked (approximately) for an 'empty' (at tg level) cpu since we would place boost load representative of what a newly woken task would receive. However, now that load is instantaneously updated this assumption is no longer true and the load calculation is rather incorrect in this case. Fix this (and improve the general case) by re-writing effective_load to take advantage of the new shares distribution code. Signed-off-by: Paul Turner Signed-off-by: Peter Zijlstra LKML-Reference: <20110115015817.069769529@google.com> Signed-off-by: Ingo Molnar --- kernel/sched_fair.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index c62ebae65cf0..414145cf5344 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1362,27 +1362,27 @@ static long effective_load(struct task_group *tg, int cpu, long wl, long wg) return wl; for_each_sched_entity(se) { - long S, rw, s, a, b; + long lw, w; - S = se->my_q->tg->shares; - s = se->load.weight; - rw = se->my_q->load.weight; + tg = se->my_q->tg; + w = se->my_q->load.weight; - a = S*(rw + wl); - b = S*rw + s*wg; + /* use this cpu's instantaneous contribution */ + lw = atomic_read(&tg->load_weight); + lw -= se->my_q->load_contribution; + lw += w + wg; - wl = s*(a-b); + wl += w; - if (likely(b)) - wl /= b; + if (lw > 0 && wl < lw) + wl = (wl * tg->shares) / lw; + else + wl = tg->shares; - /* - * Assume the group is already running and will - * thus already be accounted for in the weight. - * - * That is, moving shares between CPUs, does not - * alter the group weight. - */ + /* zero point is MIN_SHARES */ + if (wl < MIN_SHARES) + wl = MIN_SHARES; + wl -= se->load.weight; wg = 0; } -- cgit v1.2.3 From efe25c2c7b3a5d17b0c70987a758d8fe7af8e3d1 Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Tue, 11 Jan 2011 15:41:54 +0530 Subject: sched: Reinstate group names in /proc/sched_debug Displaying of group names in /proc/sched_debug was dropped in autogroup patches. Add group names while displaying cfs_rq and tasks information. Signed-off-by: Bharata B Rao Signed-off-by: Peter Zijlstra LKML-Reference: <20110111101153.GE4772@in.ibm.com> Signed-off-by: Ingo Molnar --- kernel/sched_debug.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c index 1dfae3d014b5..4d36f3726da7 100644 --- a/kernel/sched_debug.c +++ b/kernel/sched_debug.c @@ -16,6 +16,8 @@ #include #include +static DEFINE_SPINLOCK(sched_debug_lock); + /* * This allows printing both to /proc/sched_debug and * to the console @@ -86,6 +88,23 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group } #endif +#ifdef CONFIG_CGROUP_SCHED +static char group_path[PATH_MAX]; + +static char *task_group_path(struct task_group *tg) +{ + /* + * May be NULL if the underlying cgroup isn't fully-created yet + */ + if (!tg->css.cgroup) { + group_path[0] = '\0'; + return group_path; + } + cgroup_path(tg->css.cgroup, group_path, PATH_MAX); + return group_path; +} +#endif + static void print_task(struct seq_file *m, struct rq *rq, struct task_struct *p) { @@ -108,6 +127,9 @@ print_task(struct seq_file *m, struct rq *rq, struct task_struct *p) SEQ_printf(m, "%15Ld %15Ld %15Ld.%06ld %15Ld.%06ld %15Ld.%06ld", 0LL, 0LL, 0LL, 0L, 0LL, 0L, 0LL, 0L); #endif +#ifdef CONFIG_CGROUP_SCHED + SEQ_printf(m, " %s", task_group_path(task_group(p))); +#endif SEQ_printf(m, "\n"); } @@ -144,7 +166,11 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) struct sched_entity *last; unsigned long flags; +#ifdef CONFIG_FAIR_GROUP_SCHED + SEQ_printf(m, "\ncfs_rq[%d]:%s\n", cpu, task_group_path(cfs_rq->tg)); +#else SEQ_printf(m, "\ncfs_rq[%d]:\n", cpu); +#endif SEQ_printf(m, " .%-30s: %Ld.%06ld\n", "exec_clock", SPLIT_NS(cfs_rq->exec_clock)); @@ -191,7 +217,11 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) void print_rt_rq(struct seq_file *m, int cpu, struct rt_rq *rt_rq) { +#ifdef CONFIG_RT_GROUP_SCHED + SEQ_printf(m, "\nrt_rq[%d]:%s\n", cpu, task_group_path(rt_rq->tg)); +#else SEQ_printf(m, "\nrt_rq[%d]:\n", cpu); +#endif #define P(x) \ SEQ_printf(m, " .%-30s: %Ld\n", #x, (long long)(rt_rq->x)) @@ -212,6 +242,7 @@ extern __read_mostly int sched_clock_running; static void print_cpu(struct seq_file *m, int cpu) { struct rq *rq = cpu_rq(cpu); + unsigned long flags; #ifdef CONFIG_X86 { @@ -266,10 +297,14 @@ static void print_cpu(struct seq_file *m, int cpu) #undef P #endif + spin_lock_irqsave(&sched_debug_lock, flags); print_cfs_stats(m, cpu); print_rt_stats(m, cpu); + rcu_read_lock(); print_rq(m, rq, cpu); + rcu_read_unlock(); + spin_unlock_irqrestore(&sched_debug_lock, flags); } static const char *sched_tunable_scaling_names[] = { -- cgit v1.2.3 From 8ecedd7a06d27a31dbb36fab88e2ba6e6edd43ca Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Tue, 11 Jan 2011 15:42:57 +0530 Subject: sched: Display autogroup names in /proc/sched_debug Add autogroup name to cfs_rq and tasks information to /proc/sched_debug. Signed-off-by: Bharata B Rao Signed-off-by: Peter Zijlstra LKML-Reference: <20110111101257.GF4772@in.ibm.com> Signed-off-by: Ingo Molnar --- kernel/sched_autogroup.c | 5 +++++ kernel/sched_debug.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/kernel/sched_autogroup.c b/kernel/sched_autogroup.c index 32a723b8f84c..938d52f80a2d 100644 --- a/kernel/sched_autogroup.c +++ b/kernel/sched_autogroup.c @@ -231,6 +231,11 @@ void proc_sched_autogroup_show_task(struct task_struct *p, struct seq_file *m) #ifdef CONFIG_SCHED_DEBUG static inline int autogroup_path(struct task_group *tg, char *buf, int buflen) { + int enabled = ACCESS_ONCE(sysctl_sched_autogroup_enabled); + + if (!enabled || !tg->autogroup) + return 0; + return snprintf(buf, buflen, "%s-%ld", "/autogroup", tg->autogroup->id); } #endif /* CONFIG_SCHED_DEBUG */ diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c index 4d36f3726da7..e4d37259d490 100644 --- a/kernel/sched_debug.c +++ b/kernel/sched_debug.c @@ -93,6 +93,9 @@ static char group_path[PATH_MAX]; static char *task_group_path(struct task_group *tg) { + if (autogroup_path(tg, group_path, PATH_MAX)) + return group_path; + /* * May be NULL if the underlying cgroup isn't fully-created yet */ -- cgit v1.2.3 From f44937718ce3b8360f72f6c68c9481712517a867 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Thu, 13 Jan 2011 04:54:50 +0100 Subject: sched, autogroup: Fix CONFIG_RT_GROUP_SCHED sched_setscheduler() failure If CONFIG_RT_GROUP_SCHED is set, __sched_setscheduler() fails due to autogroup not allocating rt_runtime. Free unused/unusable rt_se and rt_rq, redirect RT tasks to the root task group, and tell __sched_setscheduler() that it's ok. Reported-and-tested-by: Bharata B Rao Signed-off-by: Mike Galbraith Signed-off-by: Peter Zijlstra LKML-Reference: <1294890890.8089.39.camel@marge.simson.net> Signed-off-by: Ingo Molnar --- kernel/sched.c | 3 ++- kernel/sched_autogroup.c | 27 +++++++++++++++++++++++++++ kernel/sched_autogroup.h | 4 ++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/kernel/sched.c b/kernel/sched.c index a0eb0941fa84..6cbff6bd1a60 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4871,7 +4871,8 @@ recheck: * assigned. */ if (rt_bandwidth_enabled() && rt_policy(policy) && - task_group(p)->rt_bandwidth.rt_runtime == 0) { + task_group(p)->rt_bandwidth.rt_runtime == 0 && + !task_group_is_autogroup(task_group(p))) { __task_rq_unlock(rq); raw_spin_unlock_irqrestore(&p->pi_lock, flags); return -EPERM; diff --git a/kernel/sched_autogroup.c b/kernel/sched_autogroup.c index 938d52f80a2d..9fb656283157 100644 --- a/kernel/sched_autogroup.c +++ b/kernel/sched_autogroup.c @@ -27,6 +27,11 @@ static inline void autogroup_destroy(struct kref *kref) { struct autogroup *ag = container_of(kref, struct autogroup, kref); +#ifdef CONFIG_RT_GROUP_SCHED + /* We've redirected RT tasks to the root task group... */ + ag->tg->rt_se = NULL; + ag->tg->rt_rq = NULL; +#endif sched_destroy_group(ag->tg); } @@ -55,6 +60,10 @@ static inline struct autogroup *autogroup_task_get(struct task_struct *p) return ag; } +#ifdef CONFIG_RT_GROUP_SCHED +static void free_rt_sched_group(struct task_group *tg); +#endif + static inline struct autogroup *autogroup_create(void) { struct autogroup *ag = kzalloc(sizeof(*ag), GFP_KERNEL); @@ -72,6 +81,19 @@ static inline struct autogroup *autogroup_create(void) init_rwsem(&ag->lock); ag->id = atomic_inc_return(&autogroup_seq_nr); ag->tg = tg; +#ifdef CONFIG_RT_GROUP_SCHED + /* + * Autogroup RT tasks are redirected to the root task group + * so we don't have to move tasks around upon policy change, + * or flail around trying to allocate bandwidth on the fly. + * A bandwidth exception in __sched_setscheduler() allows + * the policy change to proceed. Thereafter, task_group() + * returns &root_task_group, so zero bandwidth is required. + */ + free_rt_sched_group(tg); + tg->rt_se = root_task_group.rt_se; + tg->rt_rq = root_task_group.rt_rq; +#endif tg->autogroup = ag; return ag; @@ -106,6 +128,11 @@ task_wants_autogroup(struct task_struct *p, struct task_group *tg) return true; } +static inline bool task_group_is_autogroup(struct task_group *tg) +{ + return tg != &root_task_group && tg->autogroup; +} + static inline struct task_group * autogroup_task_group(struct task_struct *p, struct task_group *tg) { diff --git a/kernel/sched_autogroup.h b/kernel/sched_autogroup.h index 5358e241cb20..7b859ffe5dad 100644 --- a/kernel/sched_autogroup.h +++ b/kernel/sched_autogroup.h @@ -15,6 +15,10 @@ autogroup_task_group(struct task_struct *p, struct task_group *tg); static inline void autogroup_init(struct task_struct *init_task) { } static inline void autogroup_free(struct task_group *tg) { } +static inline bool task_group_is_autogroup(struct task_group *tg) +{ + return 0; +} static inline struct task_group * autogroup_task_group(struct task_struct *p, struct task_group *tg) -- cgit v1.2.3 From fce2097983d914ea8c2338efc6f6e9bb737f6ae4 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Fri, 14 Jan 2011 15:57:39 +0800 Subject: sched: Replace rq->bkl_count with rq->rq_sched_info.bkl_count Now rq->rq_sched_info.bkl_count is not used for rq, scroll rq->bkl_count into it. Thus we can save some space for rq. Signed-off-by: Yong Zhang Signed-off-by: Peter Zijlstra LKML-Reference: <1294991859-13246-1-git-send-email-yong.zhang0@gmail.com> Signed-off-by: Ingo Molnar --- kernel/sched.c | 5 +---- kernel/sched_debug.c | 4 +++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/sched.c b/kernel/sched.c index 6cbff6bd1a60..0a169a85eb3e 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -553,9 +553,6 @@ struct rq { /* try_to_wake_up() stats */ unsigned int ttwu_count; unsigned int ttwu_local; - - /* BKL stats */ - unsigned int bkl_count; #endif }; @@ -3887,7 +3884,7 @@ static inline void schedule_debug(struct task_struct *prev) schedstat_inc(this_rq(), sched_count); #ifdef CONFIG_SCHEDSTATS if (unlikely(prev->lock_depth >= 0)) { - schedstat_inc(this_rq(), bkl_count); + schedstat_inc(this_rq(), rq_sched_info.bkl_count); schedstat_inc(prev, sched_info.bkl_count); } #endif diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c index e4d37259d490..eb6cb8edd075 100644 --- a/kernel/sched_debug.c +++ b/kernel/sched_debug.c @@ -296,9 +296,11 @@ static void print_cpu(struct seq_file *m, int cpu) P(ttwu_count); P(ttwu_local); - P(bkl_count); + SEQ_printf(m, " .%-30s: %d\n", "bkl_count", + rq->rq_sched_info.bkl_count); #undef P +#undef P64 #endif spin_lock_irqsave(&sched_debug_lock, flags); print_cfs_stats(m, cpu); -- cgit v1.2.3 From d7d8294415f0ce4254827d4a2a5ee88b00be52a8 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Wed, 5 Jan 2011 05:41:17 +0100 Subject: sched: Fix signed unsigned comparison in check_preempt_tick() Signed unsigned comparison may lead to superfluous resched if leftmost is right of the current task, wasting a few cycles, and inadvertently _lengthening_ the current task's slice. Reported-by: Venkatesh Pallipadi Signed-off-by: Mike Galbraith Signed-off-by: Peter Zijlstra LKML-Reference: <1294202477.9384.5.camel@marge.simson.net> Signed-off-by: Ingo Molnar --- kernel/sched_fair.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index 414145cf5344..77e9166d7bbf 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -1062,6 +1062,9 @@ check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr) struct sched_entity *se = __pick_next_entity(cfs_rq); s64 delta = curr->vruntime - se->vruntime; + if (delta < 0) + return; + if (delta > ideal_runtime) resched_task(rq_of(cfs_rq)->curr); } -- cgit v1.2.3 From b8b1a4cb6842fb33769be1ad636f062d31d588c3 Mon Sep 17 00:00:00 2001 From: Brian Bloniarz Date: Mon, 17 Jan 2011 23:20:03 -0800 Subject: ALSA: ice1712 delta - initialize SPI clock The driver was using an initial value for the clock on the SPI bus which was read from ICE1712 EEPROM, ice->eeprom.data[ICE_EEP1_GPIO_STATE] & ICE1712_DELTA_AP_CCLK (0x02) It appears some cards have it default high, some cards have it default low. On my Delta 66 rev. E: $ cat /proc/asound/M66/ice1712 | grep 'GPIO state' GPIO state : 0x70 /* ICE1712_DELTA_AP_CCLK bit is zero */ On my Audiophile 2496: $ cat /proc/asound/M2496/ice1712 | grep 'GPIO state' GPIO state : 0xfe /* ICE1712_DELTA_AP_CCLK bit is one */ It must be raised before the first SPI write happens, or the write will fail, leading to: [ 23.248721] invalid CS8427 signature 0x0: let me try again... I theorize that 4eb4550ab37d351ab0973ccec921a5a2d8560ec7 is no longer needed, it was a different way to workaround the problem. [fixed variable decleration by tiwai] Signed-off-by: Brian Bloniarz Signed-off-by: Takashi Iwai --- sound/pci/ice1712/delta.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index 7b62de089fee..20c6b079d0df 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -580,6 +580,7 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) { int err; struct snd_akm4xxx *ak; + unsigned char tmp; if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DELTA1010 && ice->eeprom.gpiodir == 0x7b) @@ -622,6 +623,12 @@ static int __devinit snd_ice1712_delta_init(struct snd_ice1712 *ice) break; } + /* initialize the SPI clock to high */ + tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA); + tmp |= ICE1712_DELTA_AP_CCLK; + snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp); + udelay(5); + /* initialize spdif */ switch (ice->eeprom.subvendor) { case ICE1712_SUBDEVICE_AUDIOPHILE: -- cgit v1.2.3 From 23c3010808de86f21436eb822aacfa551bfc17e4 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Fri, 14 Jan 2011 22:39:16 -0600 Subject: GFS2: remove iopen glocks from cache on failed deletes When a file gets deleted on GFS2, if a node can't get an exclusive lock on the file's iopen glock, it punts on actually freeing up the space, because another node is using the file. When it does this, it needs to drop the iopen glock from its cache so that the other node can get an exclusive lock on it. Now, gfs2_delete_inode() sets GL_NOCACHE before dropping the shared lock on the iopen glock in preparation for grabbing it in the exclusive state. Since the node needs the glock in the exclusive state, dropping the shared lock from the cache doesn't slow down the case where no other nodes are using the file. Signed-off-by: Benjamin Marzinski Signed-off-by: Steven Whitehouse --- fs/gfs2/super.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 16c2ecac7eb7..ec73ed70bae1 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1336,6 +1336,7 @@ static void gfs2_evict_inode(struct inode *inode) if (error) goto out_truncate; + ip->i_iopen_gh.gh_flags |= GL_NOCACHE; gfs2_glock_dq_wait(&ip->i_iopen_gh); gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh); error = gfs2_glock_nq(&ip->i_iopen_gh); -- cgit v1.2.3 From 24d9765fc18c7838ccdbb0d71fb706321d9b824c Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Tue, 18 Jan 2011 14:49:08 +0000 Subject: GFS2: Fix error path in gfs2_lookup_by_inum() In the (impossible, except if there is fs corruption) error path in gfs2_lookup_by_inum() if the call to gfs2_inode_refresh() fails, it was leaving the function by calling iput() rather than iget_failed(). This would cause future lookups of the same inode to block forever. This patch fixes the problem by moving the call to gfs2_inode_refresh() into gfs2_inode_lookup() where iget_failed() is part of the error path already. Also this cleans up some unreachable code and makes gfs2_set_iop() static. Signed-off-by: Steven Whitehouse --- fs/gfs2/inode.c | 72 ++++++++++++++++++--------------------------------------- fs/gfs2/inode.h | 1 - 2 files changed, 22 insertions(+), 51 deletions(-) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 2232b3c780bd..7aa7d4f8984a 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -74,16 +74,14 @@ static struct inode *gfs2_iget(struct super_block *sb, u64 no_addr) } /** - * GFS2 lookup code fills in vfs inode contents based on info obtained - * from directory entry inside gfs2_inode_lookup(). This has caused issues - * with NFS code path since its get_dentry routine doesn't have the relevant - * directory entry when gfs2_inode_lookup() is invoked. Part of the code - * segment inside gfs2_inode_lookup code needs to get moved around. + * gfs2_set_iop - Sets inode operations + * @inode: The inode with correct i_mode filled in * - * Clears I_NEW as well. - **/ + * GFS2 lookup code fills in vfs inode contents based on info obtained + * from directory entry inside gfs2_inode_lookup(). + */ -void gfs2_set_iop(struct inode *inode) +static void gfs2_set_iop(struct inode *inode) { struct gfs2_sbd *sdp = GFS2_SB(inode); umode_t mode = inode->i_mode; @@ -106,8 +104,6 @@ void gfs2_set_iop(struct inode *inode) inode->i_op = &gfs2_file_iops; init_special_inode(inode, inode->i_mode, inode->i_rdev); } - - unlock_new_inode(inode); } /** @@ -119,10 +115,8 @@ void gfs2_set_iop(struct inode *inode) * Returns: A VFS inode, or an error */ -struct inode *gfs2_inode_lookup(struct super_block *sb, - unsigned int type, - u64 no_addr, - u64 no_formal_ino) +struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, + u64 no_addr, u64 no_formal_ino) { struct inode *inode; struct gfs2_inode *ip; @@ -152,51 +146,37 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT, &ip->i_iopen_gh); if (unlikely(error)) goto fail_iopen; - ip->i_iopen_gh.gh_gl->gl_object = ip; + ip->i_iopen_gh.gh_gl->gl_object = ip; gfs2_glock_put(io_gl); io_gl = NULL; - if ((type == DT_UNKNOWN) && (no_formal_ino == 0)) - goto gfs2_nfsbypass; - - inode->i_mode = DT2IF(type); - - /* - * We must read the inode in order to work out its type in - * this case. Note that this doesn't happen often as we normally - * know the type beforehand. This code path only occurs during - * unlinked inode recovery (where it is safe to do this glock, - * which is not true in the general case). - */ if (type == DT_UNKNOWN) { - struct gfs2_holder gh; - error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); - if (unlikely(error)) - goto fail_glock; - /* Inode is now uptodate */ - gfs2_glock_dq_uninit(&gh); + /* Inode glock must be locked already */ + error = gfs2_inode_refresh(GFS2_I(inode)); + if (error) + goto fail_refresh; + } else { + inode->i_mode = DT2IF(type); } gfs2_set_iop(inode); + unlock_new_inode(inode); } -gfs2_nfsbypass: return inode; -fail_glock: - gfs2_glock_dq(&ip->i_iopen_gh); + +fail_refresh: + ip->i_iopen_gh.gh_gl->gl_object = NULL; + gfs2_glock_dq_uninit(&ip->i_iopen_gh); fail_iopen: if (io_gl) gfs2_glock_put(io_gl); fail_put: - if (inode->i_state & I_NEW) - ip->i_gl->gl_object = NULL; + ip->i_gl->gl_object = NULL; gfs2_glock_put(ip->i_gl); fail: - if (inode->i_state & I_NEW) - iget_failed(inode); - else - iput(inode); + iget_failed(inode); return ERR_PTR(error); } @@ -221,14 +201,6 @@ struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, if (IS_ERR(inode)) goto fail; - error = gfs2_inode_refresh(GFS2_I(inode)); - if (error) - goto fail_iput; - - /* Pick up the works we bypass in gfs2_inode_lookup */ - if (inode->i_state & I_NEW) - gfs2_set_iop(inode); - /* Two extra checks for NFS only */ if (no_formal_ino) { error = -ESTALE; diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index 732a183efdb3..3e00a66e7cbd 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -96,7 +96,6 @@ err: return -EIO; } -extern void gfs2_set_iop(struct inode *inode); extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, u64 no_addr, u64 no_formal_ino); extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, -- cgit v1.2.3 From 379c4bf1d6e184ecb8ff72f83b7c81588cfa18f8 Mon Sep 17 00:00:00 2001 From: Seungwhan Youn Date: Thu, 13 Jan 2011 11:08:21 +0900 Subject: ASoC: documentation updates This patch is only for RFC purpose of ASoC documentation updates which match with current ASoC codes with documents. Mostly modify features are modified to be sync with changes after multi-component patches. Signed-off-by: Seungwhan Youn Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- Documentation/sound/alsa/soc/codec.txt | 45 +++++++++++++++---------------- Documentation/sound/alsa/soc/machine.txt | 38 +++++++------------------- Documentation/sound/alsa/soc/platform.txt | 12 +++++++-- 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/Documentation/sound/alsa/soc/codec.txt b/Documentation/sound/alsa/soc/codec.txt index 37ba3a72cb76..bce23a4a7875 100644 --- a/Documentation/sound/alsa/soc/codec.txt +++ b/Documentation/sound/alsa/soc/codec.txt @@ -27,42 +27,38 @@ ASoC Codec driver breakdown 1 - Codec DAI and PCM configuration ----------------------------------- -Each codec driver must have a struct snd_soc_codec_dai to define its DAI and +Each codec driver must have a struct snd_soc_dai_driver to define its DAI and PCM capabilities and operations. This struct is exported so that it can be registered with the core by your machine driver. e.g. -struct snd_soc_codec_dai wm8731_dai = { - .name = "WM8731", - /* playback capabilities */ +static struct snd_soc_dai_ops wm8731_dai_ops = { + .prepare = wm8731_pcm_prepare, + .hw_params = wm8731_hw_params, + .shutdown = wm8731_shutdown, + .digital_mute = wm8731_mute, + .set_sysclk = wm8731_set_dai_sysclk, + .set_fmt = wm8731_set_dai_fmt, +}; + +struct snd_soc_dai_driver wm8731_dai = { + .name = "wm8731-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, - /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, - /* pcm operations - see section 4 below */ - .ops = { - .prepare = wm8731_pcm_prepare, - .hw_params = wm8731_hw_params, - .shutdown = wm8731_shutdown, - }, - /* DAI operations - see DAI.txt */ - .dai_ops = { - .digital_mute = wm8731_mute, - .set_sysclk = wm8731_set_dai_sysclk, - .set_fmt = wm8731_set_dai_fmt, - } + .ops = &wm8731_dai_ops, + .symmetric_rates = 1, }; -EXPORT_SYMBOL_GPL(wm8731_dai); 2 - Codec control IO @@ -186,13 +182,14 @@ when the mute is applied or freed. i.e. -static int wm8974_mute(struct snd_soc_codec *codec, - struct snd_soc_codec_dai *dai, int mute) +static int wm8974_mute(struct snd_soc_dai *dai, int mute) { - u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf; - if(mute) - wm8974_write(codec, WM8974_DAC, mute_reg | 0x40); + struct snd_soc_codec *codec = dai->codec; + u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf; + + if (mute) + snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40); else - wm8974_write(codec, WM8974_DAC, mute_reg); + snd_soc_write(codec, WM8974_DAC, mute_reg); return 0; } diff --git a/Documentation/sound/alsa/soc/machine.txt b/Documentation/sound/alsa/soc/machine.txt index 2524c75557df..3e2ec9cbf397 100644 --- a/Documentation/sound/alsa/soc/machine.txt +++ b/Documentation/sound/alsa/soc/machine.txt @@ -12,6 +12,8 @@ the following struct:- struct snd_soc_card { char *name; + ... + int (*probe)(struct platform_device *pdev); int (*remove)(struct platform_device *pdev); @@ -22,12 +24,13 @@ struct snd_soc_card { int (*resume_pre)(struct platform_device *pdev); int (*resume_post)(struct platform_device *pdev); - /* machine stream operations */ - struct snd_soc_ops *ops; + ... /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; int num_links; + + ... }; probe()/remove() @@ -42,11 +45,6 @@ of any machine audio tasks that have to be done before or after the codec, DAIs and DMA is suspended and resumed. Optional. -Machine operations ------------------- -The machine specific audio operations can be set here. Again this is optional. - - Machine DAI Configuration ------------------------- The machine DAI configuration glues all the codec and CPU DAIs together. It can @@ -61,8 +59,10 @@ struct snd_soc_dai_link is used to set up each DAI in your machine. e.g. static struct snd_soc_dai_link corgi_dai = { .name = "WM8731", .stream_name = "WM8731", - .cpu_dai = &pxa_i2s_dai, - .codec_dai = &wm8731_dai, + .cpu_dai_name = "pxa-is2-dai", + .codec_dai_name = "wm8731-hifi", + .platform_name = "pxa-pcm-audio", + .codec_name = "wm8713-codec.0-001a", .init = corgi_wm8731_init, .ops = &corgi_ops, }; @@ -77,26 +77,6 @@ static struct snd_soc_card snd_soc_corgi = { }; -Machine Audio Subsystem ------------------------ - -The machine soc device glues the platform, machine and codec driver together. -Private data can also be set here. e.g. - -/* corgi audio private data */ -static struct wm8731_setup_data corgi_wm8731_setup = { - .i2c_address = 0x1b, -}; - -/* corgi audio subsystem */ -static struct snd_soc_device corgi_snd_devdata = { - .machine = &snd_soc_corgi, - .platform = &pxa2xx_soc_platform, - .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &corgi_wm8731_setup, -}; - - Machine Power Map ----------------- diff --git a/Documentation/sound/alsa/soc/platform.txt b/Documentation/sound/alsa/soc/platform.txt index 06d835987c6a..d57efad37e0a 100644 --- a/Documentation/sound/alsa/soc/platform.txt +++ b/Documentation/sound/alsa/soc/platform.txt @@ -20,9 +20,10 @@ struct snd_soc_ops { int (*trigger)(struct snd_pcm_substream *, int); }; -The platform driver exports its DMA functionality via struct snd_soc_platform:- +The platform driver exports its DMA functionality via struct +snd_soc_platform_driver:- -struct snd_soc_platform { +struct snd_soc_platform_driver { char *name; int (*probe)(struct platform_device *pdev); @@ -34,6 +35,13 @@ struct snd_soc_platform { int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *); void (*pcm_free)(struct snd_pcm *); + /* + * For platform caused delay reporting. + * Optional. + */ + snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *, + struct snd_soc_dai *); + /* platform stream ops */ struct snd_pcm_ops *pcm_ops; }; -- cgit v1.2.3 From 15d2e22b820bad62854d6ad99d8af8320adf4a91 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 11 Jan 2011 23:08:19 -0500 Subject: ASoC: Blackfin TDM: fix missed snd_soc_dai_get_drvdata update One spot was missed in this driver when converting from snd_soc_dai.private_data to snd_soc_dai_get_drvdata. Signed-off-by: Mike Frysinger Acked-by: Liam Girdwood Signed-off-by: Mark Brown Cc: stable@kernel.org --- sound/soc/blackfin/bf5xx-tdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 125123929f16..b2cf239f20bc 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -210,7 +210,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, #ifdef CONFIG_PM static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) { - struct sport_device *sport = dai->private_data; + struct sport_device *sport = snd_soc_dai_get_drvdata(dai); if (!dai->active) return 0; -- cgit v1.2.3 From e9c2048915048d605fd76539ddd96f00d593e1eb Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 11 Jan 2011 19:57:33 -0500 Subject: ASoC: Blackfin AC97: fix build error after multi-component update We need to tweak how we query the active capture/playback state after the recent overhauls of common code. Signed-off-by: Mike Frysinger Acked-by: Liam Girdwood Signed-off-by: Mark Brown Cc: stable@kernel.org --- sound/soc/blackfin/bf5xx-ac97.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index c5f856ec27ca..ffbac26b9bce 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -260,9 +260,9 @@ static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) pr_debug("%s : sport %d\n", __func__, dai->id); if (!dai->active) return 0; - if (dai->capture.active) + if (dai->capture_active) sport_rx_stop(sport); - if (dai->playback.active) + if (dai->playback_active) sport_tx_stop(sport); return 0; } -- cgit v1.2.3 From 950a95d4e2e2c3a9fb0daceaaf55b969e4710ce7 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 12 Jan 2011 02:59:55 -0500 Subject: ASoC: Blackfin TDM: use external frame syncs We don't want to use internal frame syncs otherwise we sometimes get out of sync, so don't enable them when setting up the SPORT. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/blackfin/bf5xx-tdm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index b2cf239f20bc..5515ac9e05c7 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c @@ -235,13 +235,13 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai) ret = -EBUSY; } - ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0); + ret = sport_config_rx(sport, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; } - ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0); + ret = sport_config_tx(sport, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; @@ -303,14 +303,14 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev) goto sport_config_err; } - ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0); + ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; goto sport_config_err; } - ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0); + ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); ret = -EBUSY; -- cgit v1.2.3 From 91056acbcb6f58265698a091a1a211f994fdb579 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 11 Jan 2011 20:04:28 -0500 Subject: ASoC: Blackfin: fix DAI/SPORT config dependency issues While I2S/TDM/AC97 DAI is built-in, others are compiled as modules, SND_BF5XX_SOC_SPORT will be module, then DAI can't get some symbols. Except that, SND_BF5XX_AC97 depends on SND_BF5XX_SOC_AC97 too. Signed-off-by: Barry Song Signed-off-by: Mike Frysinger Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/blackfin/Kconfig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 3abeeddc67d3..ae403597fd31 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -1,6 +1,7 @@ config SND_BF5XX_I2S tristate "SoC I2S Audio for the ADI BF5xx chip" depends on BLACKFIN + select SND_BF5XX_SOC_SPORT help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in I2S @@ -35,6 +36,7 @@ config SND_BFIN_AD73311_SE config SND_BF5XX_TDM tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip" depends on (BLACKFIN && SND_SOC) + select SND_BF5XX_SOC_SPORT help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in TDM @@ -61,6 +63,10 @@ config SND_BF5XX_SOC_AD193X config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN + select AC97_BUS + select SND_SOC_AC97_BUS + select SND_BF5XX_SOC_SPORT + select SND_BF5XX_SOC_AC97 help Say Y or M if you want to add support for codecs attached to the Blackfin SPORT (synchronous serial ports) interface in slot 16 @@ -122,17 +128,12 @@ config SND_BF5XX_SOC_SPORT config SND_BF5XX_SOC_I2S tristate - select SND_BF5XX_SOC_SPORT config SND_BF5XX_SOC_TDM tristate - select SND_BF5XX_SOC_SPORT config SND_BF5XX_SOC_AC97 tristate - select AC97_BUS - select SND_SOC_AC97_BUS - select SND_BF5XX_SOC_SPORT config SND_BF5XX_SPORT_NUM int "Set a SPORT for Sound chip" -- cgit v1.2.3 From 7cbf70041db039532d6b8972e88164ed45ae6460 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 18 Jan 2011 16:54:24 +0200 Subject: ASoC: PXA: Fix jack detection on Zipit Z2 Fix jack detection on Zipit Z2, otherwise it disables headphones output when jack is connected Signed-off-by: Vasily Khoruzhick Acked-by: Marek Vasut Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/pxa/z2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 2d4f896d7fec..ca0d7e342407 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -104,6 +104,7 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { .name = "hsdet-gpio", .report = SND_JACK_HEADSET, .debounce_time = 200, + .invert = 1, }, }; -- cgit v1.2.3 From c88c2823e87dd6f8214b8b8cdc36d45f205a8077 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Tue, 18 Jan 2011 16:54:25 +0200 Subject: ASoC: PXA: Fix codec address on Zipit Z2 WM8750 address is 0x1b, not 0x1a. Without this fix ALSA detects no sound cards on Zipit Signed-off-by: Vasily Khoruzhick Acked-by: Marek Vasut Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- sound/soc/pxa/z2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index ca0d7e342407..3ceaef68e01d 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -193,7 +193,7 @@ static struct snd_soc_dai_link z2_dai = { .cpu_dai_name = "pxa2xx-i2s", .codec_dai_name = "wm8750-hifi", .platform_name = "pxa-pcm-audio", - .codec_name = "wm8750-codec.0-001a", + .codec_name = "wm8750-codec.0-001b", .init = z2_wm8750_init, .ops = &z2_ops, }; -- cgit v1.2.3 From d8328f3b85f34c0dc5866f10bf6e7842fb511d62 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Sat, 15 Jan 2011 21:32:01 -0700 Subject: OMAP: counter_32k: init clocksource as part of machine timer init After commit dc548fbbd2ecd0fc3b02301d551e5f8e19ae58fd ("ARM: omap: convert sched_clock() to use new infrastructure"), OMAPs that use the 32KiHz "synchronization timer" as their clocksource crash during boot: [ 0.000000] OMAP clockevent source: GPTIMER1 at 32768 Hz [ 0.000000] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 0.000000] pgd = c0004000 [ 0.000000] [00000000] *pgd=00000000 [ 0.000000] Internal error: Oops: 80000005 [#1] SMP [ 0.000000] last sysfs file: [ 0.000000] Modules linked in: [ 0.000000] CPU: 0 Tainted: G W (2.6.37-07734-g2467802 #7) [ 0.000000] PC is at 0x0 [ 0.000000] LR is at sched_clock_poll+0x2c/0x3c [ 0.000000] pc : [<00000000>] lr : [] psr: 600001d3 [ 0.000000] sp : c058bfd0 ip : c058a000 fp : 00000000 [ 0.000000] r10: 00000000 r9 : 411fc092 r8 : 800330c8 [ 0.000000] r7 : c05a08e0 r6 : c0034c48 r5 : c05ffc40 r4 : c0034c4c [ 0.000000] r3 : c05ffe6c r2 : c05a0bc0 r1 : c059f098 r0 : 00000000 [ 0.000000] Flags: nZCv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel [ 0.000000] Control: 10c53c7f Table: 8000404a DAC: 00000017 This is due to the recent ARM init_sched_clock() changes and the late initialization of the counter_32k clock source. More information here: http://marc.info/?l=linux-omap&m=129513468605208&w=2 Fix by initializing the counter_32k clocksource during the machine timer initialization. Reported-by: Russell King Tested-by: Thomas Weber Signed-off-by: Paul Walmsley --- arch/arm/mach-omap1/time.c | 7 +++++++ arch/arm/mach-omap2/timer-gp.c | 10 ++++++++-- arch/arm/plat-omap/counter_32k.c | 3 +-- arch/arm/plat-omap/include/plat/common.h | 1 + 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index ed7a61ff916a..6ec65e599997 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -244,6 +244,13 @@ static void __init omap_timer_init(void) omap_init_mpu_timer(rate); omap_init_clocksource(rate); + /* + * XXX Since this file seems to deal mostly with the MPU timer, + * this doesn't seem like the correct place for the sync timer + * clocksource init. + */ + if (!cpu_is_omap7xx() && !cpu_is_omap15xx()) + omap_init_clocksource_32k(); } struct sys_timer omap_timer = { diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c index 4e48e786bec7..7b7c2683ae7b 100644 --- a/arch/arm/mach-omap2/timer-gp.c +++ b/arch/arm/mach-omap2/timer-gp.c @@ -42,6 +42,8 @@ #include "timer-gp.h" +#include + /* MAX_GPTIMER_ID: number of GPTIMERs on the chip */ #define MAX_GPTIMER_ID 12 @@ -176,10 +178,14 @@ static void __init omap2_gp_clockevent_init(void) /* * When 32k-timer is enabled, don't use GPTimer for clocksource * instead, just leave default clocksource which uses the 32k - * sync counter. See clocksource setup in see plat-omap/common.c. + * sync counter. See clocksource setup in plat-omap/counter_32k.c */ -static inline void __init omap2_gp_clocksource_init(void) {} +static void __init omap2_gp_clocksource_init(void) +{ + omap_init_clocksource_32k(); +} + #else /* * clocksource diff --git a/arch/arm/plat-omap/counter_32k.c b/arch/arm/plat-omap/counter_32k.c index ea4644021fb9..0367998ff685 100644 --- a/arch/arm/plat-omap/counter_32k.c +++ b/arch/arm/plat-omap/counter_32k.c @@ -160,7 +160,7 @@ void read_persistent_clock(struct timespec *ts) *ts = *tsp; } -static int __init omap_init_clocksource_32k(void) +int __init omap_init_clocksource_32k(void) { static char err[] __initdata = KERN_ERR "%s: can't register clocksource!\n"; @@ -195,7 +195,6 @@ static int __init omap_init_clocksource_32k(void) } return 0; } -arch_initcall(omap_init_clocksource_32k); #endif /* !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX)) */ diff --git a/arch/arm/plat-omap/include/plat/common.h b/arch/arm/plat-omap/include/plat/common.h index 6b8088ec74af..84c707f713b1 100644 --- a/arch/arm/plat-omap/include/plat/common.h +++ b/arch/arm/plat-omap/include/plat/common.h @@ -35,6 +35,7 @@ struct sys_timer; extern void omap_map_common_io(void); extern struct sys_timer omap_timer; +extern int __init omap_init_clocksource_32k(void); extern void omap_reserve(void); -- cgit v1.2.3 From 56bc78d414aa79bce42836df6efe9b9bef92a59d Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 17 Jan 2011 13:28:17 -0700 Subject: OMAP4: clockdomain: bypass unimplemented wake-up dependency functions on OMAP4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 56a6a19dffda6b75cef8d4183c7c6ff650025cbd ("omap2plus: prm: Trvial build break fix for undefined reference to 'omap2_prm_read_mod_reg'") generates a lot of warnings on boot since clockdomain functions that manipulate wake-up dependencies are not implemented yet on OMAP4 for 2.6.38. This patch bypasses the OMAP2/3 functions on OMAP4, which in turn avoids the warnings when the functions would attempt to call the underlying OMAP2/3 PRCM functions. A one-line warning is still logged from the clockdomain code that the OMAP4 wake-up dependency code is not yet implemented. A clockdomain wake-up and sleep dependency implementation for OMAP4 from Rajendra should be possible to merge during the 2.6.39 merge window: http://www.mail-archive.com/linux-omap@vger.kernel.org/msg41748.html http://www.mail-archive.com/linux-omap@vger.kernel.org/msg42222.html Reported-by: Russell King Cc: Rajendra Nayak Cc: Benoît Cousson Cc: Santosh Shilimkar Acked-by: Santosh Shilimkar Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/clockdomain.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index e20b98636ab4..58e42f76603f 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c @@ -423,6 +423,12 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) { struct clkdm_dep *cd; + if (!cpu_is_omap24xx() && !cpu_is_omap34xx()) { + pr_err("clockdomain: %s/%s: %s: not yet implemented\n", + clkdm1->name, clkdm2->name, __func__); + return -EINVAL; + } + if (!clkdm1 || !clkdm2) return -EINVAL; @@ -458,6 +464,12 @@ int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) { struct clkdm_dep *cd; + if (!cpu_is_omap24xx() && !cpu_is_omap34xx()) { + pr_err("clockdomain: %s/%s: %s: not yet implemented\n", + clkdm1->name, clkdm2->name, __func__); + return -EINVAL; + } + if (!clkdm1 || !clkdm2) return -EINVAL; @@ -500,6 +512,12 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) if (!clkdm1 || !clkdm2) return -EINVAL; + if (!cpu_is_omap24xx() && !cpu_is_omap34xx()) { + pr_err("clockdomain: %s/%s: %s: not yet implemented\n", + clkdm1->name, clkdm2->name, __func__); + return -EINVAL; + } + cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); if (IS_ERR(cd)) { pr_debug("clockdomain: hardware cannot set/clear wake up of " @@ -527,6 +545,12 @@ int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) struct clkdm_dep *cd; u32 mask = 0; + if (!cpu_is_omap24xx() && !cpu_is_omap34xx()) { + pr_err("clockdomain: %s: %s: not yet implemented\n", + clkdm->name, __func__); + return -EINVAL; + } + if (!clkdm) return -EINVAL; @@ -830,8 +854,7 @@ void omap2_clkdm_allow_idle(struct clockdomain *clkdm) * dependency code and data for OMAP4. */ if (cpu_is_omap44xx()) { - WARN_ONCE(1, "clockdomain: OMAP4 wakeup/sleep dependency " - "support is not yet implemented\n"); + pr_err("clockdomain: %s: OMAP4 wakeup/sleep dependency support: not yet implemented\n", clkdm->name); } else { if (atomic_read(&clkdm->usecount) > 0) _clkdm_add_autodeps(clkdm); @@ -872,8 +895,7 @@ void omap2_clkdm_deny_idle(struct clockdomain *clkdm) * dependency code and data for OMAP4. */ if (cpu_is_omap44xx()) { - WARN_ONCE(1, "clockdomain: OMAP4 wakeup/sleep dependency " - "support is not yet implemented\n"); + pr_err("clockdomain: %s: OMAP4 wakeup/sleep dependency support: not yet implemented\n", clkdm->name); } else { if (atomic_read(&clkdm->usecount) > 0) _clkdm_del_autodeps(clkdm); -- cgit v1.2.3 From bc9fcaf3697bb4f4a7cda14d31ea4c647a6b9030 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 17 Jan 2011 13:28:16 -0700 Subject: OMAP: PRCM: remove duplicated headers A few headers are included twice, remove them. Found the following errors using make includecheck: arch/arm/mach-omap2/clock44xx_data.c: prm44xx.h is included more than once. arch/arm/mach-omap2/clockdomains44xx_data.c: cm1_44xx.h is included more than once. arch/arm/mach-omap2/clockdomains44xx_data.c: cm2_44xx.h is included more than once. arch/arm/mach-omap2/powerdomain2xxx_3xxx.c: prm-regbits-34xx.h is included more than once. Cc: Paul Walmsley Cc: Tony Lindgren Cc: Russell King Signed-off-by: Felipe Balbi [paul@pwsan.com: dropped lists from patch cc:s; tweaked subject line] Signed-off-by: Paul Walmsley --- arch/arm/mach-omap2/clock44xx_data.c | 1 - arch/arm/mach-omap2/clockdomains44xx_data.c | 2 -- arch/arm/mach-omap2/powerdomain2xxx_3xxx.c | 1 - 3 files changed, 4 deletions(-) diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c index e8cb32fd7f13..de9ec8ddd2ae 100644 --- a/arch/arm/mach-omap2/clock44xx_data.c +++ b/arch/arm/mach-omap2/clock44xx_data.c @@ -34,7 +34,6 @@ #include "cm2_44xx.h" #include "cm-regbits-44xx.h" #include "prm44xx.h" -#include "prm44xx.h" #include "prm-regbits-44xx.h" #include "control.h" #include "scrm44xx.h" diff --git a/arch/arm/mach-omap2/clockdomains44xx_data.c b/arch/arm/mach-omap2/clockdomains44xx_data.c index 51920fc7fc52..10622c914abc 100644 --- a/arch/arm/mach-omap2/clockdomains44xx_data.c +++ b/arch/arm/mach-omap2/clockdomains44xx_data.c @@ -30,8 +30,6 @@ #include "cm1_44xx.h" #include "cm2_44xx.h" -#include "cm1_44xx.h" -#include "cm2_44xx.h" #include "cm-regbits-44xx.h" #include "prm44xx.h" #include "prcm44xx.h" diff --git a/arch/arm/mach-omap2/powerdomain2xxx_3xxx.c b/arch/arm/mach-omap2/powerdomain2xxx_3xxx.c index d5233890370c..cf600e22bf8e 100644 --- a/arch/arm/mach-omap2/powerdomain2xxx_3xxx.c +++ b/arch/arm/mach-omap2/powerdomain2xxx_3xxx.c @@ -19,7 +19,6 @@ #include #include "powerdomain.h" -#include "prm-regbits-34xx.h" #include "prm.h" #include "prm-regbits-24xx.h" #include "prm-regbits-34xx.h" -- cgit v1.2.3 From f376ea1780085196fcfff6bc27e8f6ddb324ae57 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 18 Jan 2011 13:25:39 -0800 Subject: omap1: Fix sched_clock for the MPU timer Otherwise systems using the MPU timer will hang. Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/time.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index 6ec65e599997..a39a15e4f3f9 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -44,11 +44,14 @@ #include #include #include +#include #include #include #include #include +#include + #include #include @@ -67,7 +70,7 @@ typedef struct { ((volatile omap_mpu_timer_regs_t*)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ (n)*OMAP_MPU_TIMER_OFFSET)) -static inline unsigned long omap_mpu_timer_read(int nr) +static inline unsigned long notrace omap_mpu_timer_read(int nr) { volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); return timer->read_tim; @@ -212,6 +215,14 @@ static struct clocksource clocksource_mpu = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; +static DEFINE_CLOCK_DATA(cd); + +static void notrace mpu_update_sched_clock(void) +{ + u32 cyc = mpu_read(&clocksource_mpu); + update_sched_clock(&cd, cyc, (u32)~0); +} + static void __init omap_init_clocksource(unsigned long rate) { static char err[] __initdata = KERN_ERR @@ -219,6 +230,7 @@ static void __init omap_init_clocksource(unsigned long rate) setup_irq(INT_TIMER2, &omap_mpu_timer2_irq); omap_mpu_timer_start(1, ~0, 1); + init_sched_clock(&cd, mpu_update_sched_clock, 32, rate); if (clocksource_register_hz(&clocksource_mpu, rate)) printk(err, clocksource_mpu.name); -- cgit v1.2.3 From 35576eab390df313095306e2a8216134910e7014 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 Jan 2011 09:22:47 +0900 Subject: trusted-keys: another free memory bugfix TSS_rawhmac() forgot to call va_end()/kfree() when data == NULL and forgot to call va_end() when crypto_shash_update() < 0. Fix these bugs by escaping from the loop using "break" (rather than "return"/"goto") in order to make sure that va_end()/kfree() are always called. Signed-off-by: Tetsuo Handa Reviewed-by: Jesper Juhl Acked-by: Mimi Zohar Acked-by: David Howells Signed-off-by: James Morris --- security/keys/trusted_defined.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c index 932f8687df16..7b2179589063 100644 --- a/security/keys/trusted_defined.c +++ b/security/keys/trusted_defined.c @@ -101,11 +101,13 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, if (dlen == 0) break; data = va_arg(argp, unsigned char *); - if (data == NULL) - return -EINVAL; + if (data == NULL) { + ret = -EINVAL; + break; + } ret = crypto_shash_update(&sdesc->shash, data, dlen); if (ret < 0) - goto out; + break; } va_end(argp); if (!ret) -- cgit v1.2.3 From 0e7491f685cbc962f2ef977f7b5f8ed0b3100e88 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 Jan 2011 09:25:34 +0900 Subject: trusted-keys: check for NULL before using it TSS_rawhmac() checks for data != NULL before using it. We should do the same thing for TSS_authhmac(). Signed-off-by: Tetsuo Handa Reviewed-by: Jesper Juhl Acked-by: Mimi Zohar Acked-by: David Howells Signed-off-by: James Morris --- security/keys/trusted_defined.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c index 7b2179589063..f7d06776faf0 100644 --- a/security/keys/trusted_defined.c +++ b/security/keys/trusted_defined.c @@ -148,6 +148,11 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, if (dlen == 0) break; data = va_arg(argp, unsigned char *); + if (!data) { + ret = -EINVAL; + va_end(argp); + goto out; + } ret = crypto_shash_update(&sdesc->shash, data, dlen); if (ret < 0) { va_end(argp); -- cgit v1.2.3 From 154a96bfcd53b8e5020718c64769e542c44788b9 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 Jan 2011 09:27:27 +0900 Subject: trusted-keys: avoid scattring va_end() We can avoid scattering va_end() within the va_start(); for (;;) { } va_end(); loop, assuming that crypto_shash_init()/crypto_shash_update() return 0 on success and negative value otherwise. Make TSS_authhmac()/TSS_checkhmac1()/TSS_checkhmac2() similar to TSS_rawhmac() by removing "va_end()/goto" from the loop. Signed-off-by: Tetsuo Handa Reviewed-by: Jesper Juhl Acked-by: Mimi Zohar Acked-by: David Howells Signed-off-by: James Morris --- security/keys/trusted_defined.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/security/keys/trusted_defined.c b/security/keys/trusted_defined.c index f7d06776faf0..2836c6dc18a3 100644 --- a/security/keys/trusted_defined.c +++ b/security/keys/trusted_defined.c @@ -150,17 +150,15 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key, data = va_arg(argp, unsigned char *); if (!data) { ret = -EINVAL; - va_end(argp); - goto out; + break; } ret = crypto_shash_update(&sdesc->shash, data, dlen); - if (ret < 0) { - va_end(argp); - goto out; - } + if (ret < 0) + break; } va_end(argp); - ret = crypto_shash_final(&sdesc->shash, paramdigest); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, paramdigest); if (!ret) ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, paramdigest, TPM_NONCE_SIZE, h1, @@ -229,13 +227,12 @@ static int TSS_checkhmac1(unsigned char *buffer, break; dpos = va_arg(argp, unsigned int); ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); - if (ret < 0) { - va_end(argp); - goto out; - } + if (ret < 0) + break; } va_end(argp); - ret = crypto_shash_final(&sdesc->shash, paramdigest); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, paramdigest); if (ret < 0) goto out; @@ -323,13 +320,12 @@ static int TSS_checkhmac2(unsigned char *buffer, break; dpos = va_arg(argp, unsigned int); ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); - if (ret < 0) { - va_end(argp); - goto out; - } + if (ret < 0) + break; } va_end(argp); - ret = crypto_shash_final(&sdesc->shash, paramdigest); + if (!ret) + ret = crypto_shash_final(&sdesc->shash, paramdigest); if (ret < 0) goto out; -- cgit v1.2.3 From be33b76a974cdb4ceadc1a12fb79cc97bcfeea37 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 16 Jan 2011 20:37:52 +0100 Subject: ACPICA: Fix memory leak in acpi_ev_asynch_execute_gpe_method(). We will leak the memory allocated to 'local_gpe_event_info' if 'acpi_ut_acquire_mutex()' fails or if 'acpi_ev_valid_gpe_event()' fails in drivers/acpi/acpica/evgpe.c::acpi_ev_asynch_execute_gpe_method(). Signed-off-by: Jesper Juhl Reviewed-by: Rafael Wysocki Signed-off-by: Len Brown --- drivers/acpi/acpica/evgpe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 7c339d34ab42..b6de1fb34f44 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -471,6 +471,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); if (ACPI_FAILURE(status)) { + ACPI_FREE(local_gpe_event_info); return_VOID; } @@ -478,6 +479,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) if (!acpi_ev_valid_gpe_event(gpe_event_info)) { status = acpi_ut_release_mutex(ACPI_MTX_EVENTS); + ACPI_FREE(local_gpe_event_info); return_VOID; } -- cgit v1.2.3 From 5d3131f5b0ae6303d042fd91ed9147ad4ae4bf6d Mon Sep 17 00:00:00 2001 From: Dana Myers Date: Wed, 12 Jan 2011 09:09:31 +0800 Subject: ACPICA: Fix namespace race condition Fixes a race condition between method execution and namespace walks that can possibly fault. Problem was apparently introduced in version 20100528 as a result of a performance optimization that reduces the number of namespace walks upon method exit by using the delete_namespace_subtree function instead of the delete_namespace_by_owner function used previously. Bug is in the delete_namespace_subtree function. Signed-off-by: Dana Myers Signed-off-by: Bob Moore Signed-off-by: Lin Ming Reviewed-by: Rafael Wysocki Signed-off-by: Len Brown --- drivers/acpi/acpica/nsalloc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index 1e5ff803d9ad..222a23e838d6 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -341,6 +341,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) { struct acpi_namespace_node *child_node = NULL; u32 level = 1; + acpi_status status; ACPI_FUNCTION_TRACE(ns_delete_namespace_subtree); @@ -348,6 +349,13 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) return_VOID; } + /* Lock namespace for possible update */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + /* * Traverse the tree of objects until we bubble back up * to where we started. @@ -397,6 +405,7 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) } } + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_VOID; } -- cgit v1.2.3 From 672af843abfc9a41c7ec792722e04b6c68a3cfea Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 12 Jan 2011 09:13:31 +0800 Subject: ACPICA: Debugger: Lock namespace for duration of a namespace dump Prevents issues if the namespace is changing underneath the debugger. Especially temporary nodes, since the debugger displays these also. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Reviewed-by: Rafael Wysocki Signed-off-by: Len Brown --- drivers/acpi/acpica/nsdump.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index a54dc39e304b..82693c3eb257 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -624,9 +624,22 @@ acpi_ns_dump_objects(acpi_object_type type, acpi_owner_id owner_id, acpi_handle start_handle) { struct acpi_walk_info info; + acpi_status status; ACPI_FUNCTION_ENTRY(); + /* + * Just lock the entire namespace for the duration of the dump. + * We don't want any changes to the namespace during this time, + * especially the temporary nodes since we are going to display + * them also. + */ + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + acpi_os_printf("Could not acquire namespace mutex\n"); + return; + } + info.debug_level = ACPI_LV_TABLES; info.owner_id = owner_id; info.display_type = display_type; @@ -636,6 +649,8 @@ acpi_ns_dump_objects(acpi_object_type type, ACPI_NS_WALK_TEMP_NODES, acpi_ns_dump_one_object, NULL, (void *)&info, NULL); + + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); } #endif /* ACPI_FUTURE_USAGE */ -- cgit v1.2.3 From 262948428878fb340127faca1791acb17146122e Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Wed, 12 Jan 2011 09:19:43 +0800 Subject: ACPICA: Fix issues/fault with automatic "serialized" method support History: This support changes a method to "serialized" on the fly if the method generates an AE_ALREADY_EXISTS error, indicating the possibility that it cannot handle reentrancy. This fix repairs a couple of issues seen in the field, especially on machines with many cores. 1) Delete method children only upon the exit of the last thread, so as to not delete objects out from under running threads. 2) Set the "serialized" bit for the method only upon the exit of the last thread, so as to not cause deadlock when running threads attempt to exit. 3) Cleanup the use of the AML "MethodFlags" and internal method flags so that there is no longer any confustion between the two. Reported-by: Dana Myers Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/acobject.h | 14 +++++++--- drivers/acpi/acpica/amlcode.h | 8 +----- drivers/acpi/acpica/dsmethod.c | 62 ++++++++++++++++++++++++++++++++---------- drivers/acpi/acpica/evrgnini.c | 4 +-- drivers/acpi/acpica/excreate.c | 8 +++--- drivers/acpi/acpica/exdump.c | 2 +- drivers/acpi/acpica/nsaccess.c | 6 ++-- drivers/acpi/acpica/nsalloc.c | 4 +-- drivers/acpi/acpica/nseval.c | 2 +- drivers/acpi/acpica/nsxfname.c | 5 ++-- drivers/acpi/acpica/psloop.c | 2 +- drivers/acpi/acpica/psparse.c | 25 ++++++----------- drivers/acpi/acpica/psxface.c | 7 ++--- 13 files changed, 85 insertions(+), 64 deletions(-) diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index bdbfaf22bd14..efaf9c6090f5 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -97,8 +97,6 @@ #define AOPOBJ_OBJECT_INITIALIZED 0x08 /* Region is initialized, _REG was run */ #define AOPOBJ_SETUP_COMPLETE 0x10 /* Region setup is complete */ #define AOPOBJ_INVALID 0x20 /* Host OS won't allow a Region address */ -#define AOPOBJ_MODULE_LEVEL 0x40 /* Method is actually module-level code */ -#define AOPOBJ_MODIFIED_NAMESPACE 0x80 /* Method modified the namespace */ /****************************************************************************** * @@ -175,7 +173,7 @@ struct acpi_object_region { }; struct acpi_object_method { - ACPI_OBJECT_COMMON_HEADER u8 method_flags; + ACPI_OBJECT_COMMON_HEADER u8 info_flags; u8 param_count; u8 sync_level; union acpi_operand_object *mutex; @@ -183,13 +181,21 @@ struct acpi_object_method { union { ACPI_INTERNAL_METHOD implementation; union acpi_operand_object *handler; - } extra; + } dispatch; u32 aml_length; u8 thread_count; acpi_owner_id owner_id; }; +/* Flags for info_flags field above */ + +#define ACPI_METHOD_MODULE_LEVEL 0x01 /* Method is actually module-level code */ +#define ACPI_METHOD_INTERNAL_ONLY 0x02 /* Method is implemented internally (_OSI) */ +#define ACPI_METHOD_SERIALIZED 0x04 /* Method is serialized */ +#define ACPI_METHOD_SERIALIZED_PENDING 0x08 /* Method is to be marked serialized */ +#define ACPI_METHOD_MODIFIED_NAMESPACE 0x10 /* Method modified the namespace */ + /****************************************************************************** * * Objects that can be notified. All share a common notify_info area. diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index 1f484ba228fc..ac1f561ce392 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -480,16 +480,10 @@ typedef enum { AML_FIELD_ATTRIB_SMB_BLOCK_CALL = 0x0D } AML_ACCESS_ATTRIBUTE; -/* Bit fields in method_flags byte */ +/* Bit fields in the AML method_flags byte */ #define AML_METHOD_ARG_COUNT 0x07 #define AML_METHOD_SERIALIZED 0x08 #define AML_METHOD_SYNC_LEVEL 0xF0 -/* METHOD_FLAGS_ARG_COUNT is not used internally, define additional flags */ - -#define AML_METHOD_INTERNAL_ONLY 0x01 -#define AML_METHOD_RESERVED1 0x02 -#define AML_METHOD_RESERVED2 0x04 - #endif /* __AMLCODE_H__ */ diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index d94dd8974b55..5a79f401a113 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -43,7 +43,6 @@ #include #include "accommon.h" -#include "amlcode.h" #include "acdispat.h" #include "acinterp.h" #include "acnamesp.h" @@ -201,7 +200,7 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, /* * If this method is serialized, we need to acquire the method mutex. */ - if (obj_desc->method.method_flags & AML_METHOD_SERIALIZED) { + if (obj_desc->method.info_flags & ACPI_METHOD_SERIALIZED) { /* * Create a mutex for the method if it is defined to be Serialized * and a mutex has not already been created. We defer the mutex creation @@ -413,8 +412,9 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread, /* Invoke an internal method if necessary */ - if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { - status = obj_desc->method.extra.implementation(next_walk_state); + if (obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) { + status = + obj_desc->method.dispatch.implementation(next_walk_state); if (status == AE_OK) { status = AE_CTRL_TERMINATE; } @@ -579,11 +579,14 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, /* * Delete any namespace objects created anywhere within the - * namespace by the execution of this method. Unless this method - * is a module-level executable code method, in which case we - * want make the objects permanent. + * namespace by the execution of this method. Unless: + * 1) This method is a module-level executable code method, in which + * case we want make the objects permanent. + * 2) There are other threads executing the method, in which case we + * will wait until the last thread has completed. */ - if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) { + if (!(method_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) + && (method_desc->method.thread_count == 1)) { /* Delete any direct children of (created by) this method */ @@ -593,12 +596,17 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, /* * Delete any objects that were created by this method * elsewhere in the namespace (if any were created). + * Use of the ACPI_METHOD_MODIFIED_NAMESPACE optimizes the + * deletion such that we don't have to perform an entire + * namespace walk for every control method execution. */ if (method_desc->method. - flags & AOPOBJ_MODIFIED_NAMESPACE) { + info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) { acpi_ns_delete_namespace_by_owner(method_desc-> method. owner_id); + method_desc->method.info_flags &= + ~ACPI_METHOD_MODIFIED_NAMESPACE; } } } @@ -629,19 +637,43 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, * Serialized if it appears that the method is incorrectly written and * does not support multiple thread execution. The best example of this * is if such a method creates namespace objects and blocks. A second - * thread will fail with an AE_ALREADY_EXISTS exception + * thread will fail with an AE_ALREADY_EXISTS exception. * * This code is here because we must wait until the last thread exits - * before creating the synchronization semaphore. + * before marking the method as serialized. */ - if ((method_desc->method.method_flags & AML_METHOD_SERIALIZED) - && (!method_desc->method.mutex)) { - (void)acpi_ds_create_method_mutex(method_desc); + if (method_desc->method. + info_flags & ACPI_METHOD_SERIALIZED_PENDING) { + if (walk_state) { + ACPI_INFO((AE_INFO, + "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error", + walk_state->method_node->name. + ascii)); + } + + /* + * Method tried to create an object twice and was marked as + * "pending serialized". The probable cause is that the method + * cannot handle reentrancy. + * + * The method was created as not_serialized, but it tried to create + * a named object and then blocked, causing the second thread + * entrance to begin and then fail. Workaround this problem by + * marking the method permanently as Serialized when the last + * thread exits here. + */ + method_desc->method.info_flags &= + ~ACPI_METHOD_SERIALIZED_PENDING; + method_desc->method.info_flags |= + ACPI_METHOD_SERIALIZED; + method_desc->method.sync_level = 0; } /* No more threads, we can free the owner_id */ - if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) { + if (! + (method_desc->method. + info_flags & ACPI_METHOD_MODULE_LEVEL)) { acpi_ut_release_owner_id(&method_desc->method.owner_id); } } diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 0b47a6dc9290..2ef32e952559 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -590,9 +590,9 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj, * See acpi_ns_exec_module_code */ if (obj_desc->method. - flags & AOPOBJ_MODULE_LEVEL) { + info_flags & ACPI_METHOD_MODULE_LEVEL) { handler_obj = - obj_desc->method.extra.handler; + obj_desc->method.dispatch.handler; } break; diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 3c61b48c73f5..ffac8c7e54c5 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -482,13 +482,11 @@ acpi_ex_create_method(u8 * aml_start, obj_desc->method.aml_length = aml_length; /* - * Disassemble the method flags. Split off the Arg Count - * for efficiency + * Disassemble the method flags. Split off the arg_count, Serialized + * flag, and sync_level for efficiency. */ method_flags = (u8) operand[1]->integer.value; - obj_desc->method.method_flags = - (u8) (method_flags & ~AML_METHOD_ARG_COUNT); obj_desc->method.param_count = (u8) (method_flags & AML_METHOD_ARG_COUNT); @@ -497,6 +495,8 @@ acpi_ex_create_method(u8 * aml_start, * created for this method when it is parsed. */ if (method_flags & AML_METHOD_SERIALIZED) { + obj_desc->method.info_flags = ACPI_METHOD_SERIALIZED; + /* * ACPI 1.0: sync_level = 0 * ACPI 2.0: sync_level = sync_level in method declaration diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index f067bbb0d961..ff4b1e61c318 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -122,7 +122,7 @@ static struct acpi_exdump_info acpi_ex_dump_event[2] = { static struct acpi_exdump_info acpi_ex_dump_method[9] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_method), NULL}, - {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.method_flags), "Method Flags"}, + {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.info_flags), "Info Flags"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.param_count), "Parameter Count"}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(method.sync_level), "Sync Level"}, diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index 0cd925be5fc1..cede0e3200e4 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -163,9 +163,9 @@ acpi_status acpi_ns_root_initialize(void) #else /* Mark this as a very SPECIAL method */ - obj_desc->method.method_flags = - AML_METHOD_INTERNAL_ONLY; - obj_desc->method.extra.implementation = + obj_desc->method.info_flags = + ACPI_METHOD_INTERNAL_ONLY; + obj_desc->method.dispatch.implementation = acpi_ut_osi_implementation; #endif break; diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index 222a23e838d6..222a9843268d 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -234,8 +234,8 @@ void acpi_ns_install_node(struct acpi_walk_state *walk_state, struct acpi_namesp * modified the namespace. This is used for cleanup when the * method exits. */ - walk_state->method_desc->method.flags |= - AOPOBJ_MODIFIED_NAMESPACE; + walk_state->method_desc->method.info_flags |= + ACPI_METHOD_MODIFIED_NAMESPACE; } } diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index f52829cc294b..a55c0d2295ca 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -389,7 +389,7 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj, * acpi_gbl_root_node->Object is NULL at PASS1. */ if ((type == ACPI_TYPE_DEVICE) && parent_node->object) { - method_obj->method.extra.handler = + method_obj->method.dispatch.handler = parent_node->object->device.handler; } diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index b01e45a415e3..0a26d73e050a 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -603,10 +603,9 @@ acpi_status acpi_install_method(u8 *buffer) method_obj->method.param_count = (u8) (method_flags & AML_METHOD_ARG_COUNT); - method_obj->method.method_flags = (u8) - (method_flags & ~AML_METHOD_ARG_COUNT); - if (method_flags & AML_METHOD_SERIALIZED) { + method_obj->method.info_flags = ACPI_METHOD_SERIALIZED; + method_obj->method.sync_level = (u8) ((method_flags & AML_METHOD_SYNC_LEVEL) >> 4); } diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index 2f2e7760938c..06edeaf3b5e5 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -655,7 +655,7 @@ acpi_ps_link_module_code(union acpi_parse_object *parent_op, method_obj->method.aml_start = aml_start; method_obj->method.aml_length = aml_length; method_obj->method.owner_id = owner_id; - method_obj->method.flags |= AOPOBJ_MODULE_LEVEL; + method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL; /* * Save the parent node in next_object. This is cheating, but we diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 8d81542194d4..3b8de113b571 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -55,7 +55,6 @@ #include "acparser.h" #include "acdispat.h" #include "amlcode.h" -#include "acnamesp.h" #include "acinterp.h" #define _COMPONENT ACPI_PARSER @@ -539,24 +538,16 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state) /* Check for possible multi-thread reentrancy problem */ if ((status == AE_ALREADY_EXISTS) && - (!walk_state->method_desc->method.mutex)) { - ACPI_INFO((AE_INFO, - "Marking method %4.4s as Serialized because of AE_ALREADY_EXISTS error", - walk_state->method_node->name. - ascii)); - + (!(walk_state->method_desc->method. + info_flags & ACPI_METHOD_SERIALIZED))) { /* - * Method tried to create an object twice. The probable cause is - * that the method cannot handle reentrancy. - * - * The method is marked not_serialized, but it tried to create - * a named object, causing the second thread entrance to fail. - * Workaround this problem by marking the method permanently - * as Serialized. + * Method is not serialized and tried to create an object + * twice. The probable cause is that the method cannot + * handle reentrancy. Mark as "pending serialized" now, and + * then mark "serialized" when the last thread exits. */ - walk_state->method_desc->method.method_flags |= - AML_METHOD_SERIALIZED; - walk_state->method_desc->method.sync_level = 0; + walk_state->method_desc->method.info_flags |= + ACPI_METHOD_SERIALIZED_PENDING; } } diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index c42f067cff9d..bc49a80c386d 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -47,7 +47,6 @@ #include "acdispat.h" #include "acinterp.h" #include "actables.h" -#include "amlcode.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psxface") @@ -285,15 +284,15 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) goto cleanup; } - if (info->obj_desc->method.flags & AOPOBJ_MODULE_LEVEL) { + if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) { walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL; } /* Invoke an internal method if necessary */ - if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { + if (info->obj_desc->method.info_flags & ACPI_METHOD_INTERNAL_ONLY) { status = - info->obj_desc->method.extra.implementation(walk_state); + info->obj_desc->method.dispatch.implementation(walk_state); info->return_object = walk_state->return_desc; /* Cleanup states */ -- cgit v1.2.3 From b4e104eaeb8cd4329a23e0e4ebf166681b1d182d Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 17 Jan 2011 11:05:40 +0800 Subject: ACPICA: Update all ACPICA copyrights and signons to 2011 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/accommon.h | 2 +- drivers/acpi/acpica/acconfig.h | 2 +- drivers/acpi/acpica/acdebug.h | 2 +- drivers/acpi/acpica/acdispat.h | 2 +- drivers/acpi/acpica/acevents.h | 2 +- drivers/acpi/acpica/acglobal.h | 2 +- drivers/acpi/acpica/achware.h | 2 +- drivers/acpi/acpica/acinterp.h | 2 +- drivers/acpi/acpica/aclocal.h | 2 +- drivers/acpi/acpica/acmacros.h | 2 +- drivers/acpi/acpica/acnamesp.h | 2 +- drivers/acpi/acpica/acobject.h | 2 +- drivers/acpi/acpica/acopcode.h | 2 +- drivers/acpi/acpica/acparser.h | 2 +- drivers/acpi/acpica/acpredef.h | 2 +- drivers/acpi/acpica/acresrc.h | 2 +- drivers/acpi/acpica/acstruct.h | 2 +- drivers/acpi/acpica/actables.h | 2 +- drivers/acpi/acpica/acutils.h | 2 +- drivers/acpi/acpica/amlcode.h | 2 +- drivers/acpi/acpica/amlresrc.h | 2 +- drivers/acpi/acpica/dsfield.c | 2 +- drivers/acpi/acpica/dsinit.c | 2 +- drivers/acpi/acpica/dsmethod.c | 2 +- drivers/acpi/acpica/dsmthdat.c | 2 +- drivers/acpi/acpica/dsobject.c | 2 +- drivers/acpi/acpica/dsopcode.c | 2 +- drivers/acpi/acpica/dsutils.c | 2 +- drivers/acpi/acpica/dswexec.c | 2 +- drivers/acpi/acpica/dswload.c | 2 +- drivers/acpi/acpica/dswscope.c | 2 +- drivers/acpi/acpica/dswstate.c | 2 +- drivers/acpi/acpica/evevent.c | 2 +- drivers/acpi/acpica/evgpe.c | 2 +- drivers/acpi/acpica/evgpeblk.c | 2 +- drivers/acpi/acpica/evgpeinit.c | 2 +- drivers/acpi/acpica/evgpeutil.c | 2 +- drivers/acpi/acpica/evmisc.c | 2 +- drivers/acpi/acpica/evregion.c | 2 +- drivers/acpi/acpica/evrgnini.c | 2 +- drivers/acpi/acpica/evsci.c | 2 +- drivers/acpi/acpica/evxface.c | 2 +- drivers/acpi/acpica/evxfevnt.c | 2 +- drivers/acpi/acpica/evxfgpe.c | 2 +- drivers/acpi/acpica/evxfregn.c | 2 +- drivers/acpi/acpica/exconfig.c | 2 +- drivers/acpi/acpica/exconvrt.c | 2 +- drivers/acpi/acpica/excreate.c | 2 +- drivers/acpi/acpica/exdebug.c | 2 +- drivers/acpi/acpica/exdump.c | 2 +- drivers/acpi/acpica/exfield.c | 2 +- drivers/acpi/acpica/exfldio.c | 2 +- drivers/acpi/acpica/exmisc.c | 2 +- drivers/acpi/acpica/exmutex.c | 2 +- drivers/acpi/acpica/exnames.c | 2 +- drivers/acpi/acpica/exoparg1.c | 2 +- drivers/acpi/acpica/exoparg2.c | 2 +- drivers/acpi/acpica/exoparg3.c | 2 +- drivers/acpi/acpica/exoparg6.c | 2 +- drivers/acpi/acpica/exprep.c | 2 +- drivers/acpi/acpica/exregion.c | 2 +- drivers/acpi/acpica/exresnte.c | 2 +- drivers/acpi/acpica/exresolv.c | 2 +- drivers/acpi/acpica/exresop.c | 2 +- drivers/acpi/acpica/exstore.c | 2 +- drivers/acpi/acpica/exstoren.c | 2 +- drivers/acpi/acpica/exstorob.c | 2 +- drivers/acpi/acpica/exsystem.c | 2 +- drivers/acpi/acpica/exutils.c | 2 +- drivers/acpi/acpica/hwacpi.c | 2 +- drivers/acpi/acpica/hwgpe.c | 2 +- drivers/acpi/acpica/hwpci.c | 2 +- drivers/acpi/acpica/hwregs.c | 2 +- drivers/acpi/acpica/hwsleep.c | 2 +- drivers/acpi/acpica/hwtimer.c | 2 +- drivers/acpi/acpica/hwvalid.c | 2 +- drivers/acpi/acpica/hwxface.c | 2 +- drivers/acpi/acpica/nsaccess.c | 2 +- drivers/acpi/acpica/nsalloc.c | 2 +- drivers/acpi/acpica/nsdump.c | 2 +- drivers/acpi/acpica/nsdumpdv.c | 2 +- drivers/acpi/acpica/nseval.c | 2 +- drivers/acpi/acpica/nsinit.c | 2 +- drivers/acpi/acpica/nsload.c | 2 +- drivers/acpi/acpica/nsnames.c | 2 +- drivers/acpi/acpica/nsobject.c | 2 +- drivers/acpi/acpica/nsparse.c | 2 +- drivers/acpi/acpica/nspredef.c | 2 +- drivers/acpi/acpica/nsrepair.c | 2 +- drivers/acpi/acpica/nsrepair2.c | 2 +- drivers/acpi/acpica/nssearch.c | 2 +- drivers/acpi/acpica/nsutils.c | 2 +- drivers/acpi/acpica/nswalk.c | 2 +- drivers/acpi/acpica/nsxfeval.c | 2 +- drivers/acpi/acpica/nsxfname.c | 2 +- drivers/acpi/acpica/nsxfobj.c | 2 +- drivers/acpi/acpica/psargs.c | 2 +- drivers/acpi/acpica/psloop.c | 2 +- drivers/acpi/acpica/psopcode.c | 2 +- drivers/acpi/acpica/psparse.c | 2 +- drivers/acpi/acpica/psscope.c | 2 +- drivers/acpi/acpica/pstree.c | 2 +- drivers/acpi/acpica/psutils.c | 2 +- drivers/acpi/acpica/pswalk.c | 2 +- drivers/acpi/acpica/psxface.c | 2 +- drivers/acpi/acpica/rsaddr.c | 2 +- drivers/acpi/acpica/rscalc.c | 2 +- drivers/acpi/acpica/rscreate.c | 2 +- drivers/acpi/acpica/rsdump.c | 2 +- drivers/acpi/acpica/rsinfo.c | 2 +- drivers/acpi/acpica/rsio.c | 2 +- drivers/acpi/acpica/rsirq.c | 2 +- drivers/acpi/acpica/rslist.c | 2 +- drivers/acpi/acpica/rsmemory.c | 2 +- drivers/acpi/acpica/rsmisc.c | 2 +- drivers/acpi/acpica/rsutils.c | 2 +- drivers/acpi/acpica/rsxface.c | 2 +- drivers/acpi/acpica/tbfadt.c | 2 +- drivers/acpi/acpica/tbfind.c | 2 +- drivers/acpi/acpica/tbinstal.c | 2 +- drivers/acpi/acpica/tbutils.c | 2 +- drivers/acpi/acpica/tbxface.c | 2 +- drivers/acpi/acpica/tbxfroot.c | 2 +- drivers/acpi/acpica/utalloc.c | 2 +- drivers/acpi/acpica/utcopy.c | 2 +- drivers/acpi/acpica/utdebug.c | 2 +- drivers/acpi/acpica/utdelete.c | 2 +- drivers/acpi/acpica/uteval.c | 2 +- drivers/acpi/acpica/utglobal.c | 2 +- drivers/acpi/acpica/utids.c | 2 +- drivers/acpi/acpica/utinit.c | 2 +- drivers/acpi/acpica/utlock.c | 2 +- drivers/acpi/acpica/utmath.c | 2 +- drivers/acpi/acpica/utmisc.c | 2 +- drivers/acpi/acpica/utmutex.c | 2 +- drivers/acpi/acpica/utobject.c | 2 +- drivers/acpi/acpica/utosi.c | 2 +- drivers/acpi/acpica/utresrc.c | 2 +- drivers/acpi/acpica/utstate.c | 2 +- drivers/acpi/acpica/utxface.c | 2 +- drivers/acpi/acpica/utxferror.c | 2 +- include/acpi/acexcep.h | 2 +- include/acpi/acnames.h | 2 +- include/acpi/acoutput.h | 2 +- include/acpi/acpi.h | 2 +- include/acpi/acpiosxf.h | 2 +- include/acpi/acpixf.h | 2 +- include/acpi/acrestyp.h | 2 +- include/acpi/actbl.h | 2 +- include/acpi/actbl1.h | 2 +- include/acpi/actbl2.h | 2 +- include/acpi/actypes.h | 2 +- include/acpi/platform/acenv.h | 2 +- include/acpi/platform/acgcc.h | 2 +- include/acpi/platform/aclinux.h | 2 +- 155 files changed, 155 insertions(+), 155 deletions(-) diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h index 3e50c74ed4a1..e0ba17f0a7c8 100644 --- a/drivers/acpi/acpica/accommon.h +++ b/drivers/acpi/acpica/accommon.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index b17d8de9f6ff..ab87396c2c07 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index 72e9d5eb083c..eb0b1f8dee6d 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h index 894a0ff2a946..666271b65418 100644 --- a/drivers/acpi/acpica/acdispat.h +++ b/drivers/acpi/acpica/acdispat.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 70e0b28801aa..41d247daf461 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 9bb69c59bb12..c129a295da3c 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index 258d628793ea..e7213beaafc7 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index 049e203bd621..3731e1c34b83 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 74000f5b7dab..54784bb42cec 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 8d5c9e0a495f..b7491ee1fba6 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index d44d3bc5b847..79a598c67fe3 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index efaf9c6090f5..a0272e6792bf 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index 8c15ff43f42b..bb2ccfad7376 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index d0bb0fd3e57a..5ea1e06afa20 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index 10998d369ad0..94e73c97cf85 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index 528bcbaf4ce7..f08b55b7f3a0 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h index 6e5dd97949fe..1623b245dde2 100644 --- a/drivers/acpi/acpica/acstruct.h +++ b/drivers/acpi/acpica/acstruct.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 62a576e34361..967f08124eba 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 72e4183c1937..99c140d8e348 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index ac1f561ce392..f4f0998d3967 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -7,7 +7,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index 0e5798fcbb19..59122cde247c 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index 347bee1726f1..34be60c0e448 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c index cc4a38c57558..a7718bf2b9a1 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 5a79f401a113..5d797751e205 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index 8095306fcd8c..905ce29a92e1 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index 8e85f54a8e0e..f42e17e5c252 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index 7c0e74227171..bbecf293aeeb 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c index 15135c25aa9b..2c477ce172fa 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index 6b0b5d08d97a..fe40e4c6554f 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 140a9d002959..52566ff5e903 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c index d1e701709dac..76a661fc1e09 100644 --- a/drivers/acpi/acpica/dswscope.c +++ b/drivers/acpi/acpica/dswscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 83155dd8671e..a6c374ef9914 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index e5e313c663a5..d458b041e651 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index b6de1fb34f44..14988a86066f 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index 9acb86958c09..ca2c41a53311 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index c59dc2340593..ce9aa9f9a972 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 10e477494dcf..80a81d0c4a80 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index fcaed9fb44ff..ccd230b7aba4 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index 98fd210e87b2..785a5ee64585 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 2ef32e952559..9659cee6093e 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c index 8dfbaa96e422..2ebd40e1a3ef 100644 --- a/drivers/acpi/acpica/evsci.c +++ b/drivers/acpi/acpica/evsci.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 1226689bdb1b..e1141402dbed 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 90488c1e0f3d..c57b5c707a77 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 416845bc9c1f..e9562a7cb2f9 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index ce9314f79451..eb7386763712 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 18832205b631..745a42b401f5 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index b73bc50c5b76..74162a11817d 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index ffac8c7e54c5..e7b372d17667 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c index be8c98b480d7..c7a2f1edd282 100644 --- a/drivers/acpi/acpica/exdebug.c +++ b/drivers/acpi/acpica/exdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index ff4b1e61c318..61b8c0e8b74d 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index f17d2ff0031b..0bde2230c028 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 38293fd3e088..6c79c29f082d 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 95db4be0877b..703d88ed0b3d 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index 6af14e43f839..be1c56ead653 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c index d11e539ef763..49ec049c157e 100644 --- a/drivers/acpi/acpica/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 84e4d185aa25..236ead14b7f7 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index 10e104cf0fb9..2571b4a310f4 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 7a08d23befcd..1b48d9d28c9a 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index 4b50730cf9a0..f4a2787e8e92 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 7aae29f73d3f..cc95e2000406 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index de17e10da0ed..f0d5e14f1f2c 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c index 1fa4289a687e..55997e46948b 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index 7ca35ea8acea..db502cd7d934 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index 8c97cfd6a0fd..e3bb00ccdff5 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index 1624436ba4c5..c0c8842dd344 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index d4af684620ca..a979017d56b8 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -7,7 +7,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c index e972b667b09b..dc665cc554de 100644 --- a/drivers/acpi/acpica/exstorob.c +++ b/drivers/acpi/acpica/exstorob.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c index 675aaa91a770..df66e7b686be 100644 --- a/drivers/acpi/acpica/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index 4093522eed45..8ad93146dd32 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index b44274a0b62c..fc380d3d45ab 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 85c3cbd4304d..f610d88a66be 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c index ad21c7d8bf4f..050fd227951b 100644 --- a/drivers/acpi/acpica/hwpci.c +++ b/drivers/acpi/acpica/hwpci.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index 5d1273b660ae..55accb7018bb 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -7,7 +7,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index 3796811276ac..2ac28bbe8827 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c index 1ef8e0bb250b..9c8eb71a12fb 100644 --- a/drivers/acpi/acpica/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index e1d9c777b213..5f1605874655 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 50cc3be77724..6f98d210e71c 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index cede0e3200e4..d93172fd15a8 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index 222a9843268d..1d0ef15d158f 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 82693c3eb257..b683cc2ff9d3 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index d2a97921e249..2ed294b7a4db 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index a55c0d2295ca..c1bd02b1a058 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index 660a2728908d..4622852eca5e 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index df18be94fefe..5f7dc691c183 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index d3104af57e13..d5fa520c3de5 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c index 41a9213dd5af..3bb8bf105ea2 100644 --- a/drivers/acpi/acpica/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index 5808c89e9fac..b3234fa795b8 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 7096bcda0c72..9fb03fa8ffde 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index d1c136692667..1d76ac85b5e7 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 4ef9f43ea926..973883babee1 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index 41102a84272f..28b0d7a62b99 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index a7d6ad9c111b..cb1b104a69a2 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 2cd5be8fe10f..345f0c3c6ad2 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index ebef8a7fd707..c53f0040e490 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 0a26d73e050a..3fd4526f3dba 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index a1f04e9b8030..db7660f8b869 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 7df1a4c95274..e1fad0ee0136 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index 06edeaf3b5e5..01dd70d1de51 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c index 2b0c3be2b1b8..bed08de7528c 100644 --- a/drivers/acpi/acpica/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 3b8de113b571..9bb0cbd37b5e 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c index 40e2b279ea12..a5faa1323a02 100644 --- a/drivers/acpi/acpica/psscope.c +++ b/drivers/acpi/acpica/psscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index d4b970c3630b..f1464c03aa42 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c index fe29eee5adb1..7eda78503422 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c index 8abb9629443d..3312d6368bf1 100644 --- a/drivers/acpi/acpica/pswalk.c +++ b/drivers/acpi/acpica/pswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index bc49a80c386d..8086805d4494 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c index 226c806ae986..9e66f9078426 100644 --- a/drivers/acpi/acpica/rsaddr.c +++ b/drivers/acpi/acpica/rsaddr.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index d6ebf7ec622d..3a8a89ec2ca4 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index c80a2eea3a01..4ce6e1147e80 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index f859b0386fe4..33db7520c74b 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c index 1fd868b964fd..f9ea60872aa4 100644 --- a/drivers/acpi/acpica/rsinfo.c +++ b/drivers/acpi/acpica/rsinfo.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsio.c b/drivers/acpi/acpica/rsio.c index 33bff17c0bbc..0c7efef008be 100644 --- a/drivers/acpi/acpica/rsio.c +++ b/drivers/acpi/acpica/rsio.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsirq.c b/drivers/acpi/acpica/rsirq.c index 545da40d7fa7..50b8ad211167 100644 --- a/drivers/acpi/acpica/rsirq.c +++ b/drivers/acpi/acpica/rsirq.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c index 7335f22aac20..1bfcef736c50 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsmemory.c b/drivers/acpi/acpica/rsmemory.c index 887b8ba8c432..7cc6d8625f1e 100644 --- a/drivers/acpi/acpica/rsmemory.c +++ b/drivers/acpi/acpica/rsmemory.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index f8cd9e87d987..410264b22a29 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 491191e6cf69..231811e56939 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 9f6a6e7e1c8e..2ff657a28f26 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index d2ff4325c427..428d44e2d162 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index 989d5c867864..a55cb2bb5abb 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 83d7af8d0905..48db0944ce4a 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 34f9c2bc5e1f..0f2d395feaba 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 4a8b9e6ea57a..4b7085dfc683 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index fd2c07d1d3ac..7eb6c6cc1edf 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c index 8f0896281567..0a697351cf69 100644 --- a/drivers/acpi/acpica/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 6fef83f04bcd..aded299a2fa8 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index f21c486929a5..a9bcd816dc29 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index ed794cd033ea..31f5a7832ef1 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 22f59ef604e0..18f73c9d10bc 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 508537f884ac..97dd9bbf055a 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index d2906328535d..b679ea693545 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index c1b1c803ea9b..191b6828cce9 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c index b081cd46a15f..f6bb75c6faf5 100644 --- a/drivers/acpi/acpica/utlock.c +++ b/drivers/acpi/acpica/utlock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c index 49cf7b7fd816..ce481da9bb45 100644 --- a/drivers/acpi/acpica/utmath.c +++ b/drivers/acpi/acpica/utmath.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index c7d0e05ef5a4..c33a852d4f42 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index d9efa495b433..a429a74dcffa 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index fd1fa2749ea5..188340a017b4 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index 18c59a85fdca..1fb10cb8f11d 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index 7965919000b1..84e051844247 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c index d35d109b8da2..30c21e1a9360 100644 --- a/drivers/acpi/acpica/utstate.c +++ b/drivers/acpi/acpica/utstate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 1f484c9a6888..98ad125e14ff 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c index 6f12e314fbae..916ae097c43c 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index 17714beb868e..5b6c391efc8e 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h index 9cf736ea4691..fc1575fd4596 100644 --- a/include/acpi/acnames.h +++ b/include/acpi/acnames.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index bc4a6deb73b0..ef1cef77d32b 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acpi.h b/include/acpi/acpi.h index a091cabca4b1..de39915f6b7f 100644 --- a/include/acpi/acpi.h +++ b/include/acpi/acpi.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 65b3f5888f42..a3252a5ead66 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -8,7 +8,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 241b8a04c83c..2f38e299b0d9 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/acrestyp.h b/include/acpi/acrestyp.h index e5526354ba5e..0a66cc45dd6b 100644 --- a/include/acpi/acrestyp.h +++ b/include/acpi/acrestyp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index ad2001683ba7..7e42bfee0e29 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index c637b75b9f3f..981349daf5d5 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index d4136b28011f..0fc15dfb2e22 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 939a431a6ab6..64f838beaabf 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index a3e334ab1119..5af3ed52ef98 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h index 5dcb9537343c..e228893591a9 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 572189e37133..5d2a5e9544d9 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2010, Intel Corp. + * Copyright (C) 2000 - 2011, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without -- cgit v1.2.3 From 8d5f0a647395c1323787df675d2805cad54fc89f Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 17 Jan 2011 10:31:08 +0800 Subject: ACPICA: Update version to 20110112 Version 20110112. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 2f38e299b0d9..e46ec95a8ada 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20101209 +#define ACPI_CA_VERSION 0x20110112 #include "actypes.h" #include "actbl.h" -- cgit v1.2.3 From 6b35eb9ddcddde7b510726de03fae071178f1ec4 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 19 Jan 2011 10:09:42 +0100 Subject: Revert "x86: Make relocatable kernel work with new binutils" This reverts commit 86b1e8dd83cb ("x86: Make relocatable kernel work with new binutils"). Markus Trippelsdorf reported a boot failure caused by this patch. The real solution to the original patch will likely involve an arch-generic solution to define an overlaid jiffies_64 and jiffies variables. Until that's done and tested on all architectures revert this commit to solve the regression. Reported-and-bisected-by: Markus Trippelsdorf Acked-by: "H. Peter Anvin" Cc: Shaohua Li Cc: "Lu, Hongjiu" Cc: Linus Torvalds , Cc: Sam Ravnborg LKML-Reference: <4D36A759.60704@intel.com> Signed-off-by: Ingo Molnar --- arch/x86/kernel/vmlinux.lds.S | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index b34ab80fddd5..bf4700755184 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -34,9 +34,11 @@ OUTPUT_FORMAT(CONFIG_OUTPUT_FORMAT, CONFIG_OUTPUT_FORMAT, CONFIG_OUTPUT_FORMAT) #ifdef CONFIG_X86_32 OUTPUT_ARCH(i386) ENTRY(phys_startup_32) +jiffies = jiffies_64; #else OUTPUT_ARCH(i386:x86-64) ENTRY(phys_startup_64) +jiffies_64 = jiffies; #endif #if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA) @@ -140,15 +142,6 @@ SECTIONS CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES) DATA_DATA - /* - * Workaround a binutils (2.20.51.0.12 to 2.21.51.0.3) bug. - * This makes jiffies relocatable in such binutils - */ -#ifdef CONFIG_X86_32 - jiffies = jiffies_64; -#else - jiffies_64 = jiffies; -#endif CONSTRUCTORS /* rarely changed data like cpu maps */ -- cgit v1.2.3 From 569ed348ecef309fae5a71b86015951680ea3415 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Jan 2011 10:14:46 +0100 Subject: Revert "ALSA: HDA: Create mixers on ALC887" This reverts commit 03b7a1ab557efe34e8f79b78660e514bd7374248. This commit was mistakenly re-introduced. While the change is harmless (as ALC887 uses patch_alc888() now), we should get rid of any wrong code. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 269dbff70b92..4f006eedd7ef 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10930,9 +10930,6 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) return 0; } -static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, - const struct auto_pin_cfg *cfg); - /* almost identical with ALC880 parser... */ static int alc882_parse_auto_config(struct hda_codec *codec) { @@ -10950,10 +10947,7 @@ static int alc882_parse_auto_config(struct hda_codec *codec) err = alc880_auto_fill_dac_nids(spec, &spec->autocfg); if (err < 0) return err; - if (codec->vendor_id == 0x10ec0887) - err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg); - else - err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); + err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg); if (err < 0) return err; err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], @@ -17134,7 +17128,7 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec) #define alc861vd_idx_to_mixer_switch(nid) ((nid) + 0x0c) /* add playback controls from the parsed DAC table */ -/* Based on ALC880 version. But ALC861VD and ALC887 have separate, +/* Based on ALC880 version. But ALC861VD has separate, * different NIDs for mute/unmute switch and volume control */ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) -- cgit v1.2.3 From 9032160275ba018003ff390835ff8ed2b5b788b8 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Wed, 19 Jan 2011 08:57:21 +0000 Subject: x86: Unify "numa=" command line option handling In order to be able to suppress the use of SRAT tables that 32-bit Linux can't deal with (in one case known to lead to a non-bootable system, unless disabling ACPI altogether), move the "numa=" option handling to common code. Signed-off-by: Jan Beulich Reviewed-by: Thomas Renninger Cc: Tejun Heo Cc: Thomas Renninger LKML-Reference: <4D36B581020000780002D0FF@vpn.id2.novell.com> Signed-off-by: Ingo Molnar --- arch/x86/include/asm/numa_32.h | 2 ++ arch/x86/include/asm/numa_64.h | 1 + arch/x86/mm/numa.c | 22 ++++++++++++++++++++++ arch/x86/mm/numa_64.c | 24 +++++------------------- arch/x86/mm/srat_32.c | 1 - 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/arch/x86/include/asm/numa_32.h b/arch/x86/include/asm/numa_32.h index a37229011b56..b0ef2b449a9d 100644 --- a/arch/x86/include/asm/numa_32.h +++ b/arch/x86/include/asm/numa_32.h @@ -1,6 +1,8 @@ #ifndef _ASM_X86_NUMA_32_H #define _ASM_X86_NUMA_32_H +extern int numa_off; + extern int pxm_to_nid(int pxm); extern void numa_remove_cpu(int cpu); diff --git a/arch/x86/include/asm/numa_64.h b/arch/x86/include/asm/numa_64.h index 5ae87285a502..0493be39607c 100644 --- a/arch/x86/include/asm/numa_64.h +++ b/arch/x86/include/asm/numa_64.h @@ -40,6 +40,7 @@ extern void __cpuinit numa_remove_cpu(int cpu); #ifdef CONFIG_NUMA_EMU #define FAKE_NODE_MIN_SIZE ((u64)32 << 20) #define FAKE_NODE_MIN_HASH_MASK (~(FAKE_NODE_MIN_SIZE - 1UL)) +void numa_emu_cmdline(char *); #endif /* CONFIG_NUMA_EMU */ #else static inline void init_cpu_to_node(void) { } diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index 787c52ca49c3..ebf6d7887a38 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -2,6 +2,28 @@ #include #include #include +#include +#include + +int __initdata numa_off; + +static __init int numa_setup(char *opt) +{ + if (!opt) + return -EINVAL; + if (!strncmp(opt, "off", 3)) + numa_off = 1; +#ifdef CONFIG_NUMA_EMU + if (!strncmp(opt, "fake=", 5)) + numa_emu_cmdline(opt + 5); +#endif +#ifdef CONFIG_ACPI_NUMA + if (!strncmp(opt, "noacpi", 6)) + acpi_numa = -1; +#endif + return 0; +} +early_param("numa", numa_setup); /* * Which logical CPUs are on which nodes diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 1e72102e80c9..95ea1551eebc 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -30,7 +30,6 @@ s16 apicid_to_node[MAX_LOCAL_APIC] __cpuinitdata = { [0 ... MAX_LOCAL_APIC-1] = NUMA_NO_NODE }; -int numa_off __initdata; static unsigned long __initdata nodemap_addr; static unsigned long __initdata nodemap_size; @@ -263,6 +262,11 @@ static struct bootnode nodes[MAX_NUMNODES] __initdata; static struct bootnode physnodes[MAX_NUMNODES] __cpuinitdata; static char *cmdline __initdata; +void __init numa_emu_cmdline(char *str) +{ + cmdline = str; +} + static int __init setup_physnodes(unsigned long start, unsigned long end, int acpi, int amd) { @@ -670,24 +674,6 @@ unsigned long __init numa_free_all_bootmem(void) return pages; } -static __init int numa_setup(char *opt) -{ - if (!opt) - return -EINVAL; - if (!strncmp(opt, "off", 3)) - numa_off = 1; -#ifdef CONFIG_NUMA_EMU - if (!strncmp(opt, "fake=", 5)) - cmdline = opt + 5; -#endif -#ifdef CONFIG_ACPI_NUMA - if (!strncmp(opt, "noacpi", 6)) - acpi_numa = -1; -#endif - return 0; -} -early_param("numa", numa_setup); - #ifdef CONFIG_NUMA static __init int find_near_online_node(int node) diff --git a/arch/x86/mm/srat_32.c b/arch/x86/mm/srat_32.c index f16434568a51..ae96e7b8051d 100644 --- a/arch/x86/mm/srat_32.c +++ b/arch/x86/mm/srat_32.c @@ -59,7 +59,6 @@ static struct node_memory_chunk_s __initdata node_memory_chunk[MAXCHUNKS]; static int __initdata num_memory_chunks; /* total number of memory chunks */ static u8 __initdata apicid_to_pxm[MAX_APICID]; -int numa_off __initdata; int acpi_numa __initdata; static __init void bad_srat(void) -- cgit v1.2.3 From 068c5cc5ac7414a8e9eb7856b4bf3cc4d4744267 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 19 Jan 2011 12:26:11 +0100 Subject: sched, cgroup: Use exit hook to avoid use-after-free crash By not notifying the controller of the on-exit move back to init_css_set, we fail to move the task out of the previous cgroup's cfs_rq. This leads to an opportunity for a cgroup-destroy to come in and free the cgroup (there are no active tasks left in it after all) to which the not-quite dead task is still enqueued. Reported-by: Miklos Vajna Fixed-by: Mike Galbraith Signed-off-by: Peter Zijlstra Cc: Cc: Mike Galbraith Signed-off-by: Ingo Molnar LKML-Reference: <1293206353.29444.205.camel@laptop> --- kernel/sched.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/kernel/sched.c b/kernel/sched.c index 0a169a85eb3e..fa5272a0932c 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -606,6 +606,9 @@ static inline struct task_group *task_group(struct task_struct *p) struct task_group *tg; struct cgroup_subsys_state *css; + if (p->flags & PF_EXITING) + return &root_task_group; + css = task_subsys_state_check(p, cpu_cgroup_subsys_id, lockdep_is_held(&task_rq(p)->lock)); tg = container_of(css, struct task_group, css); @@ -8880,6 +8883,20 @@ cpu_cgroup_attach(struct cgroup_subsys *ss, struct cgroup *cgrp, } } +static void +cpu_cgroup_exit(struct cgroup_subsys *ss, struct task_struct *task) +{ + /* + * cgroup_exit() is called in the copy_process() failure path. + * Ignore this case since the task hasn't ran yet, this avoids + * trying to poke a half freed task state from generic code. + */ + if (!(task->flags & PF_EXITING)) + return; + + sched_move_task(task); +} + #ifdef CONFIG_FAIR_GROUP_SCHED static int cpu_shares_write_u64(struct cgroup *cgrp, struct cftype *cftype, u64 shareval) @@ -8952,6 +8969,7 @@ struct cgroup_subsys cpu_cgroup_subsys = { .destroy = cpu_cgroup_destroy, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, + .exit = cpu_cgroup_exit, .populate = cpu_cgroup_populate, .subsys_id = cpu_cgroup_subsys_id, .early_init = 1, -- cgit v1.2.3 From f037484337ce009ff840de07ca3232808101db09 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Jan 2011 07:33:25 -0300 Subject: [media] tda9875: remove duplicate driver In commit 411674fd189abe5910ea4caf08b7eac5c4a4d967 the tda9875 support was added to tvaudio. This means that tda9875 is no longer used since mid-2009. If there are out-of-tree users of this driver, then they can switch to tvaudio instead. The original commit message read as follows: This change allows bttv to use tvaudio for this device. Since this device has the same i2c address as the tda9874 we need to support both in the same tvaudio driver. This makes it possible for tvaudio to detect which chip is used. Originally the tda9875 was only available in the dedicated tda9875 driver, but that makes life very hard for bttv since loading tvaudio might misdetect a tda9875 as a tda9874. So there were good reasons for moving the tda9875 code into tvaudio. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 9 - drivers/media/video/Makefile | 1 - drivers/media/video/tda9875.c | 411 ------------------------------------------ 3 files changed, 421 deletions(-) delete mode 100644 drivers/media/video/tda9875.c diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index eb875af05e79..ef3e9852f37e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -141,15 +141,6 @@ config VIDEO_TDA9840 To compile this driver as a module, choose M here: the module will be called tda9840. -config VIDEO_TDA9875 - tristate "Philips TDA9875 audio processor" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for tda9875 audio decoder chip found on some bt8xx boards. - - To compile this driver as a module, choose M here: the - module will be called tda9875. - config VIDEO_TEA6415C tristate "Philips TEA6415C audio processor" depends on I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 81e38cb0b846..a509d317e258 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -27,7 +27,6 @@ obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o -obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c deleted file mode 100644 index 35b6ff5db319..000000000000 --- a/drivers/media/video/tda9875.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * For the TDA9875 chip - * (The TDA9875 is used on the Diamond DTV2000 french version - * Other cards probably use these chips as well.) - * This driver will not complain if used with any - * other i2c device with the same address. - * - * Copyright (c) 2000 Guillaume Delvit based on Gerd Knorr source and - * Eric Sandeen - * Copyright (c) 2006 Mauro Carvalho Chehab - * This code is placed under the terms of the GNU General Public License - * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) - * Which was based on tda8425.c by Greg Alexander (c) 1998 - * - * OPTIONS: - * debug - set to 1 if you'd like to see debug messages - * - * Revision: 0.1 - original version - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int debug; /* insmod parameter */ -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_LICENSE("GPL"); - - -/* This is a superset of the TDA9875 */ -struct tda9875 { - struct v4l2_subdev sd; - int rvol, lvol; - int bass, treble; -}; - -static inline struct tda9875 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tda9875, sd); -} - -#define dprintk if (debug) printk - -/* The TDA9875 is made by Philips Semiconductor - * http://www.semiconductors.philips.com - * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator - * - */ - - /* subaddresses for TDA9875 */ -#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ -#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ -#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ -#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ - -#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ -#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ -#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ -#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ - -#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ -#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ -#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ -#define TDA9875_MVL 0x1a /* Main volume gauche */ -#define TDA9875_MVR 0x1b /* Main volume droite */ -#define TDA9875_MBA 0x1d /* Main Basse */ -#define TDA9875_MTR 0x1e /* Main treble */ -#define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/ -#define TDA9875_AVL 0x20 /* Auxilary volume gauche */ -#define TDA9875_AVR 0x21 /* Auxilary volume droite */ -#define TDA9875_ABA 0x22 /* Auxilary Basse */ -#define TDA9875_ATR 0x23 /* Auxilary treble */ - -#define TDA9875_MSR 0x02 /* Monitor select register */ -#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ -#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ -#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ -#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ -#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ -#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ -#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ -#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ -#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ - -/* values */ -#define TDA9875_MUTE_ON 0xff /* general mute */ -#define TDA9875_MUTE_OFF 0xcc /* general no mute */ - - - -/* Begin code */ - -static int tda9875_write(struct v4l2_subdev *sd, int subaddr, unsigned char val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - unsigned char buffer[2]; - - v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); - buffer[0] = subaddr; - buffer[1] = val; - if (2 != i2c_master_send(client, buffer, 2)) { - v4l2_warn(sd, "I/O error, trying (write %d 0x%x)\n", - subaddr, val); - return -1; - } - return 0; -} - - -static int i2c_read_register(struct i2c_client *client, int addr, int reg) -{ - unsigned char write[1]; - unsigned char read[1]; - struct i2c_msg msgs[2] = { - { addr, 0, 1, write }, - { addr, I2C_M_RD, 1, read } - }; - - write[0] = reg; - - if (2 != i2c_transfer(client->adapter, msgs, 2)) { - v4l_warn(client, "I/O error (read2)\n"); - return -1; - } - v4l_dbg(1, debug, client, "chip_read2: reg%d=0x%x\n", reg, read[0]); - return read[0]; -} - -static void tda9875_set(struct v4l2_subdev *sd) -{ - struct tda9875 *tda = to_state(sd); - unsigned char a; - - v4l2_dbg(1, debug, sd, "tda9875_set(%04x,%04x,%04x,%04x)\n", - tda->lvol, tda->rvol, tda->bass, tda->treble); - - a = tda->lvol & 0xff; - tda9875_write(sd, TDA9875_MVL, a); - a =tda->rvol & 0xff; - tda9875_write(sd, TDA9875_MVR, a); - a =tda->bass & 0xff; - tda9875_write(sd, TDA9875_MBA, a); - a =tda->treble & 0xff; - tda9875_write(sd, TDA9875_MTR, a); -} - -static void do_tda9875_init(struct v4l2_subdev *sd) -{ - struct tda9875 *t = to_state(sd); - - v4l2_dbg(1, debug, sd, "In tda9875_init\n"); - tda9875_write(sd, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ - tda9875_write(sd, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ - tda9875_write(sd, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ - tda9875_write(sd, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ - tda9875_write(sd, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ - tda9875_write(sd, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ - tda9875_write(sd, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ - tda9875_write(sd, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ - tda9875_write(sd, TDA9875_DCR, 0x00); /*Demod config 0x00*/ - tda9875_write(sd, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ - tda9875_write(sd, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ - tda9875_write(sd, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ - tda9875_write(sd, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ - - tda9875_write(sd, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ - tda9875_write(sd, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ - tda9875_write(sd, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ - tda9875_write(sd, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ - tda9875_write(sd, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ - tda9875_write(sd, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ - tda9875_write(sd, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ - tda9875_write(sd, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ - tda9875_write(sd, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ - tda9875_write(sd, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ - tda9875_write(sd, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ - tda9875_write(sd, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ - tda9875_write(sd, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ - tda9875_write(sd, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ - tda9875_write(sd, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ - tda9875_write(sd, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ - - tda9875_write(sd, TDA9875_MUT, 0xcc); /* General mute */ - - t->lvol = t->rvol = 0; /* 0dB */ - t->bass = 0; /* 0dB */ - t->treble = 0; /* 0dB */ - tda9875_set(sd); -} - - -static int tda9875_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tda9875 *t = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - { - int left = (t->lvol+84)*606; - int right = (t->rvol+84)*606; - - ctrl->value=max(left,right); - return 0; - } - case V4L2_CID_AUDIO_BALANCE: - { - int left = (t->lvol+84)*606; - int right = (t->rvol+84)*606; - int volume = max(left,right); - int balance = (32768*min(left,right))/ - (volume ? volume : 1); - ctrl->value=(leftvalue = (t->bass+12)*2427; /* min -12 max +15 */ - return 0; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = (t->treble+12)*2730;/* min -12 max +12 */ - return 0; - } - return -EINVAL; -} - -static int tda9875_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct tda9875 *t = to_state(sd); - int chvol = 0, volume = 0, balance = 0, left, right; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - left = (t->lvol+84)*606; - right = (t->rvol+84)*606; - - volume = max(left,right); - balance = (32768*min(left,right))/ - (volume ? volume : 1); - balance =(leftvalue; - - chvol=1; - break; - case V4L2_CID_AUDIO_BALANCE: - left = (t->lvol+84)*606; - right = (t->rvol+84)*606; - - volume=max(left,right); - - balance = ctrl->value; - - chvol=1; - break; - case V4L2_CID_AUDIO_BASS: - t->bass = ((ctrl->value/2400)-12) & 0xff; - if (t->bass > 15) - t->bass = 15; - if (t->bass < -12) - t->bass = -12 & 0xff; - break; - case V4L2_CID_AUDIO_TREBLE: - t->treble = ((ctrl->value/2700)-12) & 0xff; - if (t->treble > 12) - t->treble = 12; - if (t->treble < -12) - t->treble = -12 & 0xff; - break; - default: - return -EINVAL; - } - - if (chvol) { - left = (min(65536 - balance,32768) * - volume) / 32768; - right = (min(balance,32768) * - volume) / 32768; - t->lvol = ((left/606)-84) & 0xff; - if (t->lvol > 24) - t->lvol = 24; - if (t->lvol < -84) - t->lvol = -84 & 0xff; - - t->rvol = ((right/606)-84) & 0xff; - if (t->rvol > 24) - t->rvol = 24; - if (t->rvol < -84) - t->rvol = -84 & 0xff; - } - - tda9875_set(sd); - return 0; -} - -static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); - } - return -EINVAL; -} - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops tda9875_core_ops = { - .queryctrl = tda9875_queryctrl, - .g_ctrl = tda9875_g_ctrl, - .s_ctrl = tda9875_s_ctrl, -}; - -static const struct v4l2_subdev_ops tda9875_ops = { - .core = &tda9875_core_ops, -}; - -/* ----------------------------------------------------------------------- */ - - -/* *********************** * - * i2c interface functions * - * *********************** */ - -static int tda9875_checkit(struct i2c_client *client, int addr) -{ - int dic, rev; - - dic = i2c_read_register(client, addr, 254); - rev = i2c_read_register(client, addr, 255); - - if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ - v4l_info(client, "tda9875%s rev. %d detected at 0x%02x\n", - dic == 0 ? "" : "A", rev, addr << 1); - return 1; - } - v4l_info(client, "no such chip at 0x%02x (dic=0x%x rev=0x%x)\n", - addr << 1, dic, rev); - return 0; -} - -static int tda9875_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tda9875 *t; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - if (!tda9875_checkit(client, client->addr)) - return -ENODEV; - - t = kzalloc(sizeof(*t), GFP_KERNEL); - if (!t) - return -ENOMEM; - sd = &t->sd; - v4l2_i2c_subdev_init(sd, client, &tda9875_ops); - - do_tda9875_init(sd); - return 0; -} - -static int tda9875_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - do_tda9875_init(sd); - v4l2_device_unregister_subdev(sd); - kfree(to_state(sd)); - return 0; -} - -static const struct i2c_device_id tda9875_id[] = { - { "tda9875", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tda9875_id); - -static struct i2c_driver tda9875_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tda9875", - }, - .probe = tda9875_probe, - .remove = tda9875_remove, - .id_table = tda9875_id, -}; - -static __init int init_tda9875(void) -{ - return i2c_add_driver(&tda9875_driver); -} - -static __exit void exit_tda9875(void) -{ - i2c_del_driver(&tda9875_driver); -} - -module_init(init_tda9875); -module_exit(exit_tda9875); -- cgit v1.2.3 From b219ab9cfb2057de6e779169197f83265c310c83 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Jan 2011 07:39:41 -0300 Subject: [media] bttv: remove obsolete 'no_tda9875' field Since tda9875 is part of tvaudio this field no longer makes sense. Remove it. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bttv-cards.c | 39 ---------------------------------- drivers/media/video/bt8xx/bttv.h | 1 - 2 files changed, 40 deletions(-) diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 49efcf660ba6..7f58756d72c8 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -1373,7 +1373,6 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x1800, .audio_mode_gpio= fv2000s_audio, .no_msp34xx = 1, - .no_tda9875 = 1, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -1511,7 +1510,6 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x09, .needs_tvaudio = 1, .no_msp34xx = 1, - .no_tda9875 = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -1550,7 +1548,6 @@ struct tvcard bttv_tvcards[] = { .gpiomask2 = 0x07ff, .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), .no_msp34xx = 1, - .no_tda9875 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .muxsel_hook = rv605_muxsel, @@ -1686,7 +1683,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY1x0_848] = { @@ -1699,7 +1695,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, @@ -1714,7 +1709,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY1x1] = { @@ -1727,7 +1721,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY1x1_SVID] = { @@ -1740,7 +1733,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY2xx] = { @@ -1753,7 +1745,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, @@ -1768,7 +1759,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY2x0] = { @@ -1781,7 +1771,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY500] = { @@ -1794,7 +1783,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, [BTTV_BOARD_OSPREY540] = { @@ -1805,7 +1793,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, @@ -1820,7 +1807,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, /* must avoid, conflicts with the bt860 */ }, [BTTV_BOARD_IDS_EAGLE] = { @@ -1835,7 +1821,6 @@ struct tvcard bttv_tvcards[] = { .muxsel = MUXSEL(2, 2, 2, 2), .muxsel_hook = eagle_muxsel, .no_msp34xx = 1, - .no_tda9875 = 1, .pll = PLL_28, }, [BTTV_BOARD_PINNACLESAT] = { @@ -1846,7 +1831,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .muxsel = MUXSEL(3, 1), .pll = PLL_28, @@ -1897,7 +1881,6 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .muxsel = MUXSEL(2, 0, 1), .pll = PLL_28, @@ -1970,7 +1953,6 @@ struct tvcard bttv_tvcards[] = { /* Tuner, CVid, SVid, CVid over SVid connector */ .muxsel = MUXSEL(2, 3, 1, 1), .gpiomask = 0, - .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, @@ -2017,7 +1999,6 @@ struct tvcard bttv_tvcards[] = { .muxsel = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0), .muxsel_hook = xguard_muxsel, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, }, @@ -2029,7 +2010,6 @@ struct tvcard bttv_tvcards[] = { .svhs = NO_SVHS, .muxsel = MUXSEL(2, 3, 1, 0), .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .tuner_type = TUNER_ABSENT, @@ -2134,7 +2114,6 @@ struct tvcard bttv_tvcards[] = { .svhs = NO_SVHS, /* card has no svhs */ .needs_tvaudio = 0, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .gpiomask = 0x00, .muxsel = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), @@ -2156,7 +2135,6 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_TWINHAN_DST] = { .name = "Twinhan DST + clones", .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, @@ -2171,7 +2149,6 @@ struct tvcard bttv_tvcards[] = { /* Vid In, SVid In, Vid over SVid in connector */ .muxsel = MUXSEL(3, 1, 1, 3), .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, @@ -2226,7 +2203,6 @@ struct tvcard bttv_tvcards[] = { .svhs = NO_SVHS, .muxsel = MUXSEL(2, 3, 1, 0), .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .needs_tvaudio = 0, .tuner_type = TUNER_ABSENT, @@ -2278,7 +2254,6 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0, .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/ .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, /*878A input is always MUX0, see above.*/ .muxsel = MUXSEL(2, 2, 2, 2), @@ -2302,7 +2277,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, }, [BTTV_BOARD_AVDVBT_771] = { /* Wolfram Joost */ @@ -2313,7 +2287,6 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .muxsel = MUXSEL(3, 3), .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .has_dvb = 1, @@ -2329,7 +2302,6 @@ struct tvcard bttv_tvcards[] = { .svhs = 1, .muxsel = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */ .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .tuner_type = TUNER_ABSENT, @@ -2393,7 +2365,6 @@ struct tvcard bttv_tvcards[] = { /* Chris Pascoe */ .name = "DViCO FusionHDTV DVB-T Lite", .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .no_video = 1, @@ -2440,7 +2411,6 @@ struct tvcard bttv_tvcards[] = { .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), .pll = PLL_28, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, @@ -2478,7 +2448,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .no_msp34xx = 1, .no_tda7432 = 1, - .no_tda9875 = 1, .muxsel_hook = kodicom4400r_muxsel, }, [BTTV_BOARD_KODICOM_4400R_SL] = { @@ -2500,7 +2469,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .no_msp34xx = 1, .no_tda7432 = 1, - .no_tda9875 = 1, .muxsel_hook = kodicom4400r_muxsel, }, /* ---- card 0x86---------------------------------- */ @@ -2530,7 +2498,6 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, .gpiomute = 0x00c00007, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .has_dvb = 1, }, @@ -2630,7 +2597,6 @@ struct tvcard bttv_tvcards[] = { .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, /* ---- card 0x8d ---------------------------------- */ @@ -2658,7 +2624,6 @@ struct tvcard bttv_tvcards[] = { .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 100000, 100002, 100002, 100000 }, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .tuner_type = TUNER_TNF_5335MF, @@ -2674,7 +2639,6 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0x0f, /* old: 7 */ .muxsel = MUXSEL(0, 1, 3, 2), /* Composite 0-3 */ .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, @@ -2732,7 +2696,6 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, .gpiomute = 0x00c00007, .no_msp34xx = 1, - .no_tda9875 = 1, .no_tda7432 = 1, }, /* ---- card 0x95---------------------------------- */ @@ -2874,7 +2837,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .no_msp34xx = 1, .no_tda7432 = 1, - .no_tda9875 = 1, .muxsel_hook = gv800s_muxsel, }, [BTTV_BOARD_GEOVISION_GV800S_SL] = { @@ -2899,7 +2861,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .no_msp34xx = 1, .no_tda7432 = 1, - .no_tda9875 = 1, .muxsel_hook = gv800s_muxsel, }, [BTTV_BOARD_PV183] = { diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index fd62bf15d779..c6333595c6b9 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -234,7 +234,6 @@ struct tvcard { /* i2c audio flags */ unsigned int no_msp34xx:1; - unsigned int no_tda9875:1; unsigned int no_tda7432:1; unsigned int needs_tvaudio:1; unsigned int msp34xx_alt:1; -- cgit v1.2.3 From 9af39713feb53da96ba23fa94a73ffd0de50a815 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 18 Dec 2010 09:20:59 -0300 Subject: [media] saa7146: Convert from .ioctl to .unlocked_ioctl Convert saa7146 to use core-assisted locking. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_core.c | 2 +- drivers/media/common/saa7146_fops.c | 8 ++------ drivers/media/common/saa7146_vbi.c | 2 +- drivers/media/common/saa7146_video.c | 20 +------------------- include/media/saa7146.h | 2 +- 5 files changed, 6 insertions(+), 28 deletions(-) diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c index 982f000a57ff..9f47e383c57a 100644 --- a/drivers/media/common/saa7146_core.c +++ b/drivers/media/common/saa7146_core.c @@ -452,7 +452,7 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device)); dev->ext = ext; - mutex_init(&dev->lock); + mutex_init(&dev->v4l2_lock); spin_lock_init(&dev->int_slock); spin_lock_init(&dev->slock); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index e3fedc60fe77..1bd3dd762c6b 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -15,18 +15,15 @@ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit) } /* is it free? */ - mutex_lock(&dev->lock); if (vv->resources & bit) { DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit)); /* no, someone else uses it */ - mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ fh->resources |= bit; vv->resources |= bit; DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources)); - mutex_unlock(&dev->lock); return 1; } @@ -37,11 +34,9 @@ void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits) BUG_ON((fh->resources & bits) != bits); - mutex_lock(&dev->lock); fh->resources &= ~bits; vv->resources &= ~bits; DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources)); - mutex_unlock(&dev->lock); } @@ -396,7 +391,7 @@ static const struct v4l2_file_operations video_fops = .write = fops_write, .poll = fops_poll, .mmap = fops_mmap, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, }; static void vv_callback(struct saa7146_dev *dev, unsigned long status) @@ -505,6 +500,7 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, vfd->fops = &video_fops; vfd->ioctl_ops = &dev->ext_vv_data->ops; vfd->release = video_device_release; + vfd->lock = &dev->v4l2_lock; vfd->tvnorms = 0; for (i = 0; i < dev->ext_vv_data->num_stds; i++) vfd->tvnorms |= dev->ext_vv_data->stds[i].id; diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index 2d4533ab22b7..afe85801d6ca 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -412,7 +412,7 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, // FIXME: does this really work? sizeof(struct saa7146_buf), - file, NULL); + file, &dev->v4l2_lock); init_timer(&fh->vbi_read_timeout); fh->vbi_read_timeout.function = vbi_read_timeout; diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 0ac5c619aecf..9aafa4e969a8 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -553,8 +553,6 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f } } - mutex_lock(&dev->lock); - /* ok, accept it */ vv->ov_fb = *fb; vv->ov_fmt = fmt; @@ -563,8 +561,6 @@ static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8; DEB_D(("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline)); } - - mutex_unlock(&dev->lock); return 0; } @@ -649,8 +645,6 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) return -EINVAL; } - mutex_lock(&dev->lock); - switch (ctrl->type) { case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: @@ -693,7 +687,6 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) /* fixme: we can support changing VFLIP and HFLIP here... */ if (IS_CAPTURE_ACTIVE(fh) != 0) { DEB_D(("V4L2_CID_HFLIP while active capture.\n")); - mutex_unlock(&dev->lock); return -EBUSY; } vv->hflip = c->value; @@ -701,16 +694,13 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) case V4L2_CID_VFLIP: if (IS_CAPTURE_ACTIVE(fh) != 0) { DEB_D(("V4L2_CID_VFLIP while active capture.\n")); - mutex_unlock(&dev->lock); return -EBUSY; } vv->vflip = c->value; break; default: - mutex_unlock(&dev->lock); return -EINVAL; } - mutex_unlock(&dev->lock); if (IS_OVERLAY_ACTIVE(fh) != 0) { saa7146_stop_preview(fh); @@ -902,22 +892,18 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f err = vidioc_try_fmt_vid_overlay(file, fh, f); if (0 != err) return err; - mutex_lock(&dev->lock); fh->ov.win = f->fmt.win; fh->ov.nclips = f->fmt.win.clipcount; if (fh->ov.nclips > 16) fh->ov.nclips = 16; if (copy_from_user(fh->ov.clips, f->fmt.win.clips, sizeof(struct v4l2_clip) * fh->ov.nclips)) { - mutex_unlock(&dev->lock); return -EFAULT; } /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ fh->ov.fh = fh; - mutex_unlock(&dev->lock); - /* check if our current overlay is active */ if (IS_OVERLAY_ACTIVE(fh) != 0) { saa7146_stop_preview(fh); @@ -976,8 +962,6 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id) } } - mutex_lock(&dev->lock); - for (i = 0; i < dev->ext_vv_data->num_stds; i++) if (*id & dev->ext_vv_data->stds[i].id) break; @@ -988,8 +972,6 @@ static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id) found = 1; } - mutex_unlock(&dev->lock); - if (vv->ov_suspend != NULL) { saa7146_start_preview(vv->ov_suspend); vv->ov_suspend = NULL; @@ -1354,7 +1336,7 @@ static int video_open(struct saa7146_dev *dev, struct file *file) V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct saa7146_buf), - file, NULL); + file, &dev->v4l2_lock); return 0; } diff --git a/include/media/saa7146.h b/include/media/saa7146.h index ac7ce00f39cf..79827143d5ac 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -115,7 +115,7 @@ struct saa7146_dev /* different device locks */ spinlock_t slock; - struct mutex lock; + struct mutex v4l2_lock; unsigned char __iomem *mem; /* pointer to mapped IO memory */ u32 revision; /* chip revision; needed for bug-workarounds*/ -- cgit v1.2.3 From d2db8fee0d77f43f64e4e97ccc1558a9f59fab41 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 18 Dec 2010 09:50:43 -0300 Subject: [media] cpia2: convert .ioctl to .unlocked_ioctl Implement core-assisted locking in cpia2. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cpia2/cpia2.h | 2 +- drivers/media/video/cpia2/cpia2_core.c | 65 +++++---------------- drivers/media/video/cpia2/cpia2_v4l.c | 104 +++++++++++---------------------- 3 files changed, 50 insertions(+), 121 deletions(-) diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h index 916c13d5cf7d..6d6d1843791c 100644 --- a/drivers/media/video/cpia2/cpia2.h +++ b/drivers/media/video/cpia2/cpia2.h @@ -378,7 +378,7 @@ struct cpia2_fh { struct camera_data { /* locks */ - struct mutex busy_lock; /* guard against SMP multithreading */ + struct mutex v4l2_lock; /* serialize file operations */ struct v4l2_prio_state prio; /* camera status */ diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index 9606bc01b803..aaffca8e13fd 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -2247,7 +2247,7 @@ struct camera_data *cpia2_init_camera_struct(void) cam->present = 1; - mutex_init(&cam->busy_lock); + mutex_init(&cam->v4l2_lock); init_waitqueue_head(&cam->wq_stream); return cam; @@ -2365,9 +2365,9 @@ long cpia2_read(struct camera_data *cam, char __user *buf, unsigned long count, int noblock) { struct framebuf *frame; - if (!count) { + + if (!count) return 0; - } if (!buf) { ERR("%s: buffer NULL\n",__func__); @@ -2379,17 +2379,12 @@ long cpia2_read(struct camera_data *cam, return -EINVAL; } - /* make this _really_ smp and multithread-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -ERESTARTSYS; - if (!cam->present) { LOG("%s: camera removed\n",__func__); - mutex_unlock(&cam->busy_lock); return 0; /* EOF */ } - if(!cam->streaming) { + if (!cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); @@ -2398,42 +2393,31 @@ long cpia2_read(struct camera_data *cam, /* Copy cam->curbuff in case it changes while we're processing */ frame = cam->curbuff; if (noblock && frame->status != FRAME_READY) { - mutex_unlock(&cam->busy_lock); return -EAGAIN; } - if(frame->status != FRAME_READY) { - mutex_unlock(&cam->busy_lock); + if (frame->status != FRAME_READY) { + mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, !cam->present || (frame = cam->curbuff)->status == FRAME_READY); + mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - /* make this _really_ smp and multithread-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) { - return -ERESTARTSYS; - } - if(!cam->present) { - mutex_unlock(&cam->busy_lock); + if (!cam->present) return 0; - } } /* copy data to user space */ - if (frame->length > count) { - mutex_unlock(&cam->busy_lock); + if (frame->length > count) return -EFAULT; - } - if (copy_to_user(buf, frame->data, frame->length)) { - mutex_unlock(&cam->busy_lock); + if (copy_to_user(buf, frame->data, frame->length)) return -EFAULT; - } count = frame->length; frame->status = FRAME_EMPTY; - mutex_unlock(&cam->busy_lock); return count; } @@ -2447,17 +2431,13 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, { unsigned int status=0; - if(!cam) { + if (!cam) { ERR("%s: Internal error, camera_data not found!\n",__func__); return POLLERR; } - mutex_lock(&cam->busy_lock); - - if(!cam->present) { - mutex_unlock(&cam->busy_lock); + if (!cam->present) return POLLHUP; - } if(!cam->streaming) { /* Start streaming */ @@ -2465,16 +2445,13 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, cam->params.camera_state.stream_mode); } - mutex_unlock(&cam->busy_lock); poll_wait(filp, &cam->wq_stream, wait); - mutex_lock(&cam->busy_lock); if(!cam->present) status = POLLHUP; else if(cam->curbuff->status == FRAME_READY) status = POLLIN | POLLRDNORM; - mutex_unlock(&cam->busy_lock); return status; } @@ -2496,29 +2473,19 @@ int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) DBG("mmap offset:%ld size:%ld\n", start_offset, size); - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -ERESTARTSYS; - - if (!cam->present) { - mutex_unlock(&cam->busy_lock); + if (!cam->present) return -ENODEV; - } if (size > cam->frame_size*cam->num_frames || (start_offset % cam->frame_size) != 0 || - (start_offset+size > cam->frame_size*cam->num_frames)) { - mutex_unlock(&cam->busy_lock); + (start_offset+size > cam->frame_size*cam->num_frames)) return -EINVAL; - } pos = ((unsigned long) (cam->frame_buffer)) + start_offset; while (size > 0) { page = kvirt_to_pa(pos); - if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&cam->busy_lock); + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; - } start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) @@ -2528,7 +2495,5 @@ int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) } cam->mmapped = true; - mutex_unlock(&cam->busy_lock); return 0; } - diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 7edf80b0d01a..9bad39842936 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -238,59 +238,40 @@ static struct v4l2_queryctrl controls[] = { static int cpia2_open(struct file *file) { struct camera_data *cam = video_drvdata(file); - int retval = 0; + struct cpia2_fh *fh; if (!cam) { ERR("Internal error, camera_data not found!\n"); return -ENODEV; } - if(mutex_lock_interruptible(&cam->busy_lock)) - return -ERESTARTSYS; - - if(!cam->present) { - retval = -ENODEV; - goto err_return; - } + if (!cam->present) + return -ENODEV; - if (cam->open_count > 0) { - goto skip_init; - } + if (cam->open_count == 0) { + if (cpia2_allocate_buffers(cam)) + return -ENOMEM; - if (cpia2_allocate_buffers(cam)) { - retval = -ENOMEM; - goto err_return; - } + /* reset the camera */ + if (cpia2_reset_camera(cam) < 0) + return -EIO; - /* reset the camera */ - if (cpia2_reset_camera(cam) < 0) { - retval = -EIO; - goto err_return; + cam->APP_len = 0; + cam->COM_len = 0; } - cam->APP_len = 0; - cam->COM_len = 0; - -skip_init: - { - struct cpia2_fh *fh = kmalloc(sizeof(*fh),GFP_KERNEL); - if(!fh) { - retval = -ENOMEM; - goto err_return; - } - file->private_data = fh; - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&cam->prio, &fh->prio); - fh->mmapped = 0; - } + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) + return -ENOMEM; + file->private_data = fh; + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&cam->prio, &fh->prio); + fh->mmapped = 0; ++cam->open_count; cpia2_dbg_dump_registers(cam); - -err_return: - mutex_unlock(&cam->busy_lock); - return retval; + return 0; } /****************************************************************************** @@ -304,15 +285,11 @@ static int cpia2_close(struct file *file) struct camera_data *cam = video_get_drvdata(dev); struct cpia2_fh *fh = file->private_data; - mutex_lock(&cam->busy_lock); - if (cam->present && - (cam->open_count == 1 - || fh->prio == V4L2_PRIORITY_RECORD - )) { + (cam->open_count == 1 || fh->prio == V4L2_PRIORITY_RECORD)) { cpia2_usb_stream_stop(cam); - if(cam->open_count == 1) { + if (cam->open_count == 1) { /* save camera state for later open */ cpia2_save_camera_state(cam); @@ -321,26 +298,21 @@ static int cpia2_close(struct file *file) } } - { - if(fh->mmapped) - cam->mmapped = 0; - v4l2_prio_close(&cam->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - } + if (fh->mmapped) + cam->mmapped = 0; + v4l2_prio_close(&cam->prio, fh->prio); + file->private_data = NULL; + kfree(fh); if (--cam->open_count == 0) { cpia2_free_buffers(cam); if (!cam->present) { video_unregister_device(dev); - mutex_unlock(&cam->busy_lock); kfree(cam); return 0; } } - mutex_unlock(&cam->busy_lock); - return 0; } @@ -405,11 +377,11 @@ static int sync(struct camera_data *cam, int frame_nr) return 0; } - mutex_unlock(&cam->busy_lock); + mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, !cam->streaming || frame->status == FRAME_READY); - mutex_lock(&cam->busy_lock); + mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; if(!cam->present) @@ -1293,11 +1265,11 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) if(frame < 0) { /* Wait for a frame to become available */ struct framebuf *cb=cam->curbuff; - mutex_unlock(&cam->busy_lock); + mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, !cam->present || (cb=cam->curbuff)->status == FRAME_READY); - mutex_lock(&cam->busy_lock); + mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; if(!cam->present) @@ -1337,14 +1309,8 @@ static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!cam) return -ENOTTY; - /* make this _really_ smp-safe */ - if (mutex_lock_interruptible(&cam->busy_lock)) - return -ERESTARTSYS; - - if (!cam->present) { - mutex_unlock(&cam->busy_lock); + if (!cam->present) return -ENODEV; - } /* Priority check */ switch (cmd) { @@ -1352,10 +1318,8 @@ static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct cpia2_fh *fh = file->private_data; retval = v4l2_prio_check(&cam->prio, fh->prio); - if(retval) { - mutex_unlock(&cam->busy_lock); + if (retval) return retval; - } break; } default: @@ -1529,7 +1493,6 @@ static long cpia2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } - mutex_unlock(&cam->busy_lock); return retval; } @@ -1596,7 +1559,7 @@ static const struct v4l2_file_operations cpia2_fops = { .release = cpia2_close, .read = cpia2_v4l_read, .poll = cpia2_v4l_poll, - .ioctl = cpia2_ioctl, + .unlocked_ioctl = cpia2_ioctl, .mmap = cpia2_mmap, }; @@ -1620,6 +1583,7 @@ int cpia2_register_camera(struct camera_data *cam) memcpy(cam->vdev, &cpia2_template, sizeof(cpia2_template)); video_set_drvdata(cam->vdev, cam); + cam->vdev->lock = &cam->v4l2_lock; reset_camera_struct_v4l(cam); -- cgit v1.2.3 From 7036d6a73c88428764e4a12f30846279346f4382 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 16 Dec 2010 12:17:41 -0300 Subject: [media] vpif_cap/disp: Add debug functionality The following functions are added to the drivers: - vpif_g_chip_ident - vpif_dbg_g_register - vpif_dbg_s_register - vpif_log_status Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Acked-by: Vaibhav Hiremath Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif_capture.c | 83 ++++++++++++++++++++++++++++ drivers/media/video/davinci/vpif_display.c | 86 ++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 193abab6b355..9446dbc476fd 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "vpif_capture.h" #include "vpif.h" @@ -1807,6 +1808,82 @@ static int vpif_cropcap(struct file *file, void *priv, return 0; } +/* + * vpif_g_chip_ident() - Identify the chip + * @file: file ptr + * @priv: file handle + * @chip: chip identity + * + * Returns zero or -EINVAL if read operations fails. + */ +static int vpif_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) { + vpif_dbg(2, debug, "match_type is invalid.\n"); + return -EINVAL; + } + + return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core, + g_chip_ident, chip); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * vpif_dbg_g_register() - Read register + * @file: file ptr + * @priv: file handle + * @reg: register to be read + * + * Debugging only + * Returns zero or -EINVAL if read operations fails. + */ +static int vpif_dbg_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg){ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core, + g_register, reg); +} + +/* + * vpif_dbg_s_register() - Write to register + * @file: file ptr + * @priv: file handle + * @reg: register to be modified + * + * Debugging only + * Returns zero or -EINVAL if write operations fails. + */ +static int vpif_dbg_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg){ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core, + s_register, reg); +} +#endif + +/* + * vpif_log_status() - Status information + * @file: file ptr + * @priv: file handle + * + * Returns zero. + */ +static int vpif_log_status(struct file *filep, void *priv) +{ + /* status for sub devices */ + v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status); + + return 0; +} + /* vpif capture ioctl operations */ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_querycap = vpif_querycap, @@ -1829,6 +1906,12 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_streamon = vpif_streamon, .vidioc_streamoff = vpif_streamoff, .vidioc_cropcap = vpif_cropcap, + .vidioc_g_chip_ident = vpif_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vpif_dbg_g_register, + .vidioc_s_register = vpif_dbg_s_register, +#endif + .vidioc_log_status = vpif_log_status, }; /* vpif file operations */ diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 412c65d54fe1..2d55e3e86a84 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -38,6 +38,7 @@ #include #include #include +#include #include @@ -1315,6 +1316,85 @@ static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) return v4l2_prio_change(&ch->prio, &fh->prio, p); } + +/* + * vpif_g_chip_ident() - Identify the chip + * @file: file ptr + * @priv: file handle + * @chip: chip identity + * + * Returns zero or -EINVAL if read operations fails. + */ +static int vpif_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) { + vpif_dbg(2, debug, "match_type is invalid.\n"); + return -EINVAL; + } + + return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core, + g_chip_ident, chip); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * vpif_dbg_g_register() - Read register + * @file: file ptr + * @priv: file handle + * @reg: register to be read + * + * Debugging only + * Returns zero or -EINVAL if read operations fails. + */ +static int vpif_dbg_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg){ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core, + g_register, reg); +} + +/* + * vpif_dbg_s_register() - Write to register + * @file: file ptr + * @priv: file handle + * @reg: register to be modified + * + * Debugging only + * Returns zero or -EINVAL if write operations fails. + */ +static int vpif_dbg_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg){ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core, + s_register, reg); +} +#endif + +/* + * vpif_log_status() - Status information + * @file: file ptr + * @priv: file handle + * + * Returns zero. + */ +static int vpif_log_status(struct file *filep, void *priv) +{ + /* status for sub devices */ + v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status); + + return 0; +} + /* vpif display ioctl operations */ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_querycap = vpif_querycap, @@ -1336,6 +1416,12 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_s_output = vpif_s_output, .vidioc_g_output = vpif_g_output, .vidioc_cropcap = vpif_cropcap, + .vidioc_g_chip_ident = vpif_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vpif_dbg_g_register, + .vidioc_s_register = vpif_dbg_s_register, +#endif + .vidioc_log_status = vpif_log_status, }; static const struct v4l2_file_operations vpif_fops = { -- cgit v1.2.3 From aa4444063505983c5971bc8fb832385dfba16b41 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 16 Dec 2010 12:17:42 -0300 Subject: [media] vpif: Consolidate formats from capture and display - The ch_params tables in vpif_capture.c and vpif_display.c are moved to a common table in vpif.c. Then it is easier to maintain the table. - The field "fps" is removed from the struct vpif_channel_config_params because it is not used. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Acked-by : Murali Karicheri Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif.c | 50 ++++++++++++++++++++++++++++++ drivers/media/video/davinci/vpif.h | 4 ++- drivers/media/video/davinci/vpif_capture.c | 18 ++--------- drivers/media/video/davinci/vpif_display.c | 17 ++-------- 4 files changed, 58 insertions(+), 31 deletions(-) diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c index 1f532e31cd49..54cc0dadd755 100644 --- a/drivers/media/video/davinci/vpif.c +++ b/drivers/media/video/davinci/vpif.c @@ -41,6 +41,56 @@ spinlock_t vpif_lock; void __iomem *vpif_base; +/** + * ch_params: video standard configuration parameters for vpif + * The table must include all presets from supported subdevices. + */ +const struct vpif_channel_config_params ch_params[] = { + /* SDTV formats */ + { + .name = "NTSC_M", + .width = 720, + .height = 480, + .frm_fmt = 0, + .ycmux_mode = 1, + .eav2sav = 268, + .sav2eav = 1440, + .l1 = 1, + .l3 = 23, + .l5 = 263, + .l7 = 266, + .l9 = 286, + .l11 = 525, + .vsize = 525, + .capture_format = 0, + .vbi_supported = 1, + .hd_sd = 0, + .stdid = V4L2_STD_525_60, + }, + { + .name = "PAL_BDGHIK", + .width = 720, + .height = 576, + .frm_fmt = 0, + .ycmux_mode = 1, + .eav2sav = 280, + .sav2eav = 1440, + .l1 = 1, + .l3 = 23, + .l5 = 311, + .l7 = 313, + .l9 = 336, + .l11 = 624, + .vsize = 625, + .capture_format = 0, + .vbi_supported = 1, + .hd_sd = 0, + .stdid = V4L2_STD_625_50, + }, +}; + +const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params); + static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) { if (val) diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index ebd5c4338ebb..d14e36e5c90d 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -577,7 +577,6 @@ struct vpif_channel_config_params { char name[VPIF_MAX_NAME]; /* Name of the mode */ u16 width; /* Indicates width of the image */ u16 height; /* Indicates height of the image */ - u8 fps; u8 frm_fmt; /* Indicates whether this is interlaced * or progressive format */ u8 ycmux_mode; /* Indicates whether this mode requires @@ -594,6 +593,9 @@ struct vpif_channel_config_params { v4l2_std_id stdid; }; +extern const unsigned int vpif_ch_params_count; +extern const struct vpif_channel_config_params ch_params[]; + struct vpif_video_params; struct vpif_params; struct vpif_vbi_params; diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 9446dbc476fd..0a7ebb0e151a 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -81,20 +81,6 @@ static struct vpif_config_params config_params = { static struct vpif_device vpif_obj = { {NULL} }; static struct device *vpif_dev; -/** - * ch_params: video standard configuration parameters for vpif - */ -static const struct vpif_channel_config_params ch_params[] = { - { - "NTSC_M", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, - 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, - }, - { - "PAL_BDGHIK", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, - 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, - }, -}; - /** * vpif_uservirt_to_phys : translate user/virtual address to phy address * @virtp: user/virtual address @@ -444,7 +430,7 @@ static int vpif_update_std_info(struct channel_obj *ch) std_info = &vpifparams->std_info; - for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + for (index = 0; index < vpif_ch_params_count; index++) { config = &ch_params[index]; if (config->stdid & vid_ch->stdid) { memcpy(std_info, config, sizeof(*config)); @@ -453,7 +439,7 @@ static int vpif_update_std_info(struct channel_obj *ch) } /* standard not found */ - if (index == ARRAY_SIZE(ch_params)) + if (index == vpif_ch_params_count) return -EINVAL; common->fmt.fmt.pix.width = std_info->width; diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 2d55e3e86a84..9de0062eca59 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -85,17 +85,6 @@ static struct vpif_config_params config_params = { static struct vpif_device vpif_obj = { {NULL} }; static struct device *vpif_dev; -static const struct vpif_channel_config_params ch_params[] = { - { - "NTSC", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, - 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, - }, - { - "PAL", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, - 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, - }, -}; - /* * vpif_uservirt_to_phys: This function is used to convert user * space virtual address to physical address. @@ -388,7 +377,7 @@ static int vpif_get_std_info(struct channel_obj *ch) if (!std_info->stdid) return -1; - for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + for (index = 0; index < vpif_ch_params_count; index++) { config = &ch_params[index]; if (config->stdid & std_info->stdid) { memcpy(std_info, config, sizeof(*config)); @@ -396,8 +385,8 @@ static int vpif_get_std_info(struct channel_obj *ch) } } - if (index == ARRAY_SIZE(ch_params)) - return -1; + if (index == vpif_ch_params_count) + return -EINVAL; common->fmt.fmt.pix.width = std_info->width; common->fmt.fmt.pix.height = std_info->height; -- cgit v1.2.3 From 40c8bcea6bc594c50abf3d3867bd49c8c039eb21 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 16 Dec 2010 12:17:43 -0300 Subject: [media] vpif_cap/disp: Add support for DV presets - Added functions to set/get/query/enum DV presets for vpif_caputre and vpif_display. - The format specification table is extended with all the DV formats supported by TVP7002. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif.c | 127 +++++++++++++++++++++++++++++ drivers/media/video/davinci/vpif.h | 1 + drivers/media/video/davinci/vpif_capture.c | 124 +++++++++++++++++++++++++++- drivers/media/video/davinci/vpif_capture.h | 1 + drivers/media/video/davinci/vpif_display.c | 105 ++++++++++++++++++++++-- drivers/media/video/davinci/vpif_display.h | 1 + 6 files changed, 350 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c index 54cc0dadd755..9f3bfc1eb240 100644 --- a/drivers/media/video/davinci/vpif.c +++ b/drivers/media/video/davinci/vpif.c @@ -46,6 +46,133 @@ void __iomem *vpif_base; * The table must include all presets from supported subdevices. */ const struct vpif_channel_config_params ch_params[] = { + /* HDTV formats */ + { + .name = "480p59_94", + .width = 720, + .height = 480, + .frm_fmt = 1, + .ycmux_mode = 0, + .eav2sav = 138-8, + .sav2eav = 720, + .l1 = 1, + .l3 = 43, + .l5 = 523, + .vsize = 525, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_480P59_94, + }, + { + .name = "576p50", + .width = 720, + .height = 576, + .frm_fmt = 1, + .ycmux_mode = 0, + .eav2sav = 144-8, + .sav2eav = 720, + .l1 = 1, + .l3 = 45, + .l5 = 621, + .vsize = 625, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_576P50, + }, + { + .name = "720p50", + .width = 1280, + .height = 720, + .frm_fmt = 1, + .ycmux_mode = 0, + .eav2sav = 700-8, + .sav2eav = 1280, + .l1 = 1, + .l3 = 26, + .l5 = 746, + .vsize = 750, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_720P50, + }, + { + .name = "720p60", + .width = 1280, + .height = 720, + .frm_fmt = 1, + .ycmux_mode = 0, + .eav2sav = 370 - 8, + .sav2eav = 1280, + .l1 = 1, + .l3 = 26, + .l5 = 746, + .vsize = 750, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_720P60, + }, + { + .name = "1080I50", + .width = 1920, + .height = 1080, + .frm_fmt = 0, + .ycmux_mode = 0, + .eav2sav = 720 - 8, + .sav2eav = 1920, + .l1 = 1, + .l3 = 21, + .l5 = 561, + .l7 = 563, + .l9 = 584, + .l11 = 1124, + .vsize = 1125, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_1080I50, + }, + { + .name = "1080I60", + .width = 1920, + .height = 1080, + .frm_fmt = 0, + .ycmux_mode = 0, + .eav2sav = 280 - 8, + .sav2eav = 1920, + .l1 = 1, + .l3 = 21, + .l5 = 561, + .l7 = 563, + .l9 = 584, + .l11 = 1124, + .vsize = 1125, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_1080I60, + }, + { + .name = "1080p60", + .width = 1920, + .height = 1080, + .frm_fmt = 1, + .ycmux_mode = 0, + .eav2sav = 280 - 8, + .sav2eav = 1920, + .l1 = 1, + .l3 = 42, + .l5 = 1122, + .vsize = 1125, + .capture_format = 0, + .vbi_supported = 0, + .hd_sd = 1, + .dv_preset = V4L2_DV_1080P60, + }, + /* SDTV formats */ { .name = "NTSC_M", diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index d14e36e5c90d..b6695bee3c59 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -591,6 +591,7 @@ struct vpif_channel_config_params { * supports capturing vbi or not */ u8 hd_sd; v4l2_std_id stdid; + u32 dv_preset; /* HDTV format */ }; extern const unsigned int vpif_ch_params_count; diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 0a7ebb0e151a..42f1cd60780b 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -432,9 +432,18 @@ static int vpif_update_std_info(struct channel_obj *ch) for (index = 0; index < vpif_ch_params_count; index++) { config = &ch_params[index]; - if (config->stdid & vid_ch->stdid) { - memcpy(std_info, config, sizeof(*config)); - break; + if (config->hd_sd == 0) { + vpif_dbg(2, debug, "SD format\n"); + if (config->stdid & vid_ch->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } else { + vpif_dbg(2, debug, "HD format\n"); + if (config->dv_preset == vid_ch->dv_preset) { + memcpy(std_info, config, sizeof(*config)); + break; + } } } @@ -1442,6 +1451,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) return -ERESTARTSYS; ch->video.stdid = *std_id; + ch->video.dv_preset = V4L2_DV_INVALID; /* Get the information about the standard */ if (vpif_update_std_info(ch)) { @@ -1794,6 +1804,110 @@ static int vpif_cropcap(struct file *file, void *priv, return 0; } +/** + * vpif_enum_dv_presets() - ENUM_DV_PRESETS handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_enum_dv_presets(struct file *file, void *priv, + struct v4l2_dv_enum_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], + video, enum_dv_presets, preset); +} + +/** + * vpif_query_dv_presets() - QUERY_DV_PRESET handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_query_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], + video, query_dv_preset, preset); +} +/** + * vpif_s_dv_presets() - S_DV_PRESETS handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_s_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (common->started) { + vpif_dbg(1, debug, "streaming in progress\n"); + return -EBUSY; + } + + if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || + (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + } + + ret = v4l2_prio_check(&ch->prio, fh->prio); + if (ret) + return ret; + + fh->initialized = 1; + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.dv_preset = preset->preset; + ch->video.stdid = V4L2_STD_UNKNOWN; + + /* Get the information about the standard */ + if (vpif_update_std_info(ch)) { + vpif_dbg(1, debug, "Error getting the standard info\n"); + ret = -EINVAL; + } else { + /* Configure the default format information */ + vpif_config_format(ch); + + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], + video, s_dv_preset, preset); + } + + mutex_unlock(&common->lock); + + return ret; +} +/** + * vpif_g_dv_presets() - G_DV_PRESETS handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_g_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + preset->preset = ch->video.dv_preset; + + return 0; +} + /* * vpif_g_chip_ident() - Identify the chip * @file: file ptr @@ -1892,6 +2006,10 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_streamon = vpif_streamon, .vidioc_streamoff = vpif_streamoff, .vidioc_cropcap = vpif_cropcap, + .vidioc_enum_dv_presets = vpif_enum_dv_presets, + .vidioc_s_dv_preset = vpif_s_dv_preset, + .vidioc_g_dv_preset = vpif_g_dv_preset, + .vidioc_query_dv_preset = vpif_query_dv_preset, .vidioc_g_chip_ident = vpif_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vpif_dbg_g_register, diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h index 4e12ec8cac6f..3452a8aec020 100644 --- a/drivers/media/video/davinci/vpif_capture.h +++ b/drivers/media/video/davinci/vpif_capture.h @@ -59,6 +59,7 @@ struct video_obj { enum v4l2_field buf_field; /* Currently selected or default standard */ v4l2_std_id stdid; + u32 dv_preset; /* This is to track the last input that is passed to application */ u32 input_idx; }; diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 9de0062eca59..759c5e855ac8 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -373,15 +373,23 @@ static int vpif_get_std_info(struct channel_obj *ch) int index; - std_info->stdid = vid_ch->stdid; - if (!std_info->stdid) - return -1; + if (!vid_ch->stdid && !vid_ch->dv_preset) + return -EINVAL; for (index = 0; index < vpif_ch_params_count; index++) { config = &ch_params[index]; - if (config->stdid & std_info->stdid) { - memcpy(std_info, config, sizeof(*config)); - break; + if (config->hd_sd == 0) { + vpif_dbg(2, debug, "SD format\n"); + if (config->stdid & vid_ch->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } else { + vpif_dbg(2, debug, "HD format\n"); + if (config->dv_preset == vid_ch->dv_preset) { + memcpy(std_info, config, sizeof(*config)); + break; + } } } @@ -1305,6 +1313,88 @@ static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) return v4l2_prio_change(&ch->prio, &fh->prio, p); } +/** + * vpif_enum_dv_presets() - ENUM_DV_PRESETS handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_enum_dv_presets(struct file *file, void *priv, + struct v4l2_dv_enum_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], + video, enum_dv_presets, preset); +} + +/** + * vpif_s_dv_presets() - S_DV_PRESETS handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_s_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + int ret = 0; + + if (common->started) { + vpif_dbg(1, debug, "streaming in progress\n"); + return -EBUSY; + } + + ret = v4l2_prio_check(&ch->prio, fh->prio); + if (ret != 0) + return ret; + + fh->initialized = 1; + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.dv_preset = preset->preset; + ch->video.stdid = V4L2_STD_UNKNOWN; + + /* Get the information about the standard */ + if (vpif_get_std_info(ch)) { + ret = -EINVAL; + vpif_dbg(1, debug, "Error getting the standard info\n"); + } else { + /* Configure the default format information */ + vpif_config_format(ch); + + ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], + video, s_dv_preset, preset); + } + + mutex_unlock(&common->lock); + + return ret; +} +/** + * vpif_g_dv_presets() - G_DV_PRESETS handler + * @file: file ptr + * @priv: file handle + * @preset: input preset + */ +static int vpif_g_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + preset->preset = ch->video.dv_preset; + + return 0; +} /* * vpif_g_chip_ident() - Identify the chip @@ -1405,6 +1495,9 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_s_output = vpif_s_output, .vidioc_g_output = vpif_g_output, .vidioc_cropcap = vpif_cropcap, + .vidioc_enum_dv_presets = vpif_enum_dv_presets, + .vidioc_s_dv_preset = vpif_s_dv_preset, + .vidioc_g_dv_preset = vpif_g_dv_preset, .vidioc_g_chip_ident = vpif_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vpif_dbg_g_register, diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h index a2a7cd166bbf..3d56b3e356a2 100644 --- a/drivers/media/video/davinci/vpif_display.h +++ b/drivers/media/video/davinci/vpif_display.h @@ -67,6 +67,7 @@ struct video_obj { * most recent displayed frame only */ v4l2_std_id stdid; /* Currently selected or default * standard */ + u32 dv_preset; u32 output_id; /* Current output id */ }; -- cgit v1.2.3 From c027e165d2d901ecab485da5fee72ddce5da0297 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 16 Dec 2010 12:17:44 -0300 Subject: [media] vpif_cap/disp: Added support for DV timings Added functions to set and get custom DV timings. Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Acked-by: Vaibhav Hiremath Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif_capture.c | 123 +++++++++++++++++++ drivers/media/video/davinci/vpif_capture.h | 1 + drivers/media/video/davinci/vpif_display.c | 190 +++++++++++++++++++++++++---- drivers/media/video/davinci/vpif_display.h | 1 + 4 files changed, 290 insertions(+), 25 deletions(-) diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 42f1cd60780b..be2b9919937c 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -1452,6 +1452,7 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) ch->video.stdid = *std_id; ch->video.dv_preset = V4L2_DV_INVALID; + memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings)); /* Get the information about the standard */ if (vpif_update_std_info(ch)) { @@ -1874,6 +1875,7 @@ static int vpif_s_dv_preset(struct file *file, void *priv, ch->video.dv_preset = preset->preset; ch->video.stdid = V4L2_STD_UNKNOWN; + memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings)); /* Get the information about the standard */ if (vpif_update_std_info(ch)) { @@ -1908,6 +1910,125 @@ static int vpif_g_dv_preset(struct file *file, void *priv, return 0; } +/** + * vpif_s_dv_timings() - S_DV_TIMINGS handler + * @file: file ptr + * @priv: file handle + * @timings: digital video timings + */ +static int vpif_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct vpif_params *vpifparams = &ch->vpifparams; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; + struct video_obj *vid_ch = &ch->video; + struct v4l2_bt_timings *bt = &vid_ch->bt_timings; + int ret; + + if (timings->type != V4L2_DV_BT_656_1120) { + vpif_dbg(2, debug, "Timing type not defined\n"); + return -EINVAL; + } + + /* Configure subdevice timings, if any */ + ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], + video, s_dv_timings, timings); + if (ret == -ENOIOCTLCMD) { + vpif_dbg(2, debug, "Custom DV timings not supported by " + "subdevice\n"); + return -EINVAL; + } + if (ret < 0) { + vpif_dbg(2, debug, "Error setting custom DV timings\n"); + return ret; + } + + if (!(timings->bt.width && timings->bt.height && + (timings->bt.hbackporch || + timings->bt.hfrontporch || + timings->bt.hsync) && + timings->bt.vfrontporch && + (timings->bt.vbackporch || + timings->bt.vsync))) { + vpif_dbg(2, debug, "Timings for width, height, " + "horizontal back porch, horizontal sync, " + "horizontal front porch, vertical back porch, " + "vertical sync and vertical back porch " + "must be defined\n"); + return -EINVAL; + } + + *bt = timings->bt; + + /* Configure video port timings */ + + std_info->eav2sav = bt->hbackporch + bt->hfrontporch + + bt->hsync - 8; + std_info->sav2eav = bt->width; + + std_info->l1 = 1; + std_info->l3 = bt->vsync + bt->vbackporch + 1; + + if (bt->interlaced) { + if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) { + std_info->vsize = bt->height * 2 + + bt->vfrontporch + bt->vsync + bt->vbackporch + + bt->il_vfrontporch + bt->il_vsync + + bt->il_vbackporch; + std_info->l5 = std_info->vsize/2 - + (bt->vfrontporch - 1); + std_info->l7 = std_info->vsize/2 + 1; + std_info->l9 = std_info->l7 + bt->il_vsync + + bt->il_vbackporch + 1; + std_info->l11 = std_info->vsize - + (bt->il_vfrontporch - 1); + } else { + vpif_dbg(2, debug, "Required timing values for " + "interlaced BT format missing\n"); + return -EINVAL; + } + } else { + std_info->vsize = bt->height + bt->vfrontporch + + bt->vsync + bt->vbackporch; + std_info->l5 = std_info->vsize - (bt->vfrontporch - 1); + } + strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME); + std_info->width = bt->width; + std_info->height = bt->height; + std_info->frm_fmt = bt->interlaced ? 0 : 1; + std_info->ycmux_mode = 0; + std_info->capture_format = 0; + std_info->vbi_supported = 0; + std_info->hd_sd = 1; + std_info->stdid = 0; + std_info->dv_preset = V4L2_DV_INVALID; + + vid_ch->stdid = 0; + vid_ch->dv_preset = V4L2_DV_INVALID; + return 0; +} + +/** + * vpif_g_dv_timings() - G_DV_TIMINGS handler + * @file: file ptr + * @priv: file handle + * @timings: digital video timings + */ +static int vpif_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + struct v4l2_bt_timings *bt = &vid_ch->bt_timings; + + timings->bt = *bt; + + return 0; +} + /* * vpif_g_chip_ident() - Identify the chip * @file: file ptr @@ -2010,6 +2131,8 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_s_dv_preset = vpif_s_dv_preset, .vidioc_g_dv_preset = vpif_g_dv_preset, .vidioc_query_dv_preset = vpif_query_dv_preset, + .vidioc_s_dv_timings = vpif_s_dv_timings, + .vidioc_g_dv_timings = vpif_g_dv_timings, .vidioc_g_chip_ident = vpif_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vpif_dbg_g_register, diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h index 3452a8aec020..7a4196dfdce1 100644 --- a/drivers/media/video/davinci/vpif_capture.h +++ b/drivers/media/video/davinci/vpif_capture.h @@ -60,6 +60,7 @@ struct video_obj { /* Currently selected or default standard */ v4l2_std_id stdid; u32 dv_preset; + struct v4l2_bt_timings bt_timings; /* This is to track the last input that is passed to application */ u32 input_idx; }; diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 759c5e855ac8..44a885878ebf 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -363,21 +363,17 @@ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static int vpif_get_std_info(struct channel_obj *ch) +static int vpif_update_std_info(struct channel_obj *ch) { - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct video_obj *vid_ch = &ch->video; struct vpif_params *vpifparams = &ch->vpifparams; struct vpif_channel_config_params *std_info = &vpifparams->std_info; const struct vpif_channel_config_params *config; - int index; - - if (!vid_ch->stdid && !vid_ch->dv_preset) - return -EINVAL; + int i; - for (index = 0; index < vpif_ch_params_count; index++) { - config = &ch_params[index]; + for (i = 0; i < vpif_ch_params_count; i++) { + config = &ch_params[i]; if (config->hd_sd == 0) { vpif_dbg(2, debug, "SD format\n"); if (config->stdid & vid_ch->stdid) { @@ -393,17 +389,37 @@ static int vpif_get_std_info(struct channel_obj *ch) } } - if (index == vpif_ch_params_count) + if (i == vpif_ch_params_count) { + vpif_dbg(1, debug, "Format not found\n"); + return -EINVAL; + } + + return 0; +} + +static int vpif_update_resolution(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + struct vpif_params *vpifparams = &ch->vpifparams; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; + + if (!vid_ch->stdid && !vid_ch->dv_preset && !vid_ch->bt_timings.height) return -EINVAL; + if (vid_ch->stdid || vid_ch->dv_preset) { + if (vpif_update_std_info(ch)) + return -EINVAL; + } + common->fmt.fmt.pix.width = std_info->width; common->fmt.fmt.pix.height = std_info->height; vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", common->fmt.fmt.pix.width, common->fmt.fmt.pix.height); /* Set height and width paramateres */ - ch->common[VPIF_VIDEO_INDEX].height = std_info->height; - ch->common[VPIF_VIDEO_INDEX].width = std_info->width; + common->height = std_info->height; + common->width = std_info->width; return 0; } @@ -514,10 +530,8 @@ static int vpif_check_format(struct channel_obj *ch, else sizeimage = config_params.channel_bufsize[ch->channel_id]; - if (vpif_get_std_info(ch)) { - vpif_err("Error getting the standard info\n"); + if (vpif_update_resolution(ch)) return -EINVAL; - } hpitch = pixfmt->bytesperline; vpitch = sizeimage / (hpitch * 2); @@ -715,6 +729,7 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv, struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; /* Check the validity of the buffer type */ if (common->fmt.type != fmt->type) @@ -724,14 +739,14 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv, if (mutex_lock_interruptible(&common->lock)) return -ERESTARTSYS; - if (vpif_get_std_info(ch)) { - vpif_err("Error getting the standard info\n"); - return -EINVAL; - } + if (vpif_update_resolution(ch)) + ret = -EINVAL; + else + *fmt = common->fmt; - *fmt = common->fmt; mutex_unlock(&common->lock); - return 0; + + return ret; } static int vpif_s_fmt_vid_out(struct file *file, void *priv, @@ -992,10 +1007,13 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) return -ERESTARTSYS; ch->video.stdid = *std_id; + ch->video.dv_preset = V4L2_DV_INVALID; + memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings)); + /* Get the information about the standard */ - if (vpif_get_std_info(ch)) { - vpif_err("Error getting the standard info\n"); - return -EINVAL; + if (vpif_update_resolution(ch)) { + ret = -EINVAL; + goto s_std_exit; } if ((ch->vpifparams.std_info.width * @@ -1362,11 +1380,11 @@ static int vpif_s_dv_preset(struct file *file, void *priv, ch->video.dv_preset = preset->preset; ch->video.stdid = V4L2_STD_UNKNOWN; + memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings)); /* Get the information about the standard */ - if (vpif_get_std_info(ch)) { + if (vpif_update_resolution(ch)) { ret = -EINVAL; - vpif_dbg(1, debug, "Error getting the standard info\n"); } else { /* Configure the default format information */ vpif_config_format(ch); @@ -1395,6 +1413,126 @@ static int vpif_g_dv_preset(struct file *file, void *priv, return 0; } +/** + * vpif_s_dv_timings() - S_DV_TIMINGS handler + * @file: file ptr + * @priv: file handle + * @timings: digital video timings + */ +static int vpif_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct vpif_params *vpifparams = &ch->vpifparams; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; + struct video_obj *vid_ch = &ch->video; + struct v4l2_bt_timings *bt = &vid_ch->bt_timings; + int ret; + + if (timings->type != V4L2_DV_BT_656_1120) { + vpif_dbg(2, debug, "Timing type not defined\n"); + return -EINVAL; + } + + /* Configure subdevice timings, if any */ + ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], + video, s_dv_timings, timings); + if (ret == -ENOIOCTLCMD) { + vpif_dbg(2, debug, "Custom DV timings not supported by " + "subdevice\n"); + return -EINVAL; + } + if (ret < 0) { + vpif_dbg(2, debug, "Error setting custom DV timings\n"); + return ret; + } + + if (!(timings->bt.width && timings->bt.height && + (timings->bt.hbackporch || + timings->bt.hfrontporch || + timings->bt.hsync) && + timings->bt.vfrontporch && + (timings->bt.vbackporch || + timings->bt.vsync))) { + vpif_dbg(2, debug, "Timings for width, height, " + "horizontal back porch, horizontal sync, " + "horizontal front porch, vertical back porch, " + "vertical sync and vertical back porch " + "must be defined\n"); + return -EINVAL; + } + + *bt = timings->bt; + + /* Configure video port timings */ + + std_info->eav2sav = bt->hbackporch + bt->hfrontporch + + bt->hsync - 8; + std_info->sav2eav = bt->width; + + std_info->l1 = 1; + std_info->l3 = bt->vsync + bt->vbackporch + 1; + + if (bt->interlaced) { + if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) { + std_info->vsize = bt->height * 2 + + bt->vfrontporch + bt->vsync + bt->vbackporch + + bt->il_vfrontporch + bt->il_vsync + + bt->il_vbackporch; + std_info->l5 = std_info->vsize/2 - + (bt->vfrontporch - 1); + std_info->l7 = std_info->vsize/2 + 1; + std_info->l9 = std_info->l7 + bt->il_vsync + + bt->il_vbackporch + 1; + std_info->l11 = std_info->vsize - + (bt->il_vfrontporch - 1); + } else { + vpif_dbg(2, debug, "Required timing values for " + "interlaced BT format missing\n"); + return -EINVAL; + } + } else { + std_info->vsize = bt->height + bt->vfrontporch + + bt->vsync + bt->vbackporch; + std_info->l5 = std_info->vsize - (bt->vfrontporch - 1); + } + strncpy(std_info->name, "Custom timings BT656/1120", + VPIF_MAX_NAME); + std_info->width = bt->width; + std_info->height = bt->height; + std_info->frm_fmt = bt->interlaced ? 0 : 1; + std_info->ycmux_mode = 0; + std_info->capture_format = 0; + std_info->vbi_supported = 0; + std_info->hd_sd = 1; + std_info->stdid = 0; + std_info->dv_preset = V4L2_DV_INVALID; + + vid_ch->stdid = 0; + vid_ch->dv_preset = V4L2_DV_INVALID; + + return 0; +} + +/** + * vpif_g_dv_timings() - G_DV_TIMINGS handler + * @file: file ptr + * @priv: file handle + * @timings: digital video timings + */ +static int vpif_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + struct v4l2_bt_timings *bt = &vid_ch->bt_timings; + + timings->bt = *bt; + + return 0; +} /* * vpif_g_chip_ident() - Identify the chip @@ -1498,6 +1636,8 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_enum_dv_presets = vpif_enum_dv_presets, .vidioc_s_dv_preset = vpif_s_dv_preset, .vidioc_g_dv_preset = vpif_g_dv_preset, + .vidioc_s_dv_timings = vpif_s_dv_timings, + .vidioc_g_dv_timings = vpif_g_dv_timings, .vidioc_g_chip_ident = vpif_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vpif_dbg_g_register, diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h index 3d56b3e356a2..b53aaa883075 100644 --- a/drivers/media/video/davinci/vpif_display.h +++ b/drivers/media/video/davinci/vpif_display.h @@ -68,6 +68,7 @@ struct video_obj { v4l2_std_id stdid; /* Currently selected or default * standard */ u32 dv_preset; + struct v4l2_bt_timings bt_timings; u32 output_id; /* Current output id */ }; -- cgit v1.2.3 From 2c0ddd17741383009c53cf557d6526848c8bb917 Mon Sep 17 00:00:00 2001 From: Mats Randgaard Date: Thu, 16 Dec 2010 12:17:45 -0300 Subject: [media] vpif_cap/disp: Cleanup, improved comments Signed-off-by: Mats Randgaard Signed-off-by: Hans Verkuil Acked-by: Murali Karicheri Acked-by: Vaibhav Hiremath Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif.h | 13 ++++++------- drivers/media/video/davinci/vpif_capture.c | 13 ++++++------- drivers/media/video/davinci/vpif_display.c | 16 +++++++++++++--- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index b6695bee3c59..10550bd93b06 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -577,11 +577,10 @@ struct vpif_channel_config_params { char name[VPIF_MAX_NAME]; /* Name of the mode */ u16 width; /* Indicates width of the image */ u16 height; /* Indicates height of the image */ - u8 frm_fmt; /* Indicates whether this is interlaced - * or progressive format */ - u8 ycmux_mode; /* Indicates whether this mode requires - * single or two channels */ - u16 eav2sav; /* length of sav 2 eav */ + u8 frm_fmt; /* Interlaced (0) or progressive (1) */ + u8 ycmux_mode; /* This mode requires one (0) or two (1) + channels */ + u16 eav2sav; /* length of eav 2 sav */ u16 sav2eav; /* length of sav 2 eav */ u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */ u16 vsize; /* Vertical size of the image */ @@ -589,8 +588,8 @@ struct vpif_channel_config_params { * is in BT or in CCD/CMOS */ u8 vbi_supported; /* Indicates whether this mode * supports capturing vbi or not */ - u8 hd_sd; - v4l2_std_id stdid; + u8 hd_sd; /* HDTV (1) or SDTV (0) format */ + v4l2_std_id stdid; /* SDTV format */ u32 dv_preset; /* HDTV format */ }; diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index be2b9919937c..f8e65909dc37 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -329,7 +329,7 @@ static void vpif_schedule_next_buffer(struct common_obj *common) * @dev_id: dev_id ptr * * It changes status of the captured buffer, takes next buffer from the queue - * and sets its address in VPIF registers + * and sets its address in VPIF registers */ static irqreturn_t vpif_channel_isr(int irq, void *dev_id) { @@ -422,14 +422,12 @@ static int vpif_update_std_info(struct channel_obj *ch) struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct vpif_params *vpifparams = &ch->vpifparams; const struct vpif_channel_config_params *config; - struct vpif_channel_config_params *std_info; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; struct video_obj *vid_ch = &ch->video; int index; vpif_dbg(2, debug, "vpif_update_std_info\n"); - std_info = &vpifparams->std_info; - for (index = 0; index < vpif_ch_params_count; index++) { config = &ch_params[index]; if (config->hd_sd == 0) { @@ -458,6 +456,7 @@ static int vpif_update_std_info(struct channel_obj *ch) common->fmt.fmt.pix.bytesperline = std_info->width; vpifparams->video_params.hpitch = std_info->width; vpifparams->video_params.storage_mode = std_info->frm_fmt; + return 0; } @@ -1692,7 +1691,7 @@ static int vpif_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_pix_format *pixfmt; int ret = 0; - vpif_dbg(2, debug, "VIDIOC_S_FMT\n"); + vpif_dbg(2, debug, "%s\n", __func__); /* If streaming is started, return error */ if (common->started) { @@ -2336,9 +2335,9 @@ static __init int vpif_probe(struct platform_device *pdev) if (vpif_obj.sd[i]) vpif_obj.sd[i]->grp_id = 1 << i; } - v4l2_info(&vpif_obj.v4l2_dev, "DM646x VPIF Capture driver" - " initialized\n"); + v4l2_info(&vpif_obj.v4l2_dev, + "DM646x VPIF capture driver initialized\n"); return 0; probe_subdev_out: diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 44a885878ebf..7cb70d916f4b 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -580,7 +580,10 @@ static void vpif_config_addr(struct channel_obj *ch, int muxmode) static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) { struct vpif_fh *fh = filep->private_data; - struct common_obj *common = &fh->channel->common[VPIF_VIDEO_INDEX]; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); + + vpif_dbg(2, debug, "vpif_mmap\n"); return videobuf_mmap_mapper(&common->buffer_queue, vma); } @@ -692,7 +695,12 @@ static int vpif_release(struct file *filep) } /* functions implementing ioctls */ - +/** + * vpif_querycap() - QUERYCAP handler + * @file: file ptr + * @priv: file handle + * @cap: ptr to v4l2_capability structure + */ static int vpif_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -1106,7 +1114,7 @@ static int vpif_streamon(struct file *file, void *priv, if (ret < 0) return ret; - /* Call videobuf_streamon to start streaming in videobuf */ + /* Call videobuf_streamon to start streaming in videobuf */ ret = videobuf_streamon(&common->buffer_queue); if (ret < 0) { vpif_err("videobuf_streamon\n"); @@ -1873,6 +1881,8 @@ static __init int vpif_probe(struct platform_device *pdev) vpif_obj.sd[i]->grp_id = 1 << i; } + v4l2_info(&vpif_obj.v4l2_dev, + "DM646x VPIF display driver initialized\n"); return 0; probe_subdev_out: -- cgit v1.2.3 From 46656afa8a9a410b7939b248712e4e48921929da Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 4 Jan 2011 06:51:35 -0300 Subject: [media] davinci: convert vpif_capture to core-assisted locking Now uses .unlocked_ioctl instead of .ioctl. Signed-off-by: Hans Verkuil Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif_capture.c | 90 +++++------------------------- 1 file changed, 14 insertions(+), 76 deletions(-) diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index f8e65909dc37..d93ad74a34c5 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -752,7 +752,7 @@ static int vpif_open(struct file *filep) struct video_obj *vid_ch; struct channel_obj *ch; struct vpif_fh *fh; - int i, ret = 0; + int i; vpif_dbg(2, debug, "vpif_open\n"); @@ -761,9 +761,6 @@ static int vpif_open(struct file *filep) vid_ch = &ch->video; common = &ch->common[VPIF_VIDEO_INDEX]; - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - if (NULL == ch->curr_subdev_info) { /** * search through the sub device to see a registered @@ -780,8 +777,7 @@ static int vpif_open(struct file *filep) } if (i == config->subdev_count) { vpif_err("No sub device registered\n"); - ret = -ENOENT; - goto exit; + return -ENOENT; } } @@ -789,8 +785,7 @@ static int vpif_open(struct file *filep) fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); if (NULL == fh) { vpif_err("unable to allocate memory for file handle object\n"); - ret = -ENOMEM; - goto exit; + return -ENOMEM; } /* store pointer to fh in private_data member of filep */ @@ -810,9 +805,7 @@ static int vpif_open(struct file *filep) /* Initialize priority of this instance to default priority */ fh->prio = V4L2_PRIORITY_UNSET; v4l2_prio_open(&ch->prio, &fh->prio); -exit: - mutex_unlock(&common->lock); - return ret; + return 0; } /** @@ -832,9 +825,6 @@ static int vpif_release(struct file *filep) common = &ch->common[VPIF_VIDEO_INDEX]; - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - /* if this instance is doing IO */ if (fh->io_allowed[VPIF_VIDEO_INDEX]) { /* Reset io_usrs member of channel object */ @@ -858,9 +848,6 @@ static int vpif_release(struct file *filep) /* Decrement channel usrs counter */ ch->usrs--; - /* unlock mutex on channel object */ - mutex_unlock(&common->lock); - /* Close the priority */ v4l2_prio_close(&ch->prio, fh->prio); @@ -885,7 +872,6 @@ static int vpif_reqbufs(struct file *file, void *priv, struct channel_obj *ch = fh->channel; struct common_obj *common; u8 index = 0; - int ret = 0; vpif_dbg(2, debug, "vpif_reqbufs\n"); @@ -908,13 +894,8 @@ static int vpif_reqbufs(struct file *file, void *priv, common = &ch->common[index]; - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - - if (0 != common->io_usrs) { - ret = -EBUSY; - goto reqbuf_exit; - } + if (0 != common->io_usrs) + return -EBUSY; /* Initialize videobuf queue as per the buffer type */ videobuf_queue_dma_contig_init(&common->buffer_queue, @@ -923,7 +904,7 @@ static int vpif_reqbufs(struct file *file, void *priv, reqbuf->type, common->fmt.fmt.pix.field, sizeof(struct videobuf_buffer), fh, - NULL); + &common->lock); /* Set io allowed member of file handle to TRUE */ fh->io_allowed[index] = 1; @@ -934,11 +915,7 @@ static int vpif_reqbufs(struct file *file, void *priv, INIT_LIST_HEAD(&common->dma_queue); /* Allocate buffers */ - ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); - -reqbuf_exit: - mutex_unlock(&common->lock); - return ret; + return videobuf_reqbufs(&common->buffer_queue, reqbuf); } /** @@ -1152,11 +1129,6 @@ static int vpif_streamon(struct file *file, void *priv, return ret; } - if (mutex_lock_interruptible(&common->lock)) { - ret = -ERESTARTSYS; - goto streamoff_exit; - } - /* If buffer queue is empty, return error */ if (list_empty(&common->dma_queue)) { vpif_dbg(1, debug, "buffer queue is empty\n"); @@ -1235,13 +1207,10 @@ static int vpif_streamon(struct file *file, void *priv, enable_channel1(1); } channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; - mutex_unlock(&common->lock); return ret; exit: - mutex_unlock(&common->lock); -streamoff_exit: - ret = videobuf_streamoff(&common->buffer_queue); + videobuf_streamoff(&common->buffer_queue); return ret; } @@ -1279,9 +1248,6 @@ static int vpif_streamoff(struct file *file, void *priv, return -EINVAL; } - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - /* disable channel */ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { enable_channel0(0); @@ -1299,8 +1265,6 @@ static int vpif_streamoff(struct file *file, void *priv, if (ret && (ret != -ENOIOCTLCMD)) vpif_dbg(1, debug, "stream off failed in subdev\n"); - mutex_unlock(&common->lock); - return videobuf_streamoff(&common->buffer_queue); } @@ -1376,21 +1340,16 @@ static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id) { struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; - struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; int ret = 0; vpif_dbg(2, debug, "vpif_querystd\n"); - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - /* Call querystd function of decoder device */ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video, querystd, std_id); if (ret < 0) vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); - mutex_unlock(&common->lock); return ret; } @@ -1446,18 +1405,14 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) fh->initialized = 1; /* Call encoder subdevice function to set the standard */ - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - ch->video.stdid = *std_id; ch->video.dv_preset = V4L2_DV_INVALID; memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings)); /* Get the information about the standard */ if (vpif_update_std_info(ch)) { - ret = -EINVAL; vpif_err("Error getting the standard info\n"); - goto s_std_exit; + return -EINVAL; } /* Configure the default format information */ @@ -1468,9 +1423,6 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) s_std, *std_id); if (ret < 0) vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); - -s_std_exit: - mutex_unlock(&common->lock); return ret; } @@ -1564,9 +1516,6 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) return -EINVAL; } - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - /* first setup input path from sub device to vpif */ if (config->setup_input_path) { ret = config->setup_input_path(ch->channel_id, @@ -1575,7 +1524,7 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) vpif_dbg(1, debug, "couldn't setup input path for the" " sub device %s, for input index %d\n", subdev_info->name, index); - goto exit; + return ret; } } @@ -1586,7 +1535,7 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) input, output, 0); if (ret < 0) { vpif_dbg(1, debug, "Failed to set input\n"); - goto exit; + return ret; } } vid_ch->input_idx = index; @@ -1597,9 +1546,6 @@ static int vpif_s_input(struct file *file, void *priv, unsigned int index) /* update tvnorms from the sub device input info */ ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std; - -exit: - mutex_unlock(&common->lock); return ret; } @@ -1668,11 +1614,7 @@ static int vpif_g_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; /* Fill in the information about format */ - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - *fmt = common->fmt; - mutex_unlock(&common->lock); return 0; } @@ -1720,12 +1662,7 @@ static int vpif_s_fmt_vid_cap(struct file *file, void *priv, if (ret) return ret; /* store the format in the channel object */ - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - common->fmt = *fmt; - mutex_unlock(&common->lock); - return 0; } @@ -2145,7 +2082,7 @@ static struct v4l2_file_operations vpif_fops = { .owner = THIS_MODULE, .open = vpif_open, .release = vpif_release, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, .mmap = vpif_mmap, .poll = vpif_poll }; @@ -2288,6 +2225,7 @@ static __init int vpif_probe(struct platform_device *pdev) common = &(ch->common[VPIF_VIDEO_INDEX]); spin_lock_init(&common->irqlock); mutex_init(&common->lock); + ch->video_dev->lock = &common->lock; /* Initialize prio member of channel object */ v4l2_prio_init(&ch->prio); err = video_register_device(ch->video_dev, -- cgit v1.2.3 From 9bfaae24f991ff5255de17cd05838d1cd131727c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Jan 2011 13:35:45 -0300 Subject: [media] davinci: convert vpif_display to core-assisted locking vpif_display now uses .unlocked_ioctl instead of .ioctl. Signed-off-by: Hans Verkuil Acked-by: Manjunath Hadli Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpif_display.c | 98 ++++++------------------------ 1 file changed, 20 insertions(+), 78 deletions(-) diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 7cb70d916f4b..cdf659abdc2a 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -652,9 +652,6 @@ static int vpif_release(struct file *filep) struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - /* if this instance is doing IO */ if (fh->io_allowed[VPIF_VIDEO_INDEX]) { /* Reset io_usrs member of channel object */ @@ -677,8 +674,6 @@ static int vpif_release(struct file *filep) config_params.numbuffers[ch->channel_id]; } - mutex_unlock(&common->lock); - /* Decrement channel usrs counter */ atomic_dec(&ch->usrs); /* If this file handle has initialize encoder device, reset it */ @@ -737,24 +732,15 @@ static int vpif_g_fmt_vid_out(struct file *file, void *priv, struct vpif_fh *fh = priv; struct channel_obj *ch = fh->channel; struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; - int ret = 0; /* Check the validity of the buffer type */ if (common->fmt.type != fmt->type) return -EINVAL; - /* Fill in the information about format */ - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - if (vpif_update_resolution(ch)) - ret = -EINVAL; - else - *fmt = common->fmt; - - mutex_unlock(&common->lock); - - return ret; + return -EINVAL; + *fmt = common->fmt; + return 0; } static int vpif_s_fmt_vid_out(struct file *file, void *priv, @@ -794,12 +780,7 @@ static int vpif_s_fmt_vid_out(struct file *file, void *priv, /* store the pix format in the channel object */ common->fmt.fmt.pix = *pixfmt; /* store the format in the channel object */ - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - common->fmt = *fmt; - mutex_unlock(&common->lock); - return 0; } @@ -829,7 +810,6 @@ static int vpif_reqbufs(struct file *file, void *priv, struct common_obj *common; enum v4l2_field field; u8 index = 0; - int ret = 0; /* This file handle has not initialized the channel, It is not allowed to do settings */ @@ -847,18 +827,12 @@ static int vpif_reqbufs(struct file *file, void *priv, index = VPIF_VIDEO_INDEX; common = &ch->common[index]; - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - if (common->fmt.type != reqbuf->type) { - ret = -EINVAL; - goto reqbuf_exit; - } + if (common->fmt.type != reqbuf->type) + return -EINVAL; - if (0 != common->io_usrs) { - ret = -EBUSY; - goto reqbuf_exit; - } + if (0 != common->io_usrs) + return -EBUSY; if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) @@ -875,7 +849,7 @@ static int vpif_reqbufs(struct file *file, void *priv, &common->irqlock, reqbuf->type, field, sizeof(struct videobuf_buffer), fh, - NULL); + &common->lock); /* Set io allowed member of file handle to TRUE */ fh->io_allowed[index] = 1; @@ -886,11 +860,7 @@ static int vpif_reqbufs(struct file *file, void *priv, INIT_LIST_HEAD(&common->dma_queue); /* Allocate buffers */ - ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); - -reqbuf_exit: - mutex_unlock(&common->lock); - return ret; + return videobuf_reqbufs(&common->buffer_queue, reqbuf); } static int vpif_querybuf(struct file *file, void *priv, @@ -1011,25 +981,19 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) } /* Call encoder subdevice function to set the standard */ - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - ch->video.stdid = *std_id; ch->video.dv_preset = V4L2_DV_INVALID; memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings)); /* Get the information about the standard */ - if (vpif_update_resolution(ch)) { - ret = -EINVAL; - goto s_std_exit; - } + if (vpif_update_resolution(ch)) + return -EINVAL; if ((ch->vpifparams.std_info.width * ch->vpifparams.std_info.height * 2) > config_params.channel_bufsize[ch->channel_id]) { vpif_err("invalid std for this size\n"); - ret = -EINVAL; - goto s_std_exit; + return -EINVAL; } common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; @@ -1040,16 +1004,13 @@ static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) s_std_output, *std_id); if (ret < 0) { vpif_err("Failed to set output standard\n"); - goto s_std_exit; + return ret; } ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, s_std, *std_id); if (ret < 0) vpif_err("Failed to set standard for sub devices\n"); - -s_std_exit: - mutex_unlock(&common->lock); return ret; } @@ -1121,14 +1082,10 @@ static int vpif_streamon(struct file *file, void *priv, return ret; } - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - /* If buffer queue is empty, return error */ if (list_empty(&common->dma_queue)) { vpif_err("buffer queue is empty\n"); - ret = -EIO; - goto streamon_exit; + return -EIO; } /* Get the next frame from the buffer queue */ @@ -1154,8 +1111,7 @@ static int vpif_streamon(struct file *file, void *priv, || (!ch->vpifparams.std_info.frm_fmt && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { vpif_err("conflict in field format and std format\n"); - ret = -EINVAL; - goto streamon_exit; + return -EINVAL; } /* clock settings */ @@ -1164,13 +1120,13 @@ static int vpif_streamon(struct file *file, void *priv, ch->vpifparams.std_info.hd_sd); if (ret < 0) { vpif_err("can't set clock\n"); - goto streamon_exit; + return ret; } /* set the parameters and addresses */ ret = vpif_set_video_params(vpif, ch->channel_id + 2); if (ret < 0) - goto streamon_exit; + return ret; common->started = ret; vpif_config_addr(ch, ret); @@ -1195,9 +1151,6 @@ static int vpif_streamon(struct file *file, void *priv, } channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; } - -streamon_exit: - mutex_unlock(&common->lock); return ret; } @@ -1223,9 +1176,6 @@ static int vpif_streamoff(struct file *file, void *priv, return -EINVAL; } - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { /* disable channel */ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { @@ -1240,8 +1190,6 @@ static int vpif_streamoff(struct file *file, void *priv, } common->started = 0; - mutex_unlock(&common->lock); - return videobuf_streamoff(&common->buffer_queue); } @@ -1288,13 +1236,9 @@ static int vpif_s_output(struct file *file, void *priv, unsigned int i) struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; int ret = 0; - if (mutex_lock_interruptible(&common->lock)) - return -ERESTARTSYS; - if (common->started) { vpif_err("Streaming in progress\n"); - ret = -EBUSY; - goto s_output_exit; + return -EBUSY; } ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, @@ -1304,9 +1248,6 @@ static int vpif_s_output(struct file *file, void *priv, unsigned int i) vpif_err("Failed to set output standard\n"); vid_ch->output_id = i; - -s_output_exit: - mutex_unlock(&common->lock); return ret; } @@ -1658,7 +1599,7 @@ static const struct v4l2_file_operations vpif_fops = { .owner = THIS_MODULE, .open = vpif_open, .release = vpif_release, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, .mmap = vpif_mmap, .poll = vpif_poll }; @@ -1842,6 +1783,7 @@ static __init int vpif_probe(struct platform_device *pdev) v4l2_prio_init(&ch->prio); ch->common[VPIF_VIDEO_INDEX].fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ch->video_dev->lock = &common->lock; /* register video device */ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", -- cgit v1.2.3 From 90d873a7f9389478e68494c530afab8b19c04f29 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Jan 2011 14:40:07 -0300 Subject: [media] radio-maxiradio.c: use sensible frequency range Use the standard USA/Europe frequency range of 87-108 MHz instead of the arbitrary 50-150 MHz. Copied from the radio-gemtek-pci driver which supports the same hardware. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-maxiradio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index 6459a220b0dd..5c2a9058c09f 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -77,8 +77,8 @@ MODULE_PARM_DESC(debug, "activates debug info"); /* TEA5757 pin mappings */ static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16; -#define FREQ_LO (50 * 16000) -#define FREQ_HI (150 * 16000) +#define FREQ_LO (87 * 16000) +#define FREQ_HI (108 * 16000) #define FREQ_IF 171200 /* 10.7*16000 */ #define FREQ_STEP 200 /* 12.5*16 */ -- cgit v1.2.3 From 424852f4374561630d526e7bb04eec5bb7d665a1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 5 Jan 2011 14:42:41 -0300 Subject: [media] radio-gemtek-pci: remove duplicate driver The radio-gemtek-pci driver is for the same hardware as the radio-maxiradio driver which uses the same GemTek PR103 and tea5757 combination and the two drivers are identical. I chose the maxiradio over the gemtek-pci driver since the maxiradio has support for mono/stereo detection. Tested with my gemtek-pci card. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 14 - drivers/media/radio/Makefile | 1 - drivers/media/radio/radio-gemtek-pci.c | 478 --------------------------------- 3 files changed, 493 deletions(-) delete mode 100644 drivers/media/radio/radio-gemtek-pci.c diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 3c5a4739ed70..ecdffa6aac66 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -151,20 +151,6 @@ config RADIO_GEMTEK_PROBE following ports will be probed: 0x20c, 0x30c, 0x24c, 0x34c, 0x248 and 0x28c. -config RADIO_GEMTEK_PCI - tristate "GemTek PCI Radio Card support" - depends on VIDEO_V4L2 && PCI - ---help--- - Choose Y here if you have this PCI FM radio card. - - In order to control your radio card, you will need to use programs - that are compatible with the Video for Linux API. Information on - this API and pointers to "v4l" programs may be found at - . - - To compile this driver as a module, choose M here: the - module will be called radio-gemtek-pci. - config RADIO_MAXIRADIO tristate "Guillemot MAXI Radio FM 2000 radio" depends on VIDEO_V4L2 && PCI diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index d2970748a69f..717656d2f749 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o -obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c deleted file mode 100644 index 28fa85ba2087..000000000000 --- a/drivers/media/radio/radio-gemtek-pci.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - *************************************************************************** - * - * radio-gemtek-pci.c - Gemtek PCI Radio driver - * (C) 2001 Vladimir Shebordaev - * - *************************************************************************** - * - * 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., 675 Mass Ave, Cambridge, MA 02139, - * USA. - * - *************************************************************************** - * - * Gemtek Corp still silently refuses to release any specifications - * of their multimedia devices, so the protocol still has to be - * reverse engineered. - * - * The v4l code was inspired by Jonas Munsin's Gemtek serial line - * radio device driver. - * - * Please, let me know if this piece of code was useful :) - * - * TODO: multiple device support and portability were not tested - * - * Converted to V4L2 API by Mauro Carvalho Chehab - * - *************************************************************************** - */ - -#include -#include -#include -#include -#include -#include -#include -#include /* for KERNEL_VERSION MACRO */ -#include -#include -#include -#include - -MODULE_AUTHOR("Vladimir Shebordaev "); -MODULE_DESCRIPTION("The video4linux driver for the Gemtek PCI Radio Card"); -MODULE_LICENSE("GPL"); - -static int nr_radio = -1; -static int mx = 1; - -module_param(mx, bool, 0); -MODULE_PARM_DESC(mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not"); -module_param(nr_radio, int, 0); -MODULE_PARM_DESC(nr_radio, "video4linux device number to use"); - -#define RADIO_VERSION KERNEL_VERSION(0, 0, 2) - -#ifndef PCI_VENDOR_ID_GEMTEK -#define PCI_VENDOR_ID_GEMTEK 0x5046 -#endif - -#ifndef PCI_DEVICE_ID_GEMTEK_PR103 -#define PCI_DEVICE_ID_GEMTEK_PR103 0x1001 -#endif - -#ifndef GEMTEK_PCI_RANGE_LOW -#define GEMTEK_PCI_RANGE_LOW (87*16000) -#endif - -#ifndef GEMTEK_PCI_RANGE_HIGH -#define GEMTEK_PCI_RANGE_HIGH (108*16000) -#endif - -struct gemtek_pci { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct mutex lock; - struct pci_dev *pdev; - - u32 iobase; - u32 length; - - u32 current_frequency; - u8 mute; -}; - -static inline struct gemtek_pci *to_gemtek_pci(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct gemtek_pci, v4l2_dev); -} - -static inline u8 gemtek_pci_out(u16 value, u32 port) -{ - outw(value, port); - - return (u8)value; -} - -#define _b0(v) (*((u8 *)&v)) - -static void __gemtek_pci_cmd(u16 value, u32 port, u8 *last_byte, int keep) -{ - u8 byte = *last_byte; - - if (!value) { - if (!keep) - value = (u16)port; - byte &= 0xfd; - } else - byte |= 2; - - _b0(value) = byte; - outw(value, port); - byte |= 1; - _b0(value) = byte; - outw(value, port); - byte &= 0xfe; - _b0(value) = byte; - outw(value, port); - - *last_byte = byte; -} - -static inline void gemtek_pci_nil(u32 port, u8 *last_byte) -{ - __gemtek_pci_cmd(0x00, port, last_byte, false); -} - -static inline void gemtek_pci_cmd(u16 cmd, u32 port, u8 *last_byte) -{ - __gemtek_pci_cmd(cmd, port, last_byte, true); -} - -static void gemtek_pci_setfrequency(struct gemtek_pci *card, unsigned long frequency) -{ - int i; - u32 value = frequency / 200 + 856; - u16 mask = 0x8000; - u8 last_byte; - u32 port = card->iobase; - - mutex_lock(&card->lock); - card->current_frequency = frequency; - last_byte = gemtek_pci_out(0x06, port); - - i = 0; - do { - gemtek_pci_nil(port, &last_byte); - i++; - } while (i < 9); - - i = 0; - do { - gemtek_pci_cmd(value & mask, port, &last_byte); - mask >>= 1; - i++; - } while (i < 16); - - outw(0x10, port); - mutex_unlock(&card->lock); -} - - -static void gemtek_pci_mute(struct gemtek_pci *card) -{ - mutex_lock(&card->lock); - outb(0x1f, card->iobase); - card->mute = true; - mutex_unlock(&card->lock); -} - -static void gemtek_pci_unmute(struct gemtek_pci *card) -{ - if (card->mute) { - gemtek_pci_setfrequency(card, card->current_frequency); - card->mute = false; - } -} - -static int gemtek_pci_getsignal(struct gemtek_pci *card) -{ - int sig; - - mutex_lock(&card->lock); - sig = (inb(card->iobase) & 0x08) ? 0 : 1; - mutex_unlock(&card->lock); - return sig; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct gemtek_pci *card = video_drvdata(file); - - strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver)); - strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card)); - snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(card->pdev)); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - return 0; -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - struct gemtek_pci *card = video_drvdata(file); - - if (v->index > 0) - return -EINVAL; - - strlcpy(v->name, "FM", sizeof(v->name)); - v->type = V4L2_TUNER_RADIO; - v->rangelow = GEMTEK_PCI_RANGE_LOW; - v->rangehigh = GEMTEK_PCI_RANGE_HIGH; - v->rxsubchans = V4L2_TUNER_SUB_MONO; - v->capability = V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff * gemtek_pci_getsignal(card); - return 0; -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) -{ - return v->index ? -EINVAL : 0; -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct gemtek_pci *card = video_drvdata(file); - - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) - return -EINVAL; - if (f->frequency < GEMTEK_PCI_RANGE_LOW || - f->frequency > GEMTEK_PCI_RANGE_HIGH) - return -EINVAL; - gemtek_pci_setfrequency(card, f->frequency); - card->mute = false; - return 0; -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct gemtek_pci *card = video_drvdata(file); - - if (f->tuner != 0) - return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = card->current_frequency; - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535); - } - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gemtek_pci *card = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = card->mute; - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (card->mute) - ctrl->value = 0; - else - ctrl->value = 65535; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct gemtek_pci *card = video_drvdata(file); - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - gemtek_pci_mute(card); - else - gemtek_pci_unmute(card); - return 0; - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) - gemtek_pci_unmute(card); - else - gemtek_pci_mute(card); - return 0; - } - return -EINVAL; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - a->index = 0; - strlcpy(a->name, "Radio", sizeof(a->name)); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} - -enum { - GEMTEK_PR103 -}; - -static char *card_names[] __devinitdata = { - "GEMTEK_PR103" -}; - -static struct pci_device_id gemtek_pci_id[] = -{ - { PCI_VENDOR_ID_GEMTEK, PCI_DEVICE_ID_GEMTEK_PR103, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, GEMTEK_PR103 }, - { 0 } -}; - -MODULE_DEVICE_TABLE(pci, gemtek_pci_id); - -static const struct v4l2_file_operations gemtek_pci_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops gemtek_pci_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, -}; - -static int __devinit gemtek_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) -{ - struct gemtek_pci *card; - struct v4l2_device *v4l2_dev; - int res; - - card = kzalloc(sizeof(struct gemtek_pci), GFP_KERNEL); - if (card == NULL) { - dev_err(&pdev->dev, "out of memory\n"); - return -ENOMEM; - } - - v4l2_dev = &card->v4l2_dev; - mutex_init(&card->lock); - card->pdev = pdev; - - strlcpy(v4l2_dev->name, "gemtek_pci", sizeof(v4l2_dev->name)); - - res = v4l2_device_register(&pdev->dev, v4l2_dev); - if (res < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - kfree(card); - return res; - } - - if (pci_enable_device(pdev)) - goto err_pci; - - card->iobase = pci_resource_start(pdev, 0); - card->length = pci_resource_len(pdev, 0); - - if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) { - v4l2_err(v4l2_dev, "i/o port already in use\n"); - goto err_pci; - } - - strlcpy(card->vdev.name, v4l2_dev->name, sizeof(card->vdev.name)); - card->vdev.v4l2_dev = v4l2_dev; - card->vdev.fops = &gemtek_pci_fops; - card->vdev.ioctl_ops = &gemtek_pci_ioctl_ops; - card->vdev.release = video_device_release_empty; - video_set_drvdata(&card->vdev, card); - - gemtek_pci_mute(card); - - if (video_register_device(&card->vdev, VFL_TYPE_RADIO, nr_radio) < 0) - goto err_video; - - v4l2_info(v4l2_dev, "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n", - pdev->revision, card->iobase, card->iobase + card->length - 1); - - return 0; - -err_video: - release_region(card->iobase, card->length); - -err_pci: - v4l2_device_unregister(v4l2_dev); - kfree(card); - return -ENODEV; -} - -static void __devexit gemtek_pci_remove(struct pci_dev *pdev) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); - struct gemtek_pci *card = to_gemtek_pci(v4l2_dev); - - video_unregister_device(&card->vdev); - v4l2_device_unregister(v4l2_dev); - - release_region(card->iobase, card->length); - - if (mx) - gemtek_pci_mute(card); - - kfree(card); -} - -static struct pci_driver gemtek_pci_driver = { - .name = "gemtek_pci", - .id_table = gemtek_pci_id, - .probe = gemtek_pci_probe, - .remove = __devexit_p(gemtek_pci_remove), -}; - -static int __init gemtek_pci_init(void) -{ - return pci_register_driver(&gemtek_pci_driver); -} - -static void __exit gemtek_pci_exit(void) -{ - pci_unregister_driver(&gemtek_pci_driver); -} - -module_init(gemtek_pci_init); -module_exit(gemtek_pci_exit); -- cgit v1.2.3 From 82f4b5b67e795fe03952b278873712e4562304d2 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Fri, 7 Jan 2011 21:59:11 -0300 Subject: [media] af9013: fix AF9013 TDA18271 IF config IF freq for DVB-T 7 MHz and 8 MHz was set slightly wrong. Due to that it didn't worked at all (?) for 7 MHz channels and most likely performance was dropped for 8 MHz channels. That bug was pointed by few people during last two months. Thank you. Trivial fix. Compile tested only due to lack of proper HW and signal. Signed-off-by: Antti Palosaari Cc: Romolo Manfredini Cc: Alireza Moini Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/af9013.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index ce222055526d..ba25fa0b0fc2 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -334,11 +334,11 @@ static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw) if_sample_freq = 3300000; /* 3.3 MHz */ break; case BANDWIDTH_7_MHZ: - if_sample_freq = 3800000; /* 3.8 MHz */ + if_sample_freq = 3500000; /* 3.5 MHz */ break; case BANDWIDTH_8_MHZ: default: - if_sample_freq = 4300000; /* 4.3 MHz */ + if_sample_freq = 4000000; /* 4.0 MHz */ break; } } else if (state->config.tuner == AF9013_TUNER_TDA18218) { -- cgit v1.2.3 From 4e770f7602fb2285f3106f98109d0685618ddc22 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Dec 2010 13:11:21 -0300 Subject: [media] gspca_main: Locking fixes 1 The gspca_dev->streaming boolean is protected against multiple access through gspca_dev->queue_lock everywhere except for 2 places. This patch fixes this by bringing it under the lock in vidioc_streamoff. And by removing the check for gspca_dev->streaming in gspca_disconnect, the destroy_urbs call may be called multiple times (and is protected by the usb_lock) and calling wake_up_interruptible can also always be done safely. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 442970073e8a..74626b6a9eb4 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1630,11 +1630,15 @@ static int vidioc_streamoff(struct file *file, void *priv, if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (!gspca_dev->streaming) - return 0; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; + if (!gspca_dev->streaming) { + ret = 0; + goto out; + } + /* check the capture file */ if (gspca_dev->capt_file != file) { ret = -EBUSY; @@ -2341,12 +2345,11 @@ void gspca_disconnect(struct usb_interface *intf) PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); mutex_lock(&gspca_dev->usb_lock); + gspca_dev->present = 0; + wake_up_interruptible(&gspca_dev->wq); - if (gspca_dev->streaming) { - destroy_urbs(gspca_dev); - wake_up_interruptible(&gspca_dev->wq); - } + destroy_urbs(gspca_dev); #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) gspca_input_destroy_urb(gspca_dev); -- cgit v1.2.3 From 27074efa2ee8c1ef07dc5f644104e35d39e43322 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Dec 2010 19:54:33 -0300 Subject: [media] gspca_main: Locking fixes 2 Before this patch vidioc_dqbuf is using its own read_lock, where as other queue related functions use queue_lock. This means that dqbuf is accessing several variables in a racy manor. The most important one being fr_o, which may be changed from underneath dqbuf by vidioc_reqbufs or vidioc_streamoff. Other variables which it accesses unprotected are gspca_dev->memory, gspca_dev->streaming and gspca_dev->capt_file. This patch fixes this by changing vidioc_dqbuf to also use the queue_lock. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 112 ++++++++++++++++++++------------------ drivers/media/video/gspca/gspca.h | 1 - 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 74626b6a9eb4..64ecf669d655 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1831,33 +1831,77 @@ out: return ret; } +static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, + enum v4l2_memory memory) +{ + if (!gspca_dev->present) + return -ENODEV; + if (gspca_dev->capt_file != file || gspca_dev->memory != memory || + !gspca_dev->streaming) + return -EINVAL; + + /* check if a frame is ready */ + return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); +} + +static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, + enum v4l2_memory memory) +{ + int ret; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + ret = frame_ready_nolock(gspca_dev, file, memory); + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + /* - * wait for a video frame + * dequeue a video buffer * - * If a frame is ready, its index is returned. + * If nonblock_ing is false, block until a buffer is available. */ -static int frame_wait(struct gspca_dev *gspca_dev, - int nonblock_ing) +static int vidioc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) { - int i, ret; + struct gspca_dev *gspca_dev = priv; + struct gspca_frame *frame; + int i, j, ret; - /* check if a frame is ready */ - i = gspca_dev->fr_o; - if (i == atomic_read(&gspca_dev->fr_i)) { - if (nonblock_ing) + PDEBUG(D_FRAM, "dqbuf"); + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + for (;;) { + ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); + if (ret < 0) + goto out; + if (ret > 0) + break; + + mutex_unlock(&gspca_dev->queue_lock); + + if (file->f_flags & O_NONBLOCK) return -EAGAIN; /* wait till a frame is ready */ ret = wait_event_interruptible_timeout(gspca_dev->wq, - i != atomic_read(&gspca_dev->fr_i) || - !gspca_dev->streaming || !gspca_dev->present, + frame_ready(gspca_dev, file, v4l2_buf->memory), msecs_to_jiffies(3000)); if (ret < 0) return ret; - if (ret == 0 || !gspca_dev->streaming || !gspca_dev->present) + if (ret == 0) return -EIO; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; } + i = gspca_dev->fr_o; + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; if (gspca_dev->sd_desc->dq_callback) { @@ -1867,46 +1911,7 @@ static int frame_wait(struct gspca_dev *gspca_dev, gspca_dev->sd_desc->dq_callback(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } - return gspca_dev->fr_queue[i]; -} - -/* - * dequeue a video buffer - * - * If nonblock_ing is false, block until a buffer is available. - */ -static int vidioc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *v4l2_buf) -{ - struct gspca_dev *gspca_dev = priv; - struct gspca_frame *frame; - int i, ret; - - PDEBUG(D_FRAM, "dqbuf"); - if (v4l2_buf->memory != gspca_dev->memory) - return -EINVAL; - if (!gspca_dev->present) - return -ENODEV; - - /* if not streaming, be sure the application will not loop forever */ - if (!(file->f_flags & O_NONBLOCK) - && !gspca_dev->streaming && gspca_dev->users == 1) - return -EINVAL; - - /* only the capturing file may dequeue */ - if (gspca_dev->capt_file != file) - return -EINVAL; - - /* only one dequeue / read at a time */ - if (mutex_lock_interruptible(&gspca_dev->read_lock)) - return -ERESTARTSYS; - - ret = frame_wait(gspca_dev, file->f_flags & O_NONBLOCK); - if (ret < 0) - goto out; - i = ret; /* frame index */ - frame = &gspca_dev->frame[i]; if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, @@ -1919,10 +1924,10 @@ static int vidioc_dqbuf(struct file *file, void *priv, } frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); - PDEBUG(D_FRAM, "dqbuf %d", i); + PDEBUG(D_FRAM, "dqbuf %d", j); ret = 0; out: - mutex_unlock(&gspca_dev->read_lock); + mutex_unlock(&gspca_dev->queue_lock); return ret; } @@ -2270,7 +2275,6 @@ int gspca_dev_probe2(struct usb_interface *intf, goto out; mutex_init(&gspca_dev->usb_lock); - mutex_init(&gspca_dev->read_lock); mutex_init(&gspca_dev->queue_lock); init_waitqueue_head(&gspca_dev->wq); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 97b77a26a2eb..a2a1a6aa0606 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -205,7 +205,6 @@ struct gspca_dev { wait_queue_head_t wq; /* wait queue */ struct mutex usb_lock; /* usb exchange protection */ - struct mutex read_lock; /* read protection */ struct mutex queue_lock; /* ISOC queue protection */ int usb_err; /* USB error - protected by usb_lock */ u16 pkt_size; /* ISOC packet size */ -- cgit v1.2.3 From 4a82bc60a9abbfca0c899366ff30c592e8020520 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Dec 2010 20:00:17 -0300 Subject: [media] gspca_main: Update buffer flags even when user_copy fails Before this patch dqbuf errors out on a failing user_copy (with user pointers) before updating the buffer flags, causing a successsfully dequeued buffer to still have the DONE flag, which means that it could no longer be re-queueud (assuming the app somehow survives the segfault). Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 64ecf669d655..fbc5666ee29e 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1912,6 +1912,11 @@ static int vidioc_dqbuf(struct file *file, void *priv, mutex_unlock(&gspca_dev->usb_lock); } + frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; + memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); + PDEBUG(D_FRAM, "dqbuf %d", j); + ret = 0; + if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, @@ -1919,13 +1924,8 @@ static int vidioc_dqbuf(struct file *file, void *priv, PDEBUG(D_ERR|D_STREAM, "dqbuf cp to user failed"); ret = -EFAULT; - goto out; } } - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; - memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); - PDEBUG(D_FRAM, "dqbuf %d", j); - ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); return ret; -- cgit v1.2.3 From 7f6eb118df84715b128e25e99dc6a3ebc5b133d6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Dec 2010 20:20:09 -0300 Subject: [media] gspca_main: Remove no longer used users variable Remove the no longer used / useful users variable, and with that gone there also is no longer a need to take queue_lock in dev_open. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 32 +++++--------------------------- drivers/media/video/gspca/gspca.h | 1 - 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index fbc5666ee29e..0ba42dd4b99b 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1210,29 +1210,15 @@ static void gspca_release(struct video_device *vfd) static int dev_open(struct file *file) { struct gspca_dev *gspca_dev; - int ret; PDEBUG(D_STREAM, "[%s] open", current->comm); gspca_dev = (struct gspca_dev *) video_devdata(file); - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } - - if (gspca_dev->users > 4) { /* (arbitrary value) */ - ret = -EBUSY; - goto out; - } + if (!gspca_dev->present) + return -ENODEV; /* protect the subdriver against rmmod */ - if (!try_module_get(gspca_dev->module)) { - ret = -ENODEV; - goto out; - } - - gspca_dev->users++; + if (!try_module_get(gspca_dev->module)) + return -ENODEV; file->private_data = gspca_dev; #ifdef GSPCA_DEBUG @@ -1244,14 +1230,7 @@ static int dev_open(struct file *file) gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif - ret = 0; -out: - mutex_unlock(&gspca_dev->queue_lock); - if (ret != 0) - PDEBUG(D_ERR|D_STREAM, "open failed err %d", ret); - else - PDEBUG(D_STREAM, "open done"); - return ret; + return 0; } static int dev_close(struct file *file) @@ -1261,7 +1240,6 @@ static int dev_close(struct file *file) PDEBUG(D_STREAM, "[%s] close", current->comm); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; - gspca_dev->users--; /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index a2a1a6aa0606..41755226d389 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -211,7 +211,6 @@ struct gspca_dev { #ifdef CONFIG_PM char frozen; /* suspend - resume */ #endif - char users; /* number of opens */ char present; /* device connected */ char nbufread; /* number of buffers for read() */ char memory; /* memory type (V4L2_MEMORY_xxx) */ -- cgit v1.2.3 From d642de2ed472df308f8ee49417e29030f69b2095 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Dec 2010 04:51:36 -0300 Subject: [media] gspca_main: Set memory type to GSPCA_MEMORY_NO on buffer release Before this patch we were not setting the memory type to GSPCA_MEMORY_NO when the buffers were released by the app doing a reqbufs 0. Nor would the memory type be set to GSPCA_MEMORY_NO on device close, as capture_file already is NULL on device close because of the reqbufs 0. This caused the following problem: -app1 does reqbufs USERPTR for 4 buffers -app1 does reqbufs USERPTR for 0 buffers -app2 tries to do reqbufs MMAP for 4 buffers fails because gspca_dev->memory still is USERPTR Fixing this also allows an app to switch memory type's by unrequesting the buffers and re-requesting them of a different type. This patch also moves the setting of gspca_dev->frsz and gscpa_dev->memory to after alloc_frame succeeding, so that they are not changed when allocating fails. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 0ba42dd4b99b..adab34f91a68 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -508,8 +508,8 @@ static int gspca_is_compressed(__u32 format) return 0; } -static int frame_alloc(struct gspca_dev *gspca_dev, - unsigned int count) +static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, + enum v4l2_memory memory, unsigned int count) { struct gspca_frame *frame; unsigned int frsz; @@ -519,7 +519,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frsz = gspca_dev->cam.cam_mode[i].sizeimage; PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); frsz = PAGE_ALIGN(frsz); - gspca_dev->frsz = frsz; if (count >= GSPCA_MAX_FRAMES) count = GSPCA_MAX_FRAMES - 1; gspca_dev->frbuf = vmalloc_32(frsz * count); @@ -527,6 +526,9 @@ static int frame_alloc(struct gspca_dev *gspca_dev, err("frame alloc failed"); return -ENOMEM; } + gspca_dev->capt_file = file; + gspca_dev->memory = memory; + gspca_dev->frsz = frsz; gspca_dev->nframes = count; for (i = 0; i < count; i++) { frame = &gspca_dev->frame[i]; @@ -535,7 +537,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frame->v4l2_buf.flags = 0; frame->v4l2_buf.field = V4L2_FIELD_NONE; frame->v4l2_buf.length = frsz; - frame->v4l2_buf.memory = gspca_dev->memory; + frame->v4l2_buf.memory = memory; frame->v4l2_buf.sequence = 0; frame->data = gspca_dev->frbuf + i * frsz; frame->v4l2_buf.m.offset = i * frsz; @@ -558,6 +560,9 @@ static void frame_free(struct gspca_dev *gspca_dev) gspca_dev->frame[i].data = NULL; } gspca_dev->nframes = 0; + gspca_dev->frsz = 0; + gspca_dev->capt_file = NULL; + gspca_dev->memory = GSPCA_MEMORY_NO; } static void destroy_urbs(struct gspca_dev *gspca_dev) @@ -1250,8 +1255,6 @@ static int dev_close(struct file *file) mutex_unlock(&gspca_dev->usb_lock); } frame_free(gspca_dev); - gspca_dev->capt_file = NULL; - gspca_dev->memory = GSPCA_MEMORY_NO; } file->private_data = NULL; module_put(gspca_dev->module); @@ -1524,17 +1527,13 @@ static int vidioc_reqbufs(struct file *file, void *priv, } /* free the previous allocated buffers, if any */ - if (gspca_dev->nframes != 0) { + if (gspca_dev->nframes != 0) frame_free(gspca_dev); - gspca_dev->capt_file = NULL; - } if (rb->count == 0) /* unrequest */ goto out; - gspca_dev->memory = rb->memory; - ret = frame_alloc(gspca_dev, rb->count); + ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); if (ret == 0) { rb->count = gspca_dev->nframes; - gspca_dev->capt_file = file; if (streaming) ret = gspca_init_transfer(gspca_dev); } -- cgit v1.2.3 From ee3629914b2b115f00f4197e80b1e7cb12881059 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Dec 2010 05:05:56 -0300 Subject: [media] gspca_main: Simplify read mode memory type checks gspca_dev->memory == GSPCA_MEMORY_NO implies gspca_dev->nframes == 0, so there is no need to check for both in dev_poll. The check in dev_read also is more complex then needed, as dqbuf which dev_read calls already does all necessary checks. Moreover dqbuf is holding the proper locks while checking where as dev_read itself is not. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index adab34f91a68..244fb76eaead 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2019,9 +2019,7 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) poll_wait(file, &gspca_dev->wq, wait); /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->nframes == 0) { - if (gspca_dev->memory != GSPCA_MEMORY_NO) - return POLLERR; /* not the 1st time */ + if (gspca_dev->memory == GSPCA_MEMORY_NO) { ret = read_alloc(gspca_dev, file); if (ret != 0) return POLLERR; @@ -2053,18 +2051,10 @@ static ssize_t dev_read(struct file *file, char __user *data, PDEBUG(D_FRAM, "read (%zd)", count); if (!gspca_dev->present) return -ENODEV; - switch (gspca_dev->memory) { - case GSPCA_MEMORY_NO: /* first time */ + if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ ret = read_alloc(gspca_dev, file); if (ret != 0) return ret; - break; - case GSPCA_MEMORY_READ: - if (gspca_dev->capt_file == file) - break; - /* fall thru */ - default: - return -EINVAL; } /* get a frame */ -- cgit v1.2.3 From ce5610bca74571674c1970d33063e7c06295a9a4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Dec 2010 07:41:54 -0300 Subject: [media] gspca_main: Allow switching from read to mmap / userptr mode Some applications (xawtv, qv4l2) mix read and mmap calls. Allow switching from read mode back to mmap mode (by doing a reqbufs). Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 244fb76eaead..4ab906be8962 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1497,6 +1497,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -ERESTARTSYS; if (gspca_dev->memory != GSPCA_MEMORY_NO + && gspca_dev->memory != GSPCA_MEMORY_READ && gspca_dev->memory != rb->memory) { ret = -EBUSY; goto out; @@ -1525,6 +1526,9 @@ static int vidioc_reqbufs(struct file *file, void *priv, gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } + /* Don't restart the stream when switching from read to mmap mode */ + if (gspca_dev->memory == GSPCA_MEMORY_READ) + streaming = 0; /* free the previous allocated buffers, if any */ if (gspca_dev->nframes != 0) -- cgit v1.2.3 From 0d0ae15dde7d4a778056268e64bb2625f84deab6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Dec 2010 17:17:51 -0300 Subject: [media] gspca_main: wake wq on streamoff We check for not streaming as a condition to abort waiting in dqbuf, so when another thread does a streamoff we should wake the wq. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 4ab906be8962..3581dea3c1f7 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1634,6 +1634,8 @@ static int vidioc_streamoff(struct file *file, void *priv, gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); + /* In case another thread is waiting in dqbuf */ + wake_up_interruptible(&gspca_dev->wq); /* empty the transfer queues */ atomic_set(&gspca_dev->fr_q, 0); -- cgit v1.2.3 From 8b064ee19d24e57d807060f50e945b5935565852 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Jan 2011 12:48:10 -0300 Subject: [media] et61x251: remove wrongly claimed usb ids The et61x251 driver claims a whole list of usb id's, but it only has one sensor "module" which does sensor detection based on usb id and that only supports devices with the 102c:6251 usb id. Remove the usb-ids for other devices as for those the driver will fail with an unable to determine sensor type message anyways. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/et61x251/et61x251.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h index cc77d144df3c..bf66189cb26d 100644 --- a/drivers/media/video/et61x251/et61x251.h +++ b/drivers/media/video/et61x251/et61x251.h @@ -59,31 +59,7 @@ /*****************************************************************************/ static const struct usb_device_id et61x251_id_table[] = { - { USB_DEVICE(0x102c, 0x6151), }, { USB_DEVICE(0x102c, 0x6251), }, - { USB_DEVICE(0x102c, 0x6253), }, - { USB_DEVICE(0x102c, 0x6254), }, - { USB_DEVICE(0x102c, 0x6255), }, - { USB_DEVICE(0x102c, 0x6256), }, - { USB_DEVICE(0x102c, 0x6257), }, - { USB_DEVICE(0x102c, 0x6258), }, - { USB_DEVICE(0x102c, 0x6259), }, - { USB_DEVICE(0x102c, 0x625a), }, - { USB_DEVICE(0x102c, 0x625b), }, - { USB_DEVICE(0x102c, 0x625c), }, - { USB_DEVICE(0x102c, 0x625d), }, - { USB_DEVICE(0x102c, 0x625e), }, - { USB_DEVICE(0x102c, 0x625f), }, - { USB_DEVICE(0x102c, 0x6260), }, - { USB_DEVICE(0x102c, 0x6261), }, - { USB_DEVICE(0x102c, 0x6262), }, - { USB_DEVICE(0x102c, 0x6263), }, - { USB_DEVICE(0x102c, 0x6264), }, - { USB_DEVICE(0x102c, 0x6265), }, - { USB_DEVICE(0x102c, 0x6266), }, - { USB_DEVICE(0x102c, 0x6267), }, - { USB_DEVICE(0x102c, 0x6268), }, - { USB_DEVICE(0x102c, 0x6269), }, { } }; -- cgit v1.2.3 From a24f0c5c47946b1a1999f44808e2219e7758d146 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 5 Jan 2011 11:36:54 -0300 Subject: [media] sn9c102: Remove not supported and non existing usb ids The sn9c102 driver claims a number of usb-ids which are for cameras with sensor types which it does not support. Also it claims a number of usb-ids which do not exist at all (not present in the windows drivers .inf files, not known by google). This patch also fixes the conflict with the gspca_sonixj driver for the 0c45:60c0 and 0c45:60fb usb ids. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sn9c102/sn9c102_devtable.h | 66 ++++++++++++-------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 41064c7b5ef8..74bc719b8e89 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -56,74 +56,70 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, #endif - { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, /* not in sonixb */ #if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, #endif - { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* not in sonixb */ /* SN9C103 */ - { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ + { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ /* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ - { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ #if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, #endif - { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ -/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5130 */ - { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5110, non existent */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, #if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, #endif - { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ /* SN9C105 */ #if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, PO1030 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, OM6801 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, HV7131GP */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, non existent ? */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, MO4000 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE - { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, -#endif - { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ #if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, -#endif /* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, -- cgit v1.2.3 From 0a76cb8cefbaf84465e0cd9a6a1da7f54981c8ef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 5 Jan 2011 13:55:43 -0300 Subject: [media] gspca_sonixb: Refactor to unify bridge handling Refactor the code to unify how the sn9c101/102 and the sn9c103 bridge are handled. Also move code which is the same for all sensors from the per sensor init register settings to a central place. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 206 +++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 90 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 73504a3f87b7..43784027568f 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -23,8 +23,15 @@ /* Some documentation on known sonixb registers: Reg Use +sn9c101 / sn9c102: 0x10 high nibble red gain low nibble blue gain 0x11 low nibble green gain +sn9c103: +0x05 red gain 0-127 +0x06 blue gain 0-127 +0x07 green gain 0-127 +all: +0x08-0x0f i2c / 3wire registers 0x12 hstart 0x13 vstart 0x15 hsize (hsize = register-value * 16) @@ -88,12 +95,9 @@ struct sd { typedef const __u8 sensor_init_t[8]; struct sensor_data { - const __u8 *bridge_init[2]; - int bridge_init_size[2]; + const __u8 *bridge_init; sensor_init_t *sensor_init; int sensor_init_size; - sensor_init_t *sensor_bridge_init[2]; - int sensor_bridge_init_size[2]; int flags; unsigned ctrl_dis; __u8 sensor_addr; @@ -114,7 +118,6 @@ struct sensor_data { #define NO_FREQ (1 << FREQ_IDX) #define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) -#define COMP2 0x8f #define COMP 0xc7 /* 0x87 //0x07 */ #define COMP1 0xc9 /* 0x89 //0x09 */ @@ -123,15 +126,11 @@ struct sensor_data { #define SYS_CLK 0x04 -#define SENS(bridge_1, bridge_3, sensor, sensor_1, \ - sensor_3, _flags, _ctrl_dis, _sensor_addr) \ +#define SENS(bridge, sensor, _flags, _ctrl_dis, _sensor_addr) \ { \ - .bridge_init = { bridge_1, bridge_3 }, \ - .bridge_init_size = { sizeof(bridge_1), sizeof(bridge_3) }, \ + .bridge_init = bridge, \ .sensor_init = sensor, \ .sensor_init_size = sizeof(sensor), \ - .sensor_bridge_init = { sensor_1, sensor_3,}, \ - .sensor_bridge_init_size = { sizeof(sensor_1), sizeof(sensor_3)}, \ .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \ } @@ -311,7 +310,6 @@ static const __u8 initHv7131d[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x28, 0x1e, 0x60, 0x8e, 0x42, - 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c }; static const __u8 hv7131d_sensor_init[][8] = { {0xa0, 0x11, 0x01, 0x04, 0x00, 0x00, 0x00, 0x17}, @@ -326,7 +324,6 @@ static const __u8 initHv7131r[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x28, 0x1e, 0x60, 0x8a, 0x20, - 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c }; static const __u8 hv7131r_sensor_init[][8] = { {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, @@ -339,7 +336,7 @@ static const __u8 initOv6650[] = { 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b, - 0x10, 0x1d, 0x10, 0x02, 0x02, 0x09, 0x07 + 0x10, }; static const __u8 ov6650_sensor_init[][8] = { /* Bright, contrast, etc are set through SCBB interface. @@ -378,18 +375,7 @@ static const __u8 initOv7630[] = { 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ - 0x68, COMP2, MCK_INIT1, /* r17 .. r19 */ - 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ -}; -static const __u8 initOv7630_3[] = { - 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ - 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ - 0x28, 0x1e, /* H & V sizes r15 .. r16 */ 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ - 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c, 0x00, /* r1a .. r20 */ - 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, /* r21 .. r28 */ - 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff /* r29 .. r30 */ }; static const __u8 ov7630_sensor_init[][8] = { {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, @@ -413,16 +399,11 @@ static const __u8 ov7630_sensor_init[][8] = { {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, }; -static const __u8 ov7630_sensor_init_3[][8] = { - {0xa0, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, -}; - static const __u8 initPas106[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x16, 0x12, 0x24, COMP1, MCK_INIT1, - 0x18, 0x10, 0x02, 0x02, 0x09, 0x07 }; /* compression 0x86 mckinit1 0x2b */ @@ -496,7 +477,6 @@ static const __u8 initPas202[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, 0x28, 0x1e, 0x20, 0x89, 0x20, - 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c }; /* "Known" PAS202BCB registers: @@ -537,7 +517,6 @@ static const __u8 initTas5110c[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, - 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; /* Same as above, except a different hstart */ static const __u8 initTas5110d[] = { @@ -545,7 +524,6 @@ static const __u8 initTas5110d[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, - 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; static const __u8 tas5110_sensor_init[][8] = { {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, @@ -558,7 +536,6 @@ static const __u8 initTas5130[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a, 0x28, 0x1e, 0x60, COMP, MCK_INIT, - 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; static const __u8 tas5130_sensor_init[][8] = { /* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, @@ -569,21 +546,17 @@ static const __u8 tas5130_sensor_init[][8] = { }; static struct sensor_data sensor_data[] = { -SENS(initHv7131d, NULL, hv7131d_sensor_init, NULL, NULL, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0), -SENS(initHv7131r, NULL, hv7131r_sensor_init, NULL, NULL, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0), -SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60), -SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3, - F_GAIN, 0, 0x21), -SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_GAIN|F_SIF, NO_FREQ, - 0), -SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN, - NO_FREQ, 0), -SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, - F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), -SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, - F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), -SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, - 0), +SENS(initHv7131d, hv7131d_sensor_init, F_GAIN, NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initHv7131r, hv7131r_sensor_init, 0, NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0), +SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60), +SENS(initOv7630, ov7630_sensor_init, F_GAIN, 0, 0x21), +SENS(initPas106, pas106_sensor_init, F_GAIN|F_SIF, NO_FREQ, 0), +SENS(initPas202, pas202_sensor_init, F_GAIN, NO_FREQ, 0), +SENS(initTas5110c, tas5110_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, + NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5110d, tas5110_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, + NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5130, tas5130_sensor_init, 0, NO_EXPO|NO_FREQ, 0), }; /* get one byte in gspca_dev->usb_buf */ @@ -796,7 +769,7 @@ static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 gain; - __u8 buf[2] = { 0, 0 }; + __u8 buf[3] = { 0, 0, 0 }; if (sensor_data[sd->sensor].flags & F_GAIN) { /* Use the sensor gain to do the actual gain */ @@ -804,13 +777,18 @@ static void setgain(struct gspca_dev *gspca_dev) return; } - gain = sd->gain >> 4; - - /* red and blue gain */ - buf[0] = gain << 4 | gain; - /* green gain */ - buf[1] = gain; - reg_w(gspca_dev, 0x10, buf, 2); + if (sd->bridge == BRIDGE_103) { + gain = sd->gain >> 1; + buf[0] = gain; /* Red */ + buf[1] = gain; /* Green */ + buf[2] = gain; /* Blue */ + reg_w(gspca_dev, 0x05, buf, 3); + } else { + gain = sd->gain >> 4; + buf[0] = gain << 4 | gain; /* Red and blue */ + buf[1] = gain; /* Green */ + reg_w(gspca_dev, 0x10, buf, 2); + } } static void setexposure(struct gspca_dev *gspca_dev) @@ -1127,53 +1105,91 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam = &gspca_dev->cam; - int mode, l; - const __u8 *sn9c10x; - __u8 reg12_19[8]; + int i, mode; + __u8 regs[0x31]; mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07; - sn9c10x = sensor_data[sd->sensor].bridge_init[sd->bridge]; - l = sensor_data[sd->sensor].bridge_init_size[sd->bridge]; - memcpy(reg12_19, &sn9c10x[0x12 - 1], 8); - reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); - /* Special cases where reg 17 and or 19 value depends on mode */ + /* Copy registers 0x01 - 0x19 from the template */ + memcpy(®s[0x01], sensor_data[sd->sensor].bridge_init, 0x19); + /* Set the mode */ + regs[0x18] |= mode << 4; + + /* Set bridge gain to 1.0 */ + if (sd->bridge == BRIDGE_103) { + regs[0x05] = 0x20; /* Red */ + regs[0x06] = 0x20; /* Green */ + regs[0x07] = 0x20; /* Blue */ + } else { + regs[0x10] = 0x00; /* Red and blue */ + regs[0x11] = 0x00; /* Green */ + } + + /* Setup pixel numbers and auto exposure window */ + if (sensor_data[sd->sensor].flags & F_SIF) { + regs[0x1a] = 0x14; /* HO_SIZE 640, makes no sense */ + regs[0x1b] = 0x0a; /* VO_SIZE 320, makes no sense */ + regs[0x1c] = 0x02; /* AE H-start 64 */ + regs[0x1d] = 0x02; /* AE V-start 64 */ + regs[0x1e] = 0x09; /* AE H-end 288 */ + regs[0x1f] = 0x07; /* AE V-end 224 */ + } else { + regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */ + regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */ + regs[0x1c] = 0x02; /* AE H-start 64 */ + regs[0x1d] = 0x03; /* AE V-start 96 */ + regs[0x1e] = 0x0f; /* AE H-end 480 */ + regs[0x1f] = 0x0c; /* AE V-end 384 */ + } + + /* Setup the gamma table (only used with the sn9c103 bridge) */ + for (i = 0; i < 16; i++) + regs[0x20 + i] = i * 16; + regs[0x20 + i] = 255; + + /* Special cases where some regs depend on mode or bridge */ switch (sd->sensor) { case SENSOR_TAS5130CXX: - /* probably not mode specific at all most likely the upper + /* FIXME / TESTME + probably not mode specific at all most likely the upper nibble of 0x19 is exposure (clock divider) just as with the tas5110, we need someone to test this. */ - reg12_19[7] = mode ? 0x23 : 0x43; + regs[0x19] = mode ? 0x23 : 0x43; break; + case SENSOR_OV7630: + /* FIXME / TESTME for some reason with the 101/102 bridge the + clock is set to 12 Mhz (reg1 == 0x04), rather then 24. + Also the hstart needs to go from 1 to 2 when using a 103, + which is likely related. This does not seem right. */ + if (sd->bridge == BRIDGE_103) { + regs[0x01] = 0x44; /* Select 24 Mhz clock */ + regs[0x12] = 0x02; /* Set hstart to 2 */ + } } /* Disable compression when the raw bayer format has been selected */ if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) - reg12_19[6] &= ~0x80; + regs[0x18] &= ~0x80; /* Vga mode emulation on SIF sensor? */ if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) { - reg12_19[0] += 16; /* 0x12: hstart adjust */ - reg12_19[1] += 24; /* 0x13: vstart adjust */ - reg12_19[3] = 320 / 16; /* 0x15: hsize */ - reg12_19[4] = 240 / 16; /* 0x16: vsize */ + regs[0x12] += 16; /* hstart adjust */ + regs[0x13] += 24; /* vstart adjust */ + regs[0x15] = 320 / 16; /* hsize */ + regs[0x16] = 240 / 16; /* vsize */ } /* reg 0x01 bit 2 video transfert on */ - reg_w(gspca_dev, 0x01, &sn9c10x[0x01 - 1], 1); + reg_w(gspca_dev, 0x01, ®s[0x01], 1); /* reg 0x17 SensorClk enable inv Clk 0x60 */ - reg_w(gspca_dev, 0x17, &sn9c10x[0x17 - 1], 1); + reg_w(gspca_dev, 0x17, ®s[0x17], 1); /* Set the registers from the template */ - reg_w(gspca_dev, 0x01, sn9c10x, l); + reg_w(gspca_dev, 0x01, ®s[0x01], + (sd->bridge == BRIDGE_103) ? 0x30 : 0x1f); /* Init the sensor */ i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init, sensor_data[sd->sensor].sensor_init_size); - if (sensor_data[sd->sensor].sensor_bridge_init[sd->bridge]) - i2c_w_vector(gspca_dev, - sensor_data[sd->sensor].sensor_bridge_init[sd->bridge], - sensor_data[sd->sensor].sensor_bridge_init_size[ - sd->bridge]); - /* Mode specific sensor setup */ + /* Mode / bridge specific sensor setup */ switch (sd->sensor) { case SENSOR_PAS202: { const __u8 i2cpclockdiv[] = @@ -1181,27 +1197,37 @@ static int sd_start(struct gspca_dev *gspca_dev) /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ if (mode) i2c_w(gspca_dev, i2cpclockdiv); + break; } + case SENSOR_OV7630: + /* FIXME / TESTME We should be able to handle this identical + for the 101/102 and the 103 case */ + if (sd->bridge == BRIDGE_103) { + const __u8 i2c[] = { 0xa0, 0x21, 0x13, + 0x80, 0x00, 0x00, 0x00, 0x10 }; + i2c_w(gspca_dev, i2c); + } + break; } /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ - reg_w(gspca_dev, 0x15, ®12_19[3], 2); + reg_w(gspca_dev, 0x15, ®s[0x15], 2); /* compression register */ - reg_w(gspca_dev, 0x18, ®12_19[6], 1); + reg_w(gspca_dev, 0x18, ®s[0x18], 1); /* H_start */ - reg_w(gspca_dev, 0x12, ®12_19[0], 1); + reg_w(gspca_dev, 0x12, ®s[0x12], 1); /* V_START */ - reg_w(gspca_dev, 0x13, ®12_19[1], 1); + reg_w(gspca_dev, 0x13, ®s[0x13], 1); /* reset 0x17 SensorClk enable inv Clk 0x60 */ /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ - reg_w(gspca_dev, 0x17, ®12_19[5], 1); + reg_w(gspca_dev, 0x17, ®s[0x17], 1); /*MCKSIZE ->3 */ /*fixme: not ov7630*/ - reg_w(gspca_dev, 0x19, ®12_19[7], 1); + reg_w(gspca_dev, 0x19, ®s[0x19], 1); /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ - reg_w(gspca_dev, 0x1c, &sn9c10x[0x1c - 1], 4); + reg_w(gspca_dev, 0x1c, ®s[0x1c], 4); /* Enable video transfert */ - reg_w(gspca_dev, 0x01, &sn9c10x[0], 1); + reg_w(gspca_dev, 0x01, ®s[0x01], 1); /* Compression */ - reg_w(gspca_dev, 0x18, ®12_19[6], 2); + reg_w(gspca_dev, 0x18, ®s[0x18], 2); msleep(20); sd->reg11 = -1; -- cgit v1.2.3 From f913c001cd6084db0e3486b832234d2fe4513ff6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 5 Jan 2011 16:01:16 -0300 Subject: [media] gspca_sonixb: Adjust autoexposure window for vga cams so that it is centered Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 43784027568f..e88097f3d7ed 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1027,7 +1027,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) desired_avg_lum = 5000; } else { deadzone = 1500; - desired_avg_lum = 18000; + desired_avg_lum = 13000; } if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) @@ -1135,7 +1135,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } else { regs[0x1a] = 0x1d; /* HO_SIZE 960, makes no sense */ regs[0x1b] = 0x10; /* VO_SIZE 512, makes no sense */ - regs[0x1c] = 0x02; /* AE H-start 64 */ + regs[0x1c] = 0x05; /* AE H-start 160 */ regs[0x1d] = 0x03; /* AE V-start 96 */ regs[0x1e] = 0x0f; /* AE H-end 480 */ regs[0x1f] = 0x0c; /* AE V-end 384 */ -- cgit v1.2.3 From 0d0d7ef71ec6ba6abb680478f7d0514584b8277f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2011 07:55:20 -0300 Subject: [media] gspca_sonixb: Fix TAS5110D sensor gain control Also fix the issue of the image being mirrored. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index e88097f3d7ed..5c8420e5c02e 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -525,10 +525,18 @@ static const __u8 initTas5110d[] = { 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, }; -static const __u8 tas5110_sensor_init[][8] = { +/* tas5110c is 3 wire, tas5110d is 2 wire (regular i2c) */ +static const __u8 tas5110c_sensor_init[][8] = { {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, - {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, +}; +/* Known TAS5110D registers + * reg02: gain, bit order reversed!! 0 == max gain, 255 == min gain + * reg03: bit3: vflip, bit4: ~hflip, bit7: ~gainboost (~ == inverted) + * Note: writing reg03 seems to only work when written together with 02 + */ +static const __u8 tas5110d_sensor_init[][8] = { + {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, /* reset */ }; static const __u8 initTas5130[] = { @@ -552,9 +560,9 @@ SENS(initOv6650, ov6650_sensor_init, F_GAIN|F_SIF, 0, 0x60), SENS(initOv7630, ov7630_sensor_init, F_GAIN, 0, 0x21), SENS(initPas106, pas106_sensor_init, F_GAIN|F_SIF, NO_FREQ, 0), SENS(initPas202, pas202_sensor_init, F_GAIN, NO_FREQ, 0), -SENS(initTas5110c, tas5110_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, +SENS(initTas5110c, tas5110c_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), -SENS(initTas5110d, tas5110_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, +SENS(initTas5110d, tas5110d_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), SENS(initTas5130, tas5130_sensor_init, 0, NO_EXPO|NO_FREQ, 0), }; @@ -705,8 +713,7 @@ static void setsensorgain(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_TAS5110C: - case SENSOR_TAS5110D: { + case SENSOR_TAS5110C: { __u8 i2c[] = {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; @@ -715,6 +722,23 @@ static void setsensorgain(struct gspca_dev *gspca_dev) goto err; break; } + case SENSOR_TAS5110D: { + __u8 i2c[] = { + 0xb0, 0x61, 0x02, 0x00, 0x10, 0x00, 0x00, 0x17 }; + gain = 255 - gain; + /* The bits in the register are the wrong way around!! */ + i2c[3] |= (gain & 0x80) >> 7; + i2c[3] |= (gain & 0x40) >> 5; + i2c[3] |= (gain & 0x20) >> 3; + i2c[3] |= (gain & 0x10) >> 1; + i2c[3] |= (gain & 0x08) << 1; + i2c[3] |= (gain & 0x04) << 3; + i2c[3] |= (gain & 0x02) << 5; + i2c[3] |= (gain & 0x01) << 7; + if (i2c_w(gspca_dev, i2c) < 0) + goto err; + break; + } case SENSOR_OV6650: gain >>= 1; -- cgit v1.2.3 From 4e17cd2eac2544267bdfab67655be468f80f50c6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2011 10:58:53 -0300 Subject: [media] gspca_sonixb: TAS5130C brightness control really is a gain control Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 5c8420e5c02e..36253417bde4 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -564,7 +564,8 @@ SENS(initTas5110c, tas5110c_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), SENS(initTas5110d, tas5110d_sensor_init, F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), -SENS(initTas5130, tas5130_sensor_init, 0, NO_EXPO|NO_FREQ, 0), +SENS(initTas5130, tas5130_sensor_init, F_GAIN, + NO_BRIGHTNESS|NO_EXPO|NO_FREQ, 0), }; /* get one byte in gspca_dev->usb_buf */ @@ -636,7 +637,6 @@ static void i2c_w_vector(struct gspca_dev *gspca_dev, static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 value; switch (sd->sensor) { case SENSOR_OV6650: @@ -678,17 +678,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_TAS5130CXX: { - __u8 i2c[] = - {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; - - value = 0xff - sd->brightness; - i2c[4] = value; - PDEBUG(D_CONF, "brightness %d : %d", value, i2c[4]); - if (i2c_w(gspca_dev, i2c) < 0) - goto err; - break; - } } return; err: @@ -713,7 +702,8 @@ static void setsensorgain(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_TAS5110C: { + case SENSOR_TAS5110C: + case SENSOR_TAS5130CXX: { __u8 i2c[] = {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; -- cgit v1.2.3 From 69ffd2545766742f7d247770c6ee30a6ed6058ef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2011 11:56:30 -0300 Subject: [media] gspca_sonixb: Add usb ids for known sn9c103 cameras Now that our bridge code is unified for sn9c101/102 and sn9c103 models, the sn9c103 models should simply work, given that the only difference in the sn9c103 is audio support and a gamma correction table. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 12 ++++++++---- drivers/media/video/sn9c102/sn9c102_devtable.h | 8 +++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 36253417bde4..c20131147604 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1572,8 +1572,8 @@ static const struct usb_device_id device_table[] __devinitconst = { {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, #endif @@ -1584,11 +1584,15 @@ static const struct usb_device_id device_table[] __devinitconst = { {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, - /* {USB_DEVICE(0x0c45, 0x602b), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */ + /* {USB_DEVICE(0x0c45, 0x6030), SB(MI03XX, 102)}, */ /* MI0343 MI0360 MI0330 */ + /* {USB_DEVICE(0x0c45, 0x6082), SB(MI03XX, 103)}, */ /* MI0343 MI0360 */ + {USB_DEVICE(0x0c45, 0x6083), SB(HV7131D, 103)}, + {USB_DEVICE(0x0c45, 0x608c), SB(HV7131R, 103)}, + /* {USB_DEVICE(0x0c45, 0x608e), SB(CISVF10, 103)}, */ {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x60a8), SB(PAS106, 103)}, + {USB_DEVICE(0x0c45, 0x60aa), SB(TAS5130CXX, 103)}, {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)}, -#endif {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)}, {} }; diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 74bc719b8e89..0e0fcc017fb8 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -47,8 +47,8 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ -#endif { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, #if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE @@ -66,15 +66,14 @@ static const struct usb_device_id sn9c102_id_table[] = { /* SN9C103 */ /* { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, non existent ? */ { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, /* not in sonixb */ +#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE /* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ /* { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, non existent ? */ { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, -#endif /* { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, non existent ? */ @@ -84,9 +83,7 @@ static const struct usb_device_id sn9c102_id_table[] = { /* { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, non existent ? */ { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, -#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, -#endif /* { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, non existent ? */ @@ -94,6 +91,7 @@ static const struct usb_device_id sn9c102_id_table[] = { /* { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, non existent ? */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, non existent ? */ +#endif /* SN9C105 */ #if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, -- cgit v1.2.3 From 4944e27d85ed8e5ccd591687546d56d111c5cc98 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2011 13:05:29 -0300 Subject: [media] gspca_sonixj: Enable more usb ids when sn9c102 gets compiled too Both we and the windows driver make no sensor specific differences (with some exceptions) for different sonixj bridge types. Thus if a sn9c105 bridge has been successfully tested with a sensor, the same sensor can be successfully used with a sn9c120 bridge too. Using this knowledge we can move over most usb-ids too the sonixj driver when both are compiled. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 6 +----- drivers/media/video/sn9c102/sn9c102_devtable.h | 6 ++---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 2d0bb17a30a2..85716f864cfb 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2908,10 +2908,8 @@ static const struct sd_desc sd_desc = { | (SENSOR_ ## sensor << 8) \ | (flags) static const __devinitdata struct usb_device_id device_table[] = { -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)}, {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, -#endif {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, PDN_INV)}, {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, PDN_INV)}, {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, @@ -2936,8 +2934,8 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */ /* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */ {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, #endif {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ @@ -2962,9 +2960,7 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */ {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)}, {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)}, -#endif {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/ diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 0e0fcc017fb8..97e123672d65 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -107,21 +107,19 @@ static const struct usb_device_id sn9c102_id_table[] = { /* { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, ICM105C */ /* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ +#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ /* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ /* { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, S5K53BEB */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, #endif -- cgit v1.2.3 From e530a5e3cfe5f2dca35552d2d968f0a3fc115968 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2011 15:23:55 -0300 Subject: [media] gspca_sonixj: Probe sensor type independent of bridge type Looking at the windows inf file, for usb ids with a sensor type where probing is needed to determine the type (for example ov7630 or soi768), this is needed for all bridge variants with a usb id indicating this sensor type. So do the probing to determine the actual sensor type for types where the usb-id info is not 100% deterministic, independent of the bridge type. If you look through the list of currently active usb ids in sonixj, this effectively only changes the code path for 0c45:60fe (sn9c105 + ov7630) and 0c45:612e (sn9c110 + ov7630), which according to the inf file can have a soi768 instead of a ov7630 just like the sn9c120 + ov7630 models where we already probe for a soi7630. The main reason for this code change is to keep the code paths as bridge variant independent as possible, so that we don't need a lot of special per bridge cases, as we enable more usb-ids in the future. This change makes the 0c45:60fe code path identical to the successfully tested 0c45:613e, so also make sonixj the default driver for 0c45:60fe. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 58 +++++++++++++------------- drivers/media/video/sn9c102/sn9c102_devtable.h | 2 - 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 85716f864cfb..efe3fc7b6b74 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1822,44 +1822,46 @@ static int sd_init(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Sonix chip id: %02x", regF1); switch (sd->bridge) { case BRIDGE_SN9C102P: + case BRIDGE_SN9C105: if (regF1 != 0x11) return -ENODEV; + break; + default: +/* case BRIDGE_SN9C110: */ +/* case BRIDGE_SN9C120: */ + if (regF1 != 0x12) + return -ENODEV; + } + + switch (sd->sensor) { + case SENSOR_MI0360: + mi0360_probe(gspca_dev); + break; + case SENSOR_OV7630: + ov7630_probe(gspca_dev); + break; + case SENSOR_OV7648: + ov7648_probe(gspca_dev); + break; + case SENSOR_PO2030N: + po2030n_probe(gspca_dev); + break; + } + + switch (sd->bridge) { + case BRIDGE_SN9C102P: reg_w1(gspca_dev, 0x02, regGpio[1]); break; case BRIDGE_SN9C105: - if (regF1 != 0x11) - return -ENODEV; - if (sd->sensor == SENSOR_MI0360) - mi0360_probe(gspca_dev); reg_w(gspca_dev, 0x01, regGpio, 2); break; + case BRIDGE_SN9C110: + reg_w1(gspca_dev, 0x02, 0x62); + break; case BRIDGE_SN9C120: - if (regF1 != 0x12) - return -ENODEV; - switch (sd->sensor) { - case SENSOR_MI0360: - mi0360_probe(gspca_dev); - break; - case SENSOR_OV7630: - ov7630_probe(gspca_dev); - break; - case SENSOR_OV7648: - ov7648_probe(gspca_dev); - break; - case SENSOR_PO2030N: - po2030n_probe(gspca_dev); - break; - } regGpio[1] = 0x70; /* no audio */ reg_w(gspca_dev, 0x01, regGpio, 2); break; - default: -/* case BRIDGE_SN9C110: */ -/* case BRIDGE_SN9C325: */ - if (regF1 != 0x12) - return -ENODEV; - reg_w1(gspca_dev, 0x02, 0x62); - break; } if (sd->sensor == SENSOR_OM6802) @@ -2935,9 +2937,7 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */ {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, -#endif {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/ /* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 97e123672d65..b3d2cc729657 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -108,10 +108,8 @@ static const struct usb_device_id sn9c102_id_table[] = { /* { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, OV7648 */ { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, /* SN9C120 */ -#if !defined CONFIG_USB_GSPCA_SONIXJ && !defined CONFIG_USB_GSPCA_SONIXJ_MODULE { SN9C102_USB_DEVICE(0x0458, 0x7025, BRIDGE_SN9C120), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, po2030 */ /* { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, om6801 */ -- cgit v1.2.3 From e48d38f7f100f37edc873df1b3a1d15ee3575874 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 6 Jan 2011 16:21:57 -0300 Subject: [media] gspca_sonixj: Add one more commented out usb-id While going through windows inf file I found more usb-id, add a comment with this id for future reference. Signed-off-by: Hans de Goede Acked-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index efe3fc7b6b74..db35391d00e6 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2968,6 +2968,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/ +/* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); -- cgit v1.2.3 From 4c775902252b06673cf26a33834842f1fec3fe3e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 7 Jan 2011 07:29:24 -0300 Subject: [media] gspca_sonixb: Fix mirrored image with ov7630 Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index c20131147604..e45d1cfb4461 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -381,7 +381,7 @@ static const __u8 ov7630_sensor_init[][8] = { {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, /* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ - {0xd0, 0x21, 0x12, 0x1c, 0x00, 0x80, 0x34, 0x10}, /* jfm */ + {0xd0, 0x21, 0x12, 0x5c, 0x00, 0x80, 0x34, 0x10}, /* jfm */ {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, -- cgit v1.2.3 From a1198ccf9c52922e66a3372b0045ebe335a127dd Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Jan 2011 09:53:32 -0300 Subject: [media] v4l2-ioctl: fix incorrect error code if VIDIOC_DBG_G/S_REGISTER are unsupported The ioctls VIDIOC_DBG_S_REGISTER and VIDIOC_DBG_G_REGISTER should return -EINVAL if the driver didn't implement them. Currently they return -EPERM if called as non-root user. However, this check should only be done if the driver actually implemented these ioctls. Otherwise, just return -EINVAL as we do with all unimplemented ioctls. This bug make the v4l2-compliance test suite fail. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ioctl.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 7e47f15f350d..f51327ef6757 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -1659,20 +1659,24 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dbg_register *p = arg; - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else if (ops->vidioc_g_register) - ret = ops->vidioc_g_register(file, fh, p); + if (ops->vidioc_g_register) { + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_g_register(file, fh, p); + } break; } case VIDIOC_DBG_S_REGISTER: { struct v4l2_dbg_register *p = arg; - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else if (ops->vidioc_s_register) - ret = ops->vidioc_s_register(file, fh, p); + if (ops->vidioc_s_register) { + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_s_register(file, fh, p); + } break; } #endif -- cgit v1.2.3 From 131ddd1a3072aebca666767151acaa7574beb583 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 8 Jan 2011 14:12:30 -0300 Subject: [media] radio-si470x: de-emphasis should be set if requested by module parameter instead of always setting de-emphasis. Reported-by: Tobias Lorenz Signed-off-by: Joonyoung Shim Acked-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index ac76dfe5b3fa..35488baf29bd 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -357,7 +357,8 @@ int si470x_start(struct si470x_device *radio) goto done; /* sysconfig 1 */ - radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; + radio->registers[SYSCONFIG1] = + (de << 11) & SYSCONFIG1_DE; /* DE*/ retval = si470x_set_register(radio, SYSCONFIG1); if (retval < 0) goto done; -- cgit v1.2.3 From 186a21cb77ffe23397aaea302ab32b510b3e2df4 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Sat, 8 Jan 2011 16:13:04 -0300 Subject: [media] radio-si470x: Always report support for RDS The si470x i2c and usb driver support the RDS, so this ifdef statement doesn't need more. [mchehab@redhat.com: Fix a conflict on it] Signed-off-by: Joonyoung Shim Acked-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 35488baf29bd..60c176fe328e 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -688,12 +688,8 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, /* driver constants */ strcpy(tuner->name, "FM"); tuner->type = V4L2_TUNER_RADIO; -#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; -#else - tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; -#endif /* range limits */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { @@ -719,12 +715,10 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; -#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE) /* If there is a reliable method of detecting an RDS channel, then this code should check for that before setting this RDS subchannel. */ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS; -#endif /* mono/stereo selector */ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0) -- cgit v1.2.3 From 312d63e4b0ca8456c82d01a6446795f7d029ecde Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Jan 2011 12:16:34 -0300 Subject: [media] rc-dib0700-nec: Fix keytable for Pixelview SBTVD dib0700 now outputs NEC extended keycodes. Fix the keytable to reflect that. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/rc-dib0700-nec.c | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/media/rc/keymaps/rc-dib0700-nec.c b/drivers/media/rc/keymaps/rc-dib0700-nec.c index c59851b203da..7a5f5300caf9 100644 --- a/drivers/media/rc/keymaps/rc-dib0700-nec.c +++ b/drivers/media/rc/keymaps/rc-dib0700-nec.c @@ -19,35 +19,35 @@ static struct rc_map_table dib0700_nec_table[] = { /* Key codes for the Pixelview SBTVD remote */ - { 0x8613, KEY_MUTE }, - { 0x8612, KEY_POWER }, - { 0x8601, KEY_1 }, - { 0x8602, KEY_2 }, - { 0x8603, KEY_3 }, - { 0x8604, KEY_4 }, - { 0x8605, KEY_5 }, - { 0x8606, KEY_6 }, - { 0x8607, KEY_7 }, - { 0x8608, KEY_8 }, - { 0x8609, KEY_9 }, - { 0x8600, KEY_0 }, - { 0x860d, KEY_CHANNELUP }, - { 0x8619, KEY_CHANNELDOWN }, - { 0x8610, KEY_VOLUMEUP }, - { 0x860c, KEY_VOLUMEDOWN }, + { 0x866b13, KEY_MUTE }, + { 0x866b12, KEY_POWER }, + { 0x866b01, KEY_1 }, + { 0x866b02, KEY_2 }, + { 0x866b03, KEY_3 }, + { 0x866b04, KEY_4 }, + { 0x866b05, KEY_5 }, + { 0x866b06, KEY_6 }, + { 0x866b07, KEY_7 }, + { 0x866b08, KEY_8 }, + { 0x866b09, KEY_9 }, + { 0x866b00, KEY_0 }, + { 0x866b0d, KEY_CHANNELUP }, + { 0x866b19, KEY_CHANNELDOWN }, + { 0x866b10, KEY_VOLUMEUP }, + { 0x866b0c, KEY_VOLUMEDOWN }, - { 0x860a, KEY_CAMERA }, - { 0x860b, KEY_ZOOM }, - { 0x861b, KEY_BACKSPACE }, - { 0x8615, KEY_ENTER }, + { 0x866b0a, KEY_CAMERA }, + { 0x866b0b, KEY_ZOOM }, + { 0x866b1b, KEY_BACKSPACE }, + { 0x866b15, KEY_ENTER }, - { 0x861d, KEY_UP }, - { 0x861e, KEY_DOWN }, - { 0x860e, KEY_LEFT }, - { 0x860f, KEY_RIGHT }, + { 0x866b1d, KEY_UP }, + { 0x866b1e, KEY_DOWN }, + { 0x866b0e, KEY_LEFT }, + { 0x866b0f, KEY_RIGHT }, - { 0x8618, KEY_RECORD }, - { 0x861a, KEY_STOP }, + { 0x866b18, KEY_RECORD }, + { 0x866b1a, KEY_STOP }, /* Key codes for the EvolutePC TVWay+ remote */ { 0x7a00, KEY_MENU }, -- cgit v1.2.3 From 59aa346009c06c6697e9db008e67e4ff8c205091 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Jan 2011 12:17:52 -0300 Subject: [media] dib0700: Fix IR keycode handling Fixes Fedora 14 bug: https://bugzilla.redhat.com/show_bug.cgi?id=667157 There are a few bugs at the code that generates the scancode at dib0700: - RC keycode is wrong (it outputs a 24 bits keycode); - NEC extended outputs a keycode that have endiannes issues; - keycode tables for NEC extended remotes need to be updated. The last issue need to be done as we get reports, as we don't have the complete NEC-extended keycodes at the dibcom table. This patch fixes the first two issues. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 8ca48f76dfa9..98ffb40728e3 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -514,8 +514,8 @@ struct dib0700_rc_response { union { u16 system16; struct { - u8 system; u8 not_system; + u8 system; }; }; u8 data; @@ -575,7 +575,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) if ((poll_reply->system ^ poll_reply->not_system) != 0xff) { deb_data("NEC extended protocol\n"); /* NEC extended code - 24 bits */ - keycode = poll_reply->system16 << 8 | poll_reply->data; + keycode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data; } else { deb_data("NEC normal protocol\n"); /* normal NEC code - 16 bits */ @@ -587,7 +587,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) deb_data("RC5 protocol\n"); /* RC5 Protocol */ toggle = poll_reply->report_id; - keycode = poll_reply->system16 << 8 | poll_reply->data; + keycode = poll_reply->system << 8 | poll_reply->data; break; } -- cgit v1.2.3 From e6bcb2f324cf28469d2713e86beace07f25596cf Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Jan 2011 13:50:01 -0300 Subject: [media] ir-kbd-i2c: Make IR debug messages more useful Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ir-kbd-i2c.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index c87b6bc45555..b173e409cd28 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -244,15 +244,17 @@ static void ir_key_poll(struct IR_i2c *ir) static u32 ir_key, ir_raw; int rc; - dprintk(2,"ir_poll_key\n"); + dprintk(3, "%s\n", __func__); rc = ir->get_key(ir, &ir_key, &ir_raw); if (rc < 0) { dprintk(2,"error\n"); return; } - if (rc) + if (rc) { + dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); rc_keydown(ir->rc, ir_key, 0); + } } static void ir_work(struct work_struct *work) -- cgit v1.2.3 From 5a85025f7dabc5b039335a7d1fb5f9002efa9488 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 12 Jan 2011 14:22:42 -0300 Subject: [media] em28xx: Fix IR support for WinTV USB2 Due to a lack of a break inside the switch, it were getting the wrong keytable and get_key function. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 099d5df8c572..ba03a448b41d 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2437,6 +2437,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.ir_codes = RC_MAP_RC5_HAUPPAUGE_NEW; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; -- cgit v1.2.3 From 567aba0b7997dad5fe3fb4aeb174ee9018df8c5b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Jan 2011 11:58:36 -0300 Subject: [media] tda8290: Make all read operations atomic Read operations should be preceeded by a write operation. However, nothing prevents that an I2C operation could happen between the two transactions. To avoid that problem, use an unique I2C transfer for both parts of the I2C transaction. Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda8290.c | 119 +++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index c9062ceddc71..5f889c1b91d0 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -95,8 +95,7 @@ static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close) msleep(20); } else { msg = disable; - tuner_i2c_xfer_send(&priv->i2c_props, msg, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &msg[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, msg, 1, &msg[1], 1); buf[2] = msg[1]; buf[2] &= ~0x04; @@ -239,13 +238,15 @@ static void tda8290_set_params(struct dvb_frontend *fe, fe->ops.tuner_ops.set_analog_params(fe, params); for (i = 0; i < 3; i++) { - tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, &pll_stat, 1); if (pll_stat & 0x80) { - tuner_i2c_xfer_send(&priv->i2c_props, &addr_adc_sat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &adc_sat, 1); - tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_adc_sat, 1, + &adc_sat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_agc_stat, 1, + &agc_stat, 1); tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); break; } else { @@ -259,20 +260,22 @@ static void tda8290_set_params(struct dvb_frontend *fe, agc_stat, adc_sat, pll_stat & 0x80); tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2); msleep(100); - tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1); - tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_agc_stat, 1, &agc_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, &pll_stat, 1); if ((agc_stat > 115) || !(pll_stat & 0x80)) { tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n", agc_stat, pll_stat & 0x80); if (priv->cfg.agcf) priv->cfg.agcf(fe); msleep(100); - tuner_i2c_xfer_send(&priv->i2c_props, &addr_agc_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &agc_stat, 1); - tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_agc_stat, 1, + &agc_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, + &pll_stat, 1); if((agc_stat > 115) || !(pll_stat & 0x80)) { tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat); tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2); @@ -284,10 +287,12 @@ static void tda8290_set_params(struct dvb_frontend *fe, /* l/ l' deadlock? */ if(priv->tda8290_easy_mode & 0x60) { - tuner_i2c_xfer_send(&priv->i2c_props, &addr_adc_sat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &adc_sat, 1); - tuner_i2c_xfer_send(&priv->i2c_props, &addr_pll_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &pll_stat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_adc_sat, 1, + &adc_sat, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &addr_pll_stat, 1, + &pll_stat, 1); if ((adc_sat > 20) || !(pll_stat & 0x80)) { tuner_dbg("trying to resolve SECAM L deadlock\n"); tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2); @@ -307,8 +312,7 @@ static void tda8295_power(struct dvb_frontend *fe, int enable) struct tda8290_priv *priv = fe->analog_demod_priv; unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */ - tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); if (enable) buf[1] = 0x01; @@ -323,8 +327,7 @@ static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable) struct tda8290_priv *priv = fe->analog_demod_priv; unsigned char buf[] = { 0x01, 0x00 }; - tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); if (enable) buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */ @@ -353,8 +356,7 @@ static void tda8295_agc1_out(struct dvb_frontend *fe, int enable) struct tda8290_priv *priv = fe->analog_demod_priv; unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */ - tuner_i2c_xfer_send(&priv->i2c_props, &buf[0], 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &buf[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); if (enable) buf[1] &= ~0x40; @@ -370,10 +372,10 @@ static void tda8295_agc2_out(struct dvb_frontend *fe, int enable) unsigned char set_gpio_cf[] = { 0x44, 0x00 }; unsigned char set_gpio_val[] = { 0x46, 0x00 }; - tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_cf[0], 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_cf[1], 1); - tuner_i2c_xfer_send(&priv->i2c_props, &set_gpio_val[0], 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &set_gpio_val[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &set_gpio_cf[0], 1, &set_gpio_cf[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &set_gpio_val[0], 1, &set_gpio_val[1], 1); set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */ @@ -392,8 +394,7 @@ static int tda8295_has_signal(struct dvb_frontend *fe) unsigned char hvpll_stat = 0x26; unsigned char ret; - tuner_i2c_xfer_send(&priv->i2c_props, &hvpll_stat, 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &ret, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, &hvpll_stat, 1, &ret, 1); return (ret & 0x01) ? 65535 : 0; } @@ -413,8 +414,8 @@ static void tda8295_set_params(struct dvb_frontend *fe, tda8295_power(fe, 1); tda8295_agc1_out(fe, 1); - tuner_i2c_xfer_send(&priv->i2c_props, &blanking_mode[0], 1); - tuner_i2c_xfer_recv(&priv->i2c_props, &blanking_mode[1], 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + &blanking_mode[0], 1, &blanking_mode[1], 1); tda8295_set_video_std(fe); @@ -447,8 +448,8 @@ static int tda8290_has_signal(struct dvb_frontend *fe) unsigned char i2c_get_afc[1] = { 0x1B }; unsigned char afc = 0; - tuner_i2c_xfer_send(&priv->i2c_props, i2c_get_afc, ARRAY_SIZE(i2c_get_afc)); - tuner_i2c_xfer_recv(&priv->i2c_props, &afc, 1); + tuner_i2c_xfer_send_recv(&priv->i2c_props, + i2c_get_afc, ARRAY_SIZE(i2c_get_afc), &afc, 1); return (afc & 0x80)? 65535:0; } @@ -654,20 +655,26 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) static int tda8290_probe(struct tuner_i2c_props *i2c_props) { #define TDA8290_ID 0x89 - unsigned char tda8290_id[] = { 0x1f, 0x00 }; + u8 reg = 0x1f, id; + struct i2c_msg msg_read[] = { + { .addr = 0x4b, .flags = 0, .len = 1, .buf = ® }, + { .addr = 0x4b, .flags = I2C_M_RD, .len = 1, .buf = &id }, + }; /* detect tda8290 */ - tuner_i2c_xfer_send(i2c_props, &tda8290_id[0], 1); - tuner_i2c_xfer_recv(i2c_props, &tda8290_id[1], 1); + if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { + printk(KERN_WARNING "%s: tda8290 couldn't read register 0x%02x\n", + __func__, reg); + return -ENODEV; + } - if (tda8290_id[1] == TDA8290_ID) { + if (id == TDA8290_ID) { if (debug) printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n", __func__, i2c_adapter_id(i2c_props->adap), i2c_props->addr); return 0; } - return -ENODEV; } @@ -675,16 +682,23 @@ static int tda8295_probe(struct tuner_i2c_props *i2c_props) { #define TDA8295_ID 0x8a #define TDA8295C2_ID 0x8b - unsigned char tda8295_id[] = { 0x2f, 0x00 }; + u8 reg = 0x2f, id; + struct i2c_msg msg_read[] = { + { .addr = 0x4b, .flags = 0, .len = 1, .buf = ® }, + { .addr = 0x4b, .flags = I2C_M_RD, .len = 1, .buf = &id }, + }; - /* detect tda8295 */ - tuner_i2c_xfer_send(i2c_props, &tda8295_id[0], 1); - tuner_i2c_xfer_recv(i2c_props, &tda8295_id[1], 1); + /* detect tda8290 */ + if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { + printk(KERN_WARNING "%s: tda8290 couldn't read register 0x%02x\n", + __func__, reg); + return -ENODEV; + } - if ((tda8295_id[1] & 0xfe) == TDA8295_ID) { + if ((id & 0xfe) == TDA8295_ID) { if (debug) printk(KERN_DEBUG "%s: %s detected @ %d-%04x\n", - __func__, (tda8295_id[1] == TDA8295_ID) ? + __func__, (id == TDA8295_ID) ? "tda8295c1" : "tda8295c2", i2c_adapter_id(i2c_props->adap), i2c_props->addr); @@ -809,8 +823,8 @@ int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) int i; /* rule out tda9887, which would return the same byte repeatedly */ - tuner_i2c_xfer_send(&i2c_props, soft_reset, 1); - tuner_i2c_xfer_recv(&i2c_props, buf, PROBE_BUFFER_SIZE); + tuner_i2c_xfer_send_recv(&i2c_props, + soft_reset, 1, buf, PROBE_BUFFER_SIZE); for (i = 1; i < PROBE_BUFFER_SIZE; i++) { if (buf[i] != buf[0]) break; @@ -827,13 +841,12 @@ int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) /* fall back to old probing method */ tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2); tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); - tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1); - tuner_i2c_xfer_recv(&i2c_props, &data, 1); + tuner_i2c_xfer_send_recv(&i2c_props, &addr_dto_lsb, 1, &data, 1); if (data == 0) { tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2); tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); - tuner_i2c_xfer_send(&i2c_props, &addr_dto_lsb, 1); - tuner_i2c_xfer_recv(&i2c_props, &data, 1); + tuner_i2c_xfer_send_recv(&i2c_props, + &addr_dto_lsb, 1, &data, 1); if (data == 0x7b) { return 0; } -- cgit v1.2.3 From 47ab285a960ac456506297c93322ab13c3522f5a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Jan 2011 12:02:00 -0300 Subject: [media] tda8290: Fix a bug if no tuner is detected If tda8290 is detected, but no tuner is found, the driver will do bad things: tuner 2-0060: chip found @ 0xc0 (saa7133[0]) tda829x 2-0060: could not clearly identify tuner address, defaulting to 60 tda829x 2-0060: tuner access failed! BUG: unable to handle kernel NULL pointer dereference at 0000000000000020 IP: [] set_audio+0x47/0x170 [tda8290] PGD 1187b0067 PUD 11771e067 PMD 0 Oops: 0002 [#1] SMP last sysfs file: /sys/module/i2c_core/initstate CPU 0 Modules linked in: tda8290(U) tea5767(U) tuner(U) ir_lirc_codec(U) lirc_dev(U) ir_sony_decoder(U) ir_jvc_decoder(U) ir_rc6_decoder(U) ir_rc5_decoder(U) saa7134(+)(U) v4l2_common(U) ir_nec_decoder(U) videodev(U) v4l2_compat_ioctl32(U) rc_core(U) videobuf_dma_sg(U) videobuf_core(U) tveeprom(U) ebtable_nat ebtables xt_CHECKSUM iptable_mangle ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ipt_REJECT bridge stp llc autofs4 sunrpc cpufreq_ondemand acpi_cpufreq freq_table xt_physdev iptable_filter ip_tables ip6t_REJECT ip6table_filter ip6_tables ipv6 dm_mirror dm_region_hash dm_log parport kvm_intel kvm uinput floppy tpm_infineon wmi sg serio_raw iTCO_wdt iTCO_vendor_support tg3 snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc i7core_edac edac_core nouveau Modules linked in: tda8290(U) tea5767(U) tuner(U) ir_lirc_codec(U) lirc_dev(U) ir_sony_decoder(U) ir_jvc_decoder(U) ir_rc6_decoder(U) ir_rc5_decoder(U) saa7134(+)(U) v4l2_common(U) ir_nec_decoder(U) videodev(U) v4l2_compat_ioctl32(U) rc_core(U) videobuf_dma_sg(U) videobuf_core(U) tveeprom(U) ebtable_nat ebtables xt_CHECKSUM iptable_mangle ipt_MASQUERADE iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack ipt_REJECT bridge stp llc autofs4 sunrpc cpufreq_ondemand acpi_cpufreq freq_table xt_physdev iptable_filter ip_tables ip6t_REJECT ip6table_filter ip6_tables ipv6 dm_mirror dm_region_hash dm_log parport kvm_intel kvm uinput floppy tpm_infineon wmi sg serio_raw iTCO_wdt iTCO_vendor_support tg3 snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc i7core_edac edac_core nouveau ttm drm_kms_helper drm i2c_algo_bit video output i2c_core ext3 jbd mbcache firewire_ohci firewire_core crc_itu_t sr_mod cdrom sd_mod crc_t10dif ahci dm_mod [last unloaded: microcode] Pid: 9497, comm: modprobe Not tainted 2.6.32-72.el6.x86_64 #1 HP Z400 Workstation RIP: 0010:[] [] set_audio+0x47/0x170 [tda8290] RSP: 0018:ffff88010ba01b28 EFLAGS: 00010206 RAX: 00000000000000ff RBX: ffff880119522800 RCX: 0000000000000002 RDX: 0000000000003be0 RSI: ffff88010ba01bb8 RDI: 0000000000000000 RBP: ffff88010ba01b28 R08: 0000000000000002 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000 R13: ffff88010ba01bb8 R14: 0000000000001900 R15: 0000000000001900 FS: 00007f4b96b3d700(0000) GS:ffff880028200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 0000000000000020 CR3: 000000011866c000 CR4: 00000000000026f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process modprobe (pid: 9497, threadinfo ffff88010ba00000, task ffff880100708a70) Stack: ffff88010ba01b98 ffffffffa048c95b ffff88010ba01b78 0000000000000060 <0> 0000000000000000 0000000e00000000 000000000000001d ffffffffa03ec838 <0> ffff88010abac240 ffff880119522800 ffff880119522800 ffff880119522bc0 Call Trace: [] tda8295_set_params+0x3b/0x210 [tda8290] [] ? v4l2_i2c_new_subdev_cfg+0x88/0xc0 [v4l2_common] [] set_freq+0x128/0x2f0 [tuner] [] tuner_s_std+0xc4/0x740 [tuner] [] saa7134_set_tvnorm_hw+0x2d6/0x3d0 [saa7134] [] set_tvnorm+0xd5/0x100 [saa7134] [] saa7134_video_init2+0x1d/0x50 [saa7134] [] saa7134_initdev+0x6e1/0xb1d [saa7134] [] ? kobject_get+0x1a/0x30 [] local_pci_probe+0x17/0x20 [] pci_device_probe+0x101/0x120 [] ? driver_sysfs_add+0x62/0x90 [] driver_probe_device+0xa0/0x2a0 [] __driver_attach+0xab/0xb0 [] ? __driver_attach+0x0/0xb0 [] bus_for_each_dev+0x64/0x90 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x200/0x300 [] driver_register+0x76/0x140 [] ? printk+0x41/0x46 [] __pci_register_driver+0x56/0xd0 [] ? saa7134_init+0x0/0x4f [saa7134] [] saa7134_init+0x4d/0x4f [saa7134] [] do_one_initcall+0x3c/0x1d0 [] sys_init_module+0xdf/0x250 [] system_call_fastpath+0x16/0x1b Code: 20 01 49 c7 c0 c9 ec 48 a0 83 7e 04 01 74 2d 8b 0d 3f 2f 00 00 85 c9 0f 85 d7 00 00 00 c9 c3 0f 1f 44 00 00 a9 03 00 01 00 74 61 47 20 02 83 7e 04 01 49 c7 c0 cc ec 48 a0 75 d3 0f b6 47 22 RIP [] set_audio+0x47/0x170 [tda8290] RSP CR2: 0000000000000020 This happens because some I2C callbacks actually depend on having the driver entirely initialized. To avoid this OOPS, just clean the I2C callbacks, as if no device were detected. Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda8290.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 5f889c1b91d0..11ea4e0f9c04 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -755,8 +755,11 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, } if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) && - (tda829x_find_tuner(fe) < 0)) + (tda829x_find_tuner(fe) < 0)) { + memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops)); + goto fail; + } switch (priv->ver) { case TDA8290: -- cgit v1.2.3 From 9d700a0696ae241380e8ca833bb5a358620d33f6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Jan 2011 14:01:39 -0300 Subject: [media] tda8290: Turn tda829x on before touching at the I2C gate On Kworld SBTVD, tda8295-c1 starts in power off mode. It needs to be powered, otherwise, the I2C gate control command won't work. Cc: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda8290.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 11ea4e0f9c04..419d064a0188 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -754,11 +754,10 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, sizeof(struct analog_demod_ops)); } - if ((!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) && - (tda829x_find_tuner(fe) < 0)) { - memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops)); - - goto fail; + if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) { + tda8295_power(fe, 1); + if (tda829x_find_tuner(fe) < 0) + goto fail; } switch (priv->ver) { @@ -803,6 +802,8 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, return fe; fail: + memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops)); + tda829x_release(fe); return NULL; } -- cgit v1.2.3 From 7570800c9de39c718f84ec4ea820a788556cde4b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Jan 2011 08:50:00 -0300 Subject: [media] mb86a20s: Fix i2c read/write error messages A script replaced err var to rc. Howerver, this script gambled "error" string, changing it to "rcor". Revert that bad change. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/mb86a20s.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/mb86a20s.c b/drivers/media/dvb/frontends/mb86a20s.c index d3ad3e75a35a..e06507d2c3b2 100644 --- a/drivers/media/dvb/frontends/mb86a20s.c +++ b/drivers/media/dvb/frontends/mb86a20s.c @@ -318,7 +318,7 @@ static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, &msg, 1); if (rc != 1) { - printk("%s: writereg rcor(rc == %i, reg == 0x%02x," + printk("%s: writereg error (rc == %i, reg == 0x%02x," " data == 0x%02x)\n", __func__, rc, reg, data); return rc; } @@ -353,7 +353,7 @@ static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, rc = i2c_transfer(state->i2c, msg, 2); if (rc != 2) { - rc("%s: reg=0x%x (rcor=%d)\n", __func__, reg, rc); + rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); return rc; } -- cgit v1.2.3 From c736a5f28e81299b05ad14e892bdfb414daa9f5f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Jan 2011 11:10:05 -0300 Subject: [media] mb86a20s: Be sure that device is initialized before starting DVB Due to a hard to track bug between tda829x/tda18271/saa7134, tda829x wants to go to analog mode during DVB initialization, causing some I2C errors. The analog failure doesn't cause any harm, as the device were already properly initialized in analog mode. However, the failure at the digital mode causes the frontend mb86a20s to not initialize. Fortunately, at least on my tests, it was possible to detect that the device is a mb86a20s before the failure. What happens is that tda8290 is a very bad boy: during DVB setup, it keeps insisting to call tda18271 analog_set_params, that calls tune_agc code. The tune_agc code calls saa7134 driver, changing the value of GPIO 27, switching from digital to analog mode and disabling the access to mb86a20s, as, on Kworld SBTVD, the same GPIO used to switch the hardware AGC mode seems to be used to enable the I2C switch that allows access to the frontend (mb86a20s). So, a call to analog_set_params ultimately disables the access to the frontend, and causes a failure at the init frontend logic. This patch is a workaround for this issue: it simply checks if the frontend init had any failure. If so, it will init the frontend when some DTV application will try to set DVB mode. Even being a hack for Kworld SBTVD to work, and assumning that we could teach tda8290 to be a good boy, this is actually an improvement at the frontend driver, as it will be more reliable to initialization failures. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/mb86a20s.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/frontends/mb86a20s.c b/drivers/media/dvb/frontends/mb86a20s.c index e06507d2c3b2..cc4acd2f920d 100644 --- a/drivers/media/dvb/frontends/mb86a20s.c +++ b/drivers/media/dvb/frontends/mb86a20s.c @@ -43,6 +43,8 @@ struct mb86a20s_state { const struct mb86a20s_config *config; struct dvb_frontend frontend; + + bool need_init; }; struct regdata { @@ -382,23 +384,31 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) /* Initialize the frontend */ rc = mb86a20s_writeregdata(state, mb86a20s_init); if (rc < 0) - return rc; + goto err; if (!state->config->is_serial) { regD5 &= ~1; rc = mb86a20s_writereg(state, 0x50, 0xd5); if (rc < 0) - return rc; + goto err; rc = mb86a20s_writereg(state, 0x51, regD5); if (rc < 0) - return rc; + goto err; } if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - return 0; +err: + if (rc < 0) { + state->need_init = true; + printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); + } else { + state->need_init = false; + dprintk("Initialization succeded.\n"); + } + return rc; } static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) @@ -485,8 +495,22 @@ static int mb86a20s_set_frontend(struct dvb_frontend *fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); + dprintk("Calling tuner set parameters\n"); fe->ops.tuner_ops.set_params(fe, p); + /* + * Make it more reliable: if, for some reason, the initial + * device initialization doesn't happen, initialize it when + * a SBTVD parameters are adjusted. + * + * Unfortunately, due to a hard to track bug at tda829x/tda18271, + * the agc callback logic is not called during DVB attach time, + * causing mb86a20s to not be initialized with Kworld SBTVD. + * So, this hack is needed, in order to make Kworld SBTVD to work. + */ + if (state->need_init) + mb86a20s_initfe(fe); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); -- cgit v1.2.3 From 6183040680c56ec4bd3d7c9398cbc05e84d60c1f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 13 Jan 2011 14:24:44 -0300 Subject: [media] saa7134: Fix analog mode for Kworld SBTVD There were some issues at tda8290 that were preventing this device to work. Now that those fixes were fixed, we can enable analog mode. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-cards.c | 40 +++-------------------------- drivers/media/video/saa7134/saa7134-dvb.c | 40 +---------------------------- 2 files changed, 4 insertions(+), 76 deletions(-) diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index e7aa588c6c5a..b2426000e1d8 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5179,18 +5179,8 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", .audio_clock = 0x00187de7, -#if 0 - /* - * FIXME: Analog mode doesn't work, if digital is enabled. The proper - * fix is to use tda8290 driver, but Kworld seems to use an - * unsupported version of tda8295. - */ - .tuner_type = TUNER_NXP_TDA18271, /* TUNER_PHILIPS_TDA8290 */ - .tuner_addr = 0x60, -#else - .tuner_type = UNSET, + .tuner_type = TUNER_PHILIPS_TDA8290, .tuner_addr = ADDR_UNSET, -#endif .radio_type = UNSET, .radio_addr = ADDR_UNSET, .gpiomask = 0x8e054000, @@ -5201,6 +5191,7 @@ struct saa7134_board saa7134_boards[] = { .vmux = 1, .amux = TV, .tv = 1, + .gpio = 0x4000, #if 0 /* FIXME */ }, { .name = name_comp1, @@ -7659,36 +7650,11 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; } case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: - { - struct i2c_msg msg = { .addr = 0x4b, .flags = 0 }; - int i; - static u8 buffer[][2] = { - {0x30, 0x31}, - {0xff, 0x00}, - {0x41, 0x03}, - {0x41, 0x1a}, - {0xff, 0x02}, - {0x34, 0x00}, - {0x45, 0x97}, - {0x45, 0xc1}, - }; saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000); saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000); - /* - * FIXME: identify what device is at addr 0x4b and what means - * this initialization - */ - for (i = 0; i < ARRAY_SIZE(buffer); i++) { - msg.buf = &buffer[i][0]; - msg.len = ARRAY_SIZE(buffer[0]); - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - printk(KERN_WARNING - "%s: Unable to enable tuner(%i).\n", - dev->name, i); - } + saa7134_set_gpio(dev, 27, 0); break; - } } /* switch() */ /* initialize tuner */ diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 3315a48a848b..064bf2cd5f21 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -236,7 +236,7 @@ static struct tda18271_std_map mb86a20s_tda18271_std_map = { static struct tda18271_config kworld_tda18271_config = { .std_map = &mb86a20s_tda18271_std_map, - .gate = TDA18271_GATE_DIGITAL, + .gate = TDA18271_GATE_ANALOG, }; static const struct mb86a20s_config kworld_mb86a20s_config = { @@ -623,37 +623,6 @@ static struct tda827x_config tda827x_cfg_2_sw42 = { /* ------------------------------------------------------------------ */ -static int __kworld_sbtvd_i2c_gate_ctrl(struct saa7134_dev *dev, int enable) -{ - unsigned char initmsg[] = {0x45, 0x97}; - unsigned char msg_enable[] = {0x45, 0xc1}; - unsigned char msg_disable[] = {0x45, 0x81}; - struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { - wprintk("could not access the I2C gate\n"); - return -EIO; - } - if (enable) - msg.buf = msg_enable; - else - msg.buf = msg_disable; - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { - wprintk("could not access the I2C gate\n"); - return -EIO; - } - msleep(20); - return 0; -} -static int kworld_sbtvd_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct saa7134_dev *dev = fe->dvb->priv; - - return __kworld_sbtvd_i2c_gate_ctrl(dev, enable); -} - -/* ------------------------------------------------------------------ */ - static struct tda1004x_config tda827x_lifeview_config = { .demod_address = 0x08, .invert = 1, @@ -1660,7 +1629,6 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: - __kworld_sbtvd_i2c_gate_ctrl(dev, 0); saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000); saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000); msleep(20); @@ -1670,16 +1638,10 @@ static int dvb_init(struct saa7134_dev *dev) fe0->dvb.frontend = dvb_attach(mb86a20s_attach, &kworld_mb86a20s_config, &dev->i2c_adap); - __kworld_sbtvd_i2c_gate_ctrl(dev, 1); if (fe0->dvb.frontend != NULL) { dvb_attach(tda18271_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, &kworld_tda18271_config); - /* - * Only after success, it can initialize the gate, otherwise - * an OOPS will hit, due to kfree(fe0->dvb.frontend) - */ - fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_i2c_gate_ctrl; } break; default: -- cgit v1.2.3 From 6a58bc0f506c1825cb8f8b81a5123e26bf70902c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Jan 2011 09:11:21 -0300 Subject: [media] saa7134: Fix digital mode on Kworld SBTVD This patch fixes digital mode on Kworld SBTVD. Unfortunately, it disables analog mode. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-cards.c | 5 +++- drivers/media/video/saa7134/saa7134-dvb.c | 38 ++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index b2426000e1d8..dea90a19043f 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5179,7 +5179,11 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", .audio_clock = 0x00187de7, +#if 0 .tuner_type = TUNER_PHILIPS_TDA8290, +#else + .tuner_type = UNSET, +#endif .tuner_addr = ADDR_UNSET, .radio_type = UNSET, .radio_addr = ADDR_UNSET, @@ -5191,7 +5195,6 @@ struct saa7134_board saa7134_boards[] = { .vmux = 1, .amux = TV, .tv = 1, - .gpio = 0x4000, #if 0 /* FIXME */ }, { .name = name_comp1, diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 064bf2cd5f21..d2a12df28af0 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -236,13 +236,38 @@ static struct tda18271_std_map mb86a20s_tda18271_std_map = { static struct tda18271_config kworld_tda18271_config = { .std_map = &mb86a20s_tda18271_std_map, - .gate = TDA18271_GATE_ANALOG, + .gate = TDA18271_GATE_DIGITAL, }; static const struct mb86a20s_config kworld_mb86a20s_config = { .demod_address = 0x10, }; +static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct saa7134_dev *dev = fe->dvb->priv; + + unsigned char initmsg[] = {0x45, 0x97}; + unsigned char msg_enable[] = {0x45, 0xc1}; + unsigned char msg_disable[] = {0x45, 0x81}; + struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2}; + + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + wprintk("could not access the I2C gate\n"); + return -EIO; + } + if (enable) + msg.buf = msg_enable; + else + msg.buf = msg_disable; + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + wprintk("could not access the I2C gate\n"); + return -EIO; + } + msleep(20); + return 0; +} + /* ================================================================== * tda1004x based DVB-T cards, helper functions */ @@ -1639,10 +1664,21 @@ static int dvb_init(struct saa7134_dev *dev) &kworld_mb86a20s_config, &dev->i2c_adap); if (fe0->dvb.frontend != NULL) { +#if 0 + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); +#else + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, NULL); +#endif dvb_attach(tda18271_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, &kworld_tda18271_config); + fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl; } + + /* mb86a20s need to use the I2C gateway */ break; default: wprintk("Huh? unknown DVB card?\n"); -- cgit v1.2.3 From ecb71d262b0323981e07ce415da9b7adc917990a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 14 Jan 2011 12:03:03 -0300 Subject: [media] saa7134: Kworld SBTVD: make both analog and digital to work There are some weird bugs at tda8290/tda18271 initialization, as it insits do do analog initialization during DVB frontend attach: DVB: registering new adapter (saa7133[0]) DVB: registering adapter 0 frontend 0 (Fujitsu mb86A20s)... mb86a20s: mb86a20s_initfe tda18271_write_regs: [2-0060|M] ERROR: idx = 0x5, len = 1, i2c_transfer returned: -5 tda18271_init: [2-0060|M] error -5 on line 830 tda18271_tune: [2-0060|M] error -5 on line 908 tda18271_write_regs tda18271_write_regs: [2-0060|M] ERROR: idx = 0x5, len = 1, i2c_transfer returned: -5 tda18271c2_rf_tracking_filters_correction: [2-0060|M] error -5 on line 265 tda18271_write_regs tda18271_write_regs: [2-0060|M] ERROR: idx = 0x25, len = 1, i2c_transfer returned: -5 tda18271_channel_configuration: [2-0060|M] error -5 on line 119 tda18271_set_analog_params: [2-0060|M] error -5 on line 1045 tda18271_set_analog_params: [2-0060|M] error -5 on line 1045 tda829x 2-004b: tda8295 not locked, no signal? tda829x 2-004b: tda8295_i2c_bridge: disable i2c gate tda829x 2-004b: tda8295 not locked, no signal? tda829x 2-004b: tda8295_i2c_bridge: disable i2c gate mb86a20s_i2c_writereg: writereg error (rc == -5, reg == 0x29, data == 0x33) mb86a20s: Init failed. Will try again later The problem is that mb86a20s is only visible if the analog part is disabled. However, due to a trick at mb86a20s, it will later initialize properly: mb86a20s: mb86a20s_initfe: Initialization succeded. This is hacky and ugly. However, I coldn't find any easy way to fix it. A proper fix would be to have a resource locking schema, used by both V4L and DVB parts that would block access to analog registers while digital registers are in use, but this will probably put tda829x into a dead lock. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tda8290.c | 1 + drivers/media/video/saa7134/saa7134-cards.c | 16 ++++++++++------ drivers/media/video/saa7134/saa7134-dvb.c | 16 +++++----------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 419d064a0188..bc6a67768af1 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -232,6 +232,7 @@ static void tda8290_set_params(struct dvb_frontend *fe, tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); } + tda8290_i2c_bridge(fe, 1); if (fe->ops.tuner_ops.set_analog_params) diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index dea90a19043f..deb8fcf4aa49 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5179,11 +5179,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", .audio_clock = 0x00187de7, -#if 0 .tuner_type = TUNER_PHILIPS_TDA8290, -#else - .tuner_type = UNSET, -#endif .tuner_addr = ADDR_UNSET, .radio_type = UNSET, .radio_addr = ADDR_UNSET, @@ -6926,10 +6922,17 @@ static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev, /* toggle AGC switch through GPIO 27 */ switch (mode) { case TDA18271_ANALOG: - saa7134_set_gpio(dev, 27, 0); + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000); + msleep(20); break; case TDA18271_DIGITAL: - saa7134_set_gpio(dev, 27, 1); + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000); + msleep(20); + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x54000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x54000); + msleep(30); break; default: return -EINVAL; @@ -6987,6 +6990,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, int saa7134_tuner_callback(void *priv, int component, int command, int arg) { struct saa7134_dev *dev = priv; + if (dev != NULL) { switch (dev->tuner_type) { case TUNER_PHILIPS_TDA8290: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index d2a12df28af0..f65cad287b83 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -237,6 +237,8 @@ static struct tda18271_std_map mb86a20s_tda18271_std_map = { static struct tda18271_config kworld_tda18271_config = { .std_map = &mb86a20s_tda18271_std_map, .gate = TDA18271_GATE_DIGITAL, + .config = 3, /* Use tuner callback for AGC */ + }; static const struct mb86a20s_config kworld_mb86a20s_config = { @@ -1654,24 +1656,16 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: - saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000); - saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000); - msleep(20); - saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x54000); - saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x54000); - msleep(20); + /* Switch to digital mode */ + saa7134_tuner_callback(dev, 0, + TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); fe0->dvb.frontend = dvb_attach(mb86a20s_attach, &kworld_mb86a20s_config, &dev->i2c_adap); if (fe0->dvb.frontend != NULL) { -#if 0 dvb_attach(tda829x_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x4b, &tda829x_no_probe); -#else - dvb_attach(tda829x_attach, fe0->dvb.frontend, - &dev->i2c_adap, 0x4b, NULL); -#endif dvb_attach(tda18271_attach, fe0->dvb.frontend, 0x60, &dev->i2c_adap, &kworld_tda18271_config); -- cgit v1.2.3 From 3c7c9370fb645f4713e0fbbe69425d8db9b47a13 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Jan 2011 07:08:02 -0300 Subject: [media] v4l2-subdev: remove core.s_config and v4l2_i2c_new_subdev_cfg() The core.s_config op was meant for legacy drivers that needed to work with old pre-2.6.26 kernels. This is no longer relevant. Unfortunately, this op was incorrectly called from several drivers. Replace those occurences with proper i2c_board_info structs and call v4l2_i2c_new_subdev_board. After these changes v4l2_i2c_new_subdev_cfg() was no longer used, so remove that function as well. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cafe_ccic.c | 11 +++-- drivers/media/video/cx25840/cx25840-core.c | 22 +++------ drivers/media/video/em28xx/em28xx-cards.c | 18 ++++---- drivers/media/video/ivtv/ivtv-i2c.c | 9 +++- drivers/media/video/mt9v011.c | 54 ++++++++++++++-------- drivers/media/video/mt9v011.h | 36 --------------- drivers/media/video/ov7670.c | 74 +++++++++++++----------------- drivers/media/video/sr030pc30.c | 10 ---- drivers/media/video/v4l2-common.c | 19 +------- include/media/mt9v011.h | 17 +++++++ include/media/v4l2-common.h | 13 +----- include/media/v4l2-subdev.h | 6 +-- 12 files changed, 118 insertions(+), 171 deletions(-) delete mode 100644 drivers/media/video/mt9v011.h create mode 100644 include/media/mt9v011.h diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 49f1b8f1418e..55ffd60ffa7f 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -2001,6 +2001,11 @@ static int cafe_pci_probe(struct pci_dev *pdev, .min_width = 320, .min_height = 240, }; + struct i2c_board_info ov7670_info = { + .type = "ov7670", + .addr = 0x42, + .platform_data = &sensor_cfg, + }; /* * Start putting together one of our big camera structures. @@ -2062,9 +2067,9 @@ static int cafe_pci_probe(struct pci_dev *pdev, if (dmi_check_system(olpc_xo1_dmi)) sensor_cfg.clock_speed = 45; - cam->sensor_addr = 0x42; - cam->sensor = v4l2_i2c_new_subdev_cfg(&cam->v4l2_dev, &cam->i2c_adapter, - "ov7670", 0, &sensor_cfg, cam->sensor_addr, NULL); + cam->sensor_addr = ov7670_info.addr; + cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, &cam->i2c_adapter, + &ov7670_info, NULL); if (cam->sensor == NULL) { ret = -ENODEV; goto out_smbus; diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index f16461844c5c..6fc09dd41b9d 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1682,20 +1682,6 @@ static int cx25840_log_status(struct v4l2_subdev *sd) return 0; } -static int cx25840_s_config(struct v4l2_subdev *sd, int irq, void *platform_data) -{ - struct cx25840_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (platform_data) { - struct cx25840_platform_data *pdata = platform_data; - - state->pvr150_workaround = pdata->pvr150_workaround; - set_input(client, state->vid_input, state->aud_input); - } - return 0; -} - static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) { @@ -1787,7 +1773,6 @@ static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { static const struct v4l2_subdev_core_ops cx25840_core_ops = { .log_status = cx25840_log_status, - .s_config = cx25840_s_config, .g_chip_ident = cx25840_g_chip_ident, .g_ctrl = v4l2_subdev_g_ctrl, .s_ctrl = v4l2_subdev_s_ctrl, @@ -1974,7 +1959,6 @@ static int cx25840_probe(struct i2c_client *client, state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; - state->pvr150_workaround = 0; state->audmode = V4L2_TUNER_MODE_LANG1; state->vbi_line_offset = 8; state->id = id; @@ -2034,6 +2018,12 @@ static int cx25840_probe(struct i2c_client *client, v4l2_ctrl_cluster(2, &state->volume); v4l2_ctrl_handler_setup(&state->hdl); + if (client->dev.platform_data) { + struct cx25840_platform_data *pdata = client->dev.platform_data; + + state->pvr150_workaround = pdata->pvr150_workaround; + } + cx25840_ir_probe(sd); return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index ba03a448b41d..87f77a34eeab 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -1917,11 +1918,6 @@ static unsigned short tvp5150_addrs[] = { I2C_CLIENT_END }; -static unsigned short mt9v011_addrs[] = { - 0xba >> 1, - I2C_CLIENT_END -}; - static unsigned short msp3400_addrs[] = { 0x80 >> 1, 0x88 >> 1, @@ -2624,11 +2620,17 @@ void em28xx_card_setup(struct em28xx *dev) "tvp5150", 0, tvp5150_addrs); if (dev->em28xx_sensor == EM28XX_MT9V011) { + struct mt9v011_platform_data pdata; + struct i2c_board_info mt9v011_info = { + .type = "mt9v011", + .addr = 0xba >> 1, + .platform_data = &pdata, + }; struct v4l2_subdev *sd; - sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "mt9v011", 0, mt9v011_addrs); - v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); + pdata.xtal = dev->sensor_xtal; + sd = v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap, + &mt9v011_info, NULL); } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index e103b8fc7452..9fb86a081c0f 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -300,10 +300,15 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) adap, type, 0, I2C_ADDRS(hw_addrs[idx])); } else if (hw == IVTV_HW_CX25840) { struct cx25840_platform_data pdata; + struct i2c_board_info cx25840_info = { + .type = "cx25840", + .addr = hw_addrs[idx], + .platform_data = &pdata, + }; pdata.pvr150_workaround = itv->pvr150_workaround; - sd = v4l2_i2c_new_subdev_cfg(&itv->v4l2_dev, - adap, type, 0, &pdata, hw_addrs[idx], NULL); + sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, + &cx25840_info, NULL); } else { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, hw_addrs[idx], NULL); diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index 209ff97261a9..4904d25f689f 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -12,17 +12,41 @@ #include #include #include -#include "mt9v011.h" +#include MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_LICENSE("GPL"); - static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-2)"); +#define R00_MT9V011_CHIP_VERSION 0x00 +#define R01_MT9V011_ROWSTART 0x01 +#define R02_MT9V011_COLSTART 0x02 +#define R03_MT9V011_HEIGHT 0x03 +#define R04_MT9V011_WIDTH 0x04 +#define R05_MT9V011_HBLANK 0x05 +#define R06_MT9V011_VBLANK 0x06 +#define R07_MT9V011_OUT_CTRL 0x07 +#define R09_MT9V011_SHUTTER_WIDTH 0x09 +#define R0A_MT9V011_CLK_SPEED 0x0a +#define R0B_MT9V011_RESTART 0x0b +#define R0C_MT9V011_SHUTTER_DELAY 0x0c +#define R0D_MT9V011_RESET 0x0d +#define R1E_MT9V011_DIGITAL_ZOOM 0x1e +#define R20_MT9V011_READ_MODE 0x20 +#define R2B_MT9V011_GREEN_1_GAIN 0x2b +#define R2C_MT9V011_BLUE_GAIN 0x2c +#define R2D_MT9V011_RED_GAIN 0x2d +#define R2E_MT9V011_GREEN_2_GAIN 0x2e +#define R35_MT9V011_GLOBAL_GAIN 0x35 +#define RF1_MT9V011_CHIP_ENABLE 0xf1 + +#define MT9V011_VERSION 0x8232 +#define MT9V011_REV_B_VERSION 0x8243 + /* supported controls */ static struct v4l2_queryctrl mt9v011_qctrl[] = { { @@ -469,23 +493,6 @@ static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt return 0; } -static int mt9v011_s_config(struct v4l2_subdev *sd, int dumb, void *data) -{ - struct mt9v011 *core = to_mt9v011(sd); - unsigned *xtal = data; - - v4l2_dbg(1, debug, sd, "s_config called\n"); - - if (xtal) { - core->xtal = *xtal; - v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", - *xtal / 1000000, (*xtal / 1000) % 1000); - } - - return 0; -} - - #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9v011_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) @@ -536,7 +543,6 @@ static const struct v4l2_subdev_core_ops mt9v011_core_ops = { .g_ctrl = mt9v011_g_ctrl, .s_ctrl = mt9v011_s_ctrl, .reset = mt9v011_reset, - .s_config = mt9v011_s_config, .g_chip_ident = mt9v011_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9v011_g_register, @@ -596,6 +602,14 @@ static int mt9v011_probe(struct i2c_client *c, core->height = 480; core->xtal = 27000000; /* Hz */ + if (c->dev.platform_data) { + struct mt9v011_platform_data *pdata = c->dev.platform_data; + + core->xtal = pdata->xtal; + v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", + core->xtal / 1000000, (core->xtal / 1000) % 1000); + } + v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", c->addr << 1, c->adapter->name, version); diff --git a/drivers/media/video/mt9v011.h b/drivers/media/video/mt9v011.h deleted file mode 100644 index 3350fd6083c3..000000000000 --- a/drivers/media/video/mt9v011.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor - * - * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) - * This code is placed under the terms of the GNU General Public License v2 - */ - -#ifndef MT9V011_H_ -#define MT9V011_H_ - -#define R00_MT9V011_CHIP_VERSION 0x00 -#define R01_MT9V011_ROWSTART 0x01 -#define R02_MT9V011_COLSTART 0x02 -#define R03_MT9V011_HEIGHT 0x03 -#define R04_MT9V011_WIDTH 0x04 -#define R05_MT9V011_HBLANK 0x05 -#define R06_MT9V011_VBLANK 0x06 -#define R07_MT9V011_OUT_CTRL 0x07 -#define R09_MT9V011_SHUTTER_WIDTH 0x09 -#define R0A_MT9V011_CLK_SPEED 0x0a -#define R0B_MT9V011_RESTART 0x0b -#define R0C_MT9V011_SHUTTER_DELAY 0x0c -#define R0D_MT9V011_RESET 0x0d -#define R1E_MT9V011_DIGITAL_ZOOM 0x1e -#define R20_MT9V011_READ_MODE 0x20 -#define R2B_MT9V011_GREEN_1_GAIN 0x2b -#define R2C_MT9V011_BLUE_GAIN 0x2c -#define R2D_MT9V011_RED_GAIN 0x2d -#define R2E_MT9V011_GREEN_2_GAIN 0x2e -#define R35_MT9V011_GLOBAL_GAIN 0x35 -#define RF1_MT9V011_CHIP_ENABLE 0xf1 - -#define MT9V011_VERSION 0x8232 -#define MT9V011_REV_B_VERSION 0x8243 - -#endif diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index c881a64b41fd..d4e7c11553c3 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -1449,47 +1449,6 @@ static int ov7670_g_chip_ident(struct v4l2_subdev *sd, return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); } -static int ov7670_s_config(struct v4l2_subdev *sd, int dumb, void *data) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct ov7670_config *config = data; - struct ov7670_info *info = to_state(sd); - int ret; - - info->clock_speed = 30; /* default: a guess */ - - /* - * Must apply configuration before initializing device, because it - * selects I/O method. - */ - if (config) { - info->min_width = config->min_width; - info->min_height = config->min_height; - info->use_smbus = config->use_smbus; - - if (config->clock_speed) - info->clock_speed = config->clock_speed; - } - - /* Make sure it's an ov7670 */ - ret = ov7670_detect(sd); - if (ret) { - v4l_dbg(1, debug, client, - "chip found @ 0x%x (%s) is not an ov7670 chip.\n", - client->addr << 1, client->adapter->name); - kfree(info); - return ret; - } - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - info->fmt = &ov7670_formats[0]; - info->sat = 128; /* Review this */ - info->clkrc = info->clock_speed / 30; - - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { @@ -1528,7 +1487,6 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { .s_ctrl = ov7670_s_ctrl, .queryctrl = ov7670_queryctrl, .reset = ov7670_reset, - .s_config = ov7670_s_config, .init = ov7670_init, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov7670_g_register, @@ -1558,6 +1516,7 @@ static int ov7670_probe(struct i2c_client *client, { struct v4l2_subdev *sd; struct ov7670_info *info; + int ret; info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); if (info == NULL) @@ -1565,6 +1524,37 @@ static int ov7670_probe(struct i2c_client *client, sd = &info->sd; v4l2_i2c_subdev_init(sd, client, &ov7670_ops); + info->clock_speed = 30; /* default: a guess */ + if (client->dev.platform_data) { + struct ov7670_config *config = client->dev.platform_data; + + /* + * Must apply configuration before initializing device, because it + * selects I/O method. + */ + info->min_width = config->min_width; + info->min_height = config->min_height; + info->use_smbus = config->use_smbus; + + if (config->clock_speed) + info->clock_speed = config->clock_speed; + } + + /* Make sure it's an ov7670 */ + ret = ov7670_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; + } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + info->fmt = &ov7670_formats[0]; + info->sat = 128; /* Review this */ + info->clkrc = info->clock_speed / 30; return 0; } diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c index 864696b7a006..c901721a1db3 100644 --- a/drivers/media/video/sr030pc30.c +++ b/drivers/media/video/sr030pc30.c @@ -714,15 +714,6 @@ static int sr030pc30_base_config(struct v4l2_subdev *sd) return ret; } -static int sr030pc30_s_config(struct v4l2_subdev *sd, - int irq, void *platform_data) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - info->pdata = platform_data; - return 0; -} - static int sr030pc30_s_stream(struct v4l2_subdev *sd, int enable) { return 0; @@ -763,7 +754,6 @@ static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) } static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { - .s_config = sr030pc30_s_config, .s_power = sr030pc30_s_power, .queryctrl = sr030pc30_queryctrl, .s_ctrl = sr030pc30_s_ctrl, diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 3f0871b550ad..810eef43c216 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -407,18 +407,6 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); - if (sd) { - /* We return errors from v4l2_subdev_call only if we have the - callback as the .s_config is not mandatory */ - int err = v4l2_subdev_call(sd, core, s_config, - info->irq, info->platform_data); - - if (err && err != -ENOIOCTLCMD) { - v4l2_device_unregister_subdev(sd); - sd = NULL; - } - } - error: /* If we have a client but no subdev, then something went wrong and we must unregister the client. */ @@ -428,9 +416,8 @@ error: } EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); -struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, +struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, struct i2c_adapter *adapter, const char *client_type, - int irq, void *platform_data, u8 addr, const unsigned short *probe_addrs) { struct i2c_board_info info; @@ -440,12 +427,10 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, memset(&info, 0, sizeof(info)); strlcpy(info.type, client_type, sizeof(info.type)); info.addr = addr; - info.irq = irq; - info.platform_data = platform_data; return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs); } -EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_cfg); +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); /* Return i2c client address of v4l2_subdev. */ unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) diff --git a/include/media/mt9v011.h b/include/media/mt9v011.h new file mode 100644 index 000000000000..ea29fc74cd06 --- /dev/null +++ b/include/media/mt9v011.h @@ -0,0 +1,17 @@ +/* mt9v011 sensor + * + * Copyright (C) 2011 Hans Verkuil + * + * 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. + */ + +#ifndef __MT9V011_H__ +#define __MT9V011_H__ + +struct mt9v011_platform_data { + unsigned xtal; /* Hz */ +}; + +#endif diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 2d65b35cdab2..a659319e8582 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -138,21 +138,10 @@ struct v4l2_subdev_ops; /* Load an i2c module and return an initialized v4l2_subdev struct. The client_type argument is the name of the chip that's on the adapter. */ -struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, +struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, struct i2c_adapter *adapter, const char *client_type, - int irq, void *platform_data, u8 addr, const unsigned short *probe_addrs); -/* Load an i2c module and return an initialized v4l2_subdev struct. - The client_type argument is the name of the chip that's on the adapter. */ -static inline struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, const char *client_type, - u8 addr, const unsigned short *probe_addrs) -{ - return v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter, client_type, 0, NULL, - addr, probe_addrs); -} - struct i2c_board_info; struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index b0316a7cf08d..42fbe462031d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -106,10 +106,7 @@ struct v4l2_subdev_io_pin_config { u8 strength; /* Pin drive strength */ }; -/* s_config: if set, then it is always called by the v4l2_i2c_new_subdev* - functions after the v4l2_subdev was registered. It is used to pass - platform data to the subdev which can be used during initialization. - +/* s_io_pin_config: configure one or more chip I/O pins for chips that multiplex different internal signal pads out to IO pins. This function takes a pointer to an array of 'n' pin configuration entries, one for @@ -141,7 +138,6 @@ struct v4l2_subdev_io_pin_config { struct v4l2_subdev_core_ops { int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip); int (*log_status)(struct v4l2_subdev *sd); - int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data); int (*s_io_pin_config)(struct v4l2_subdev *sd, size_t n, struct v4l2_subdev_io_pin_config *pincfg); int (*init)(struct v4l2_subdev *sd, u32 val); -- cgit v1.2.3 From 45f6f84af3ae9db19f39bc5d0976d626b0ef626e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 8 Jan 2011 07:15:53 -0300 Subject: [media] v4l2-subdev: add (un)register internal ops Some subdevs need to call into the board code after they are registered and have a valid struct v4l2_device pointer. The s_config op was abused for this, but now that it is removed we need a cleaner way of solving this. So this patch adds a struct with internal ops that the v4l2 core can call. Currently only two ops exist: register and unregister. Subdevs can implement these to call the board code and pass it the v4l2_device pointer, which the board code can then use to get access to the struct that embeds the v4l2_device. It is expected that in the future open and close ops will also be added. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-device.c | 14 ++++++++++++-- include/media/v4l2-subdev.h | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 7fe6f92af480..b24f002ffa67 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -126,11 +126,19 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, WARN_ON(sd->v4l2_dev != NULL); if (!try_module_get(sd->owner)) return -ENODEV; + sd->v4l2_dev = v4l2_dev; + if (sd->internal_ops && sd->internal_ops->registered) { + err = sd->internal_ops->registered(sd); + if (err) + return err; + } /* This just returns 0 if either of the two args is NULL */ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); - if (err) + if (err) { + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); return err; - sd->v4l2_dev = v4l2_dev; + } spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); @@ -146,6 +154,8 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) spin_lock(&sd->v4l2_dev->lock); list_del(&sd->list); spin_unlock(&sd->v4l2_dev->lock); + if (sd->internal_ops && sd->internal_ops->unregistered) + sd->internal_ops->unregistered(sd); sd->v4l2_dev = NULL; module_put(sd->owner); } diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 42fbe462031d..daf1e57d9b26 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -411,6 +411,21 @@ struct v4l2_subdev_ops { const struct v4l2_subdev_sensor_ops *sensor; }; +/* + * Internal ops. Never call this from drivers, only the v4l2 framework can call + * these ops. + * + * registered: called when this subdev is registered. When called the v4l2_dev + * field is set to the correct v4l2_device. + * + * unregistered: called when this subdev is unregistered. When called the + * v4l2_dev field is still set to the correct v4l2_device. + */ +struct v4l2_subdev_internal_ops { + int (*registered)(struct v4l2_subdev *sd); + void (*unregistered)(struct v4l2_subdev *sd); +}; + #define V4L2_SUBDEV_NAME_SIZE 32 /* Set this flag if this subdev is a i2c device. */ @@ -427,6 +442,8 @@ struct v4l2_subdev { u32 flags; struct v4l2_device *v4l2_dev; const struct v4l2_subdev_ops *ops; + /* Never call these internal ops from within a driver! */ + const struct v4l2_subdev_internal_ops *internal_ops; /* The control handler of this subdev. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; /* name must be unique */ -- cgit v1.2.3 From 2a863793beaa0fc9ee7aeb87efe85544a6b129c0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Jan 2011 14:45:03 -0300 Subject: [media] v4l2-ctrls: v4l2_ctrl_handler_setup must set is_new to 1 Renamed has_new to is_new. Drivers can use the is_new field to determine if a new value was specified for a control. The v4l2_ctrl_handler_setup() must always set this to 1 since the setup has to force a full update of all controls. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-controls.txt | 12 ++++++++++++ drivers/media/video/v4l2-ctrls.c | 24 ++++++++++++++---------- include/media/v4l2-ctrls.h | 6 ++++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index 8773778d23fc..881e7f44491b 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -285,6 +285,9 @@ implement g_volatile_ctrl like this: The 'new value' union is not used in g_volatile_ctrl. In general controls that need to implement g_volatile_ctrl are read-only controls. +Note that if one or more controls in a control cluster are marked as volatile, +then all the controls in the cluster are seen as volatile. + To mark a control as volatile you have to set the is_volatile flag: ctrl = v4l2_ctrl_new_std(&sd->ctrl_handler, ...); @@ -462,6 +465,15 @@ pointer to the v4l2_ctrl_ops struct that is used for that cluster. Obviously, all controls in the cluster array must be initialized to either a valid control or to NULL. +In rare cases you might want to know which controls of a cluster actually +were set explicitly by the user. For this you can check the 'is_new' flag of +each control. For example, in the case of a volume/mute cluster the 'is_new' +flag of the mute control would be set if the user called VIDIOC_S_CTRL for +mute only. If the user would call VIDIOC_S_EXT_CTRLS for both mute and volume +controls, then the 'is_new' flag would be 1 for both controls. + +The 'is_new' flag is always 1 when called from v4l2_ctrl_handler_setup(). + VIDIOC_LOG_STATUS Support ========================= diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 8f81efcfcf56..0d1a3d831374 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -569,7 +569,7 @@ static int user_to_new(struct v4l2_ext_control *c, int ret; u32 size; - ctrl->has_new = 1; + ctrl->is_new = 1; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: ctrl->val64 = c->value64; @@ -1280,8 +1280,12 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (ctrl->done) continue; - for (i = 0; i < master->ncontrols; i++) - cur_to_new(master->cluster[i]); + for (i = 0; i < master->ncontrols; i++) { + if (master->cluster[i]) { + cur_to_new(master->cluster[i]); + master->cluster[i]->is_new = 1; + } + } /* Skip button controls and read-only controls. */ if (ctrl->type == V4L2_CTRL_TYPE_BUTTON || @@ -1645,7 +1649,7 @@ static int try_or_set_control_cluster(struct v4l2_ctrl *master, bool set) if (ctrl == NULL) continue; - if (ctrl->has_new) { + if (ctrl->is_new) { /* Double check this: it may have changed since the last check in try_or_set_ext_ctrls(). */ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)) @@ -1719,13 +1723,13 @@ static int try_or_set_ext_ctrls(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_lock(ctrl); - /* Reset the 'has_new' flags of the cluster */ + /* Reset the 'is_new' flags of the cluster */ for (j = 0; j < master->ncontrols; j++) if (master->cluster[j]) - master->cluster[j]->has_new = 0; + master->cluster[j]->is_new = 0; /* Copy the new caller-supplied control values. - user_to_new() sets 'has_new' to 1. */ + user_to_new() sets 'is_new' to 1. */ ret = cluster_walk(i, cs, helpers, user_to_new); if (!ret) @@ -1822,13 +1826,13 @@ static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val) v4l2_ctrl_lock(ctrl); - /* Reset the 'has_new' flags of the cluster */ + /* Reset the 'is_new' flags of the cluster */ for (i = 0; i < master->ncontrols; i++) if (master->cluster[i]) - master->cluster[i]->has_new = 0; + master->cluster[i]->is_new = 0; ctrl->val = *val; - ctrl->has_new = 1; + ctrl->is_new = 1; ret = try_or_set_control_cluster(master, false); if (!ret) ret = try_or_set_control_cluster(master, true); diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index d69ab4aae032..fcc9a0cf8ff1 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -53,8 +53,10 @@ struct v4l2_ctrl_ops { * @handler: The handler that owns the control. * @cluster: Point to start of cluster array. * @ncontrols: Number of controls in cluster array. - * @has_new: Internal flag: set when there is a valid new value. * @done: Internal flag: set for each processed control. + * @is_new: Set when the user specified a new value for this control. It + * is also set when called from v4l2_ctrl_handler_setup. Drivers + * should never set this flag. * @is_private: If set, then this control is private to its handler and it * will not be added to any other handlers. Drivers can set * this flag. @@ -97,9 +99,9 @@ struct v4l2_ctrl { struct v4l2_ctrl_handler *handler; struct v4l2_ctrl **cluster; unsigned ncontrols; - unsigned int has_new:1; unsigned int done:1; + unsigned int is_new:1; unsigned int is_private:1; unsigned int is_volatile:1; -- cgit v1.2.3 From 0beb6714e787ae46e1c1fc5e275452c1950ad141 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 04:44:02 -0300 Subject: [media] gspca: Version change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 3581dea3c1f7..f21f2a258ae0 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -55,7 +55,7 @@ MODULE_AUTHOR("Jean-François Moine "); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 11, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 12, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; -- cgit v1.2.3 From 95c967c167785eb991cf6b22fb854dd8d61d0ff8 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 05:20:29 -0300 Subject: [media] gspca: Remove __devinit, __devinitconst and __devinitdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __devinit* must not be used in USB drivers. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/benq.c | 2 +- drivers/media/video/gspca/conex.c | 4 ++-- drivers/media/video/gspca/cpia1.c | 2 +- drivers/media/video/gspca/etoms.c | 4 ++-- drivers/media/video/gspca/finepix.c | 2 +- drivers/media/video/gspca/gl860/gl860.c | 2 +- drivers/media/video/gspca/jeilinj.c | 2 +- drivers/media/video/gspca/konica.c | 2 +- drivers/media/video/gspca/m5602/m5602_core.c | 2 +- drivers/media/video/gspca/mars.c | 2 +- drivers/media/video/gspca/mr97310a.c | 2 +- drivers/media/video/gspca/ov519.c | 2 +- drivers/media/video/gspca/ov534.c | 2 +- drivers/media/video/gspca/ov534_9.c | 2 +- drivers/media/video/gspca/pac207.c | 2 +- drivers/media/video/gspca/pac7302.c | 4 ++-- drivers/media/video/gspca/pac7311.c | 4 ++-- drivers/media/video/gspca/sn9c2028.c | 2 +- drivers/media/video/gspca/sn9c20x.c | 2 +- drivers/media/video/gspca/sonixb.c | 4 ++-- drivers/media/video/gspca/sonixj.c | 2 +- drivers/media/video/gspca/spca1528.c | 2 +- drivers/media/video/gspca/spca500.c | 2 +- drivers/media/video/gspca/spca501.c | 2 +- drivers/media/video/gspca/spca505.c | 2 +- drivers/media/video/gspca/spca508.c | 2 +- drivers/media/video/gspca/spca561.c | 2 +- drivers/media/video/gspca/sq905.c | 2 +- drivers/media/video/gspca/sq905c.c | 2 +- drivers/media/video/gspca/sq930x.c | 2 +- drivers/media/video/gspca/stk014.c | 2 +- drivers/media/video/gspca/stv0680.c | 2 +- drivers/media/video/gspca/stv06xx/stv06xx.c | 2 +- drivers/media/video/gspca/sunplus.c | 2 +- drivers/media/video/gspca/t613.c | 2 +- drivers/media/video/gspca/tv8532.c | 2 +- drivers/media/video/gspca/vc032x.c | 2 +- drivers/media/video/gspca/xirlink_cit.c | 2 +- drivers/media/video/gspca/zc3xx.c | 2 +- 39 files changed, 44 insertions(+), 44 deletions(-) diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c index 629043933501..a09c4709d613 100644 --- a/drivers/media/video/gspca/benq.c +++ b/drivers/media/video/gspca/benq.c @@ -276,7 +276,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x04a5, 0x3035)}, {} }; diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 1eacb6c7926d..8b398493f96b 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -1040,14 +1040,14 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const struct usb_device_id device_table[] __devinitconst = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0572, 0x0041)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ -static int __devinit sd_probe(struct usb_interface *intf, +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c index c1ae05f4661f..4bf2cab98d64 100644 --- a/drivers/media/video/gspca/cpia1.c +++ b/drivers/media/video/gspca/cpia1.c @@ -2088,7 +2088,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0553, 0x0002)}, {USB_DEVICE(0x0813, 0x0001)}, {} diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index a594b36d6199..4b2c483fce6f 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -864,7 +864,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const struct usb_device_id device_table[] __devinitconst = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, #if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, @@ -875,7 +875,7 @@ static const struct usb_device_id device_table[] __devinitconst = { MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ -static int __devinit sd_probe(struct usb_interface *intf, +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index d78226455d1f..987b4b69d7ab 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -229,7 +229,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } /* Table of supported USB devices */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x04cb, 0x0104)}, {USB_DEVICE(0x04cb, 0x0109)}, {USB_DEVICE(0x04cb, 0x010b)}, diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index b05bec7321b5..99083038cec3 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -488,7 +488,7 @@ static void sd_callback(struct gspca_dev *gspca_dev) /*=================== USB driver structure initialisation ==================*/ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x05e3, 0x0503)}, {USB_DEVICE(0x05e3, 0xf191)}, {} diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index a35e87bb0388..06b777f5379e 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -314,7 +314,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* Table of supported USB devices */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0979, 0x0280)}, {} }; diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/video/gspca/konica.c index d2ce65dcbfdc..5964691c0e95 100644 --- a/drivers/media/video/gspca/konica.c +++ b/drivers/media/video/gspca/konica.c @@ -607,7 +607,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x04c8, 0x0720)}, /* Intel YC 76 */ {} }; diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index c872b93a3351..a7722b1aef9b 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -28,7 +28,7 @@ int force_sensor; static int dump_bridge; int dump_sensor; -static const __devinitdata struct usb_device_id m5602_table[] = { +static const struct usb_device_id m5602_table[] = { {USB_DEVICE(0x0402, 0x5602)}, {} }; diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index a81536e78698..cb4d0bf0d784 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -490,7 +490,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x050f)}, {} }; diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 7607a288b51c..3884c9d300c5 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -1229,7 +1229,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index e1c3b9328ace..e30791f05690 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -4747,7 +4747,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 0edf93973b1c..ba8e56c3fc83 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1289,7 +1289,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x1415, 0x2000)}, {} }; diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c index c5244b4b4777..aaf5428c57f5 100644 --- a/drivers/media/video/gspca/ov534_9.c +++ b/drivers/media/video/gspca/ov534_9.c @@ -1429,7 +1429,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3003)}, {} }; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 96f9986305b4..81739a2f205e 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -530,7 +530,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x4028)}, {USB_DEVICE(0x093a, 0x2460)}, {USB_DEVICE(0x093a, 0x2461)}, diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 2700975abce5..5615d7bd8304 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -1184,7 +1184,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const struct usb_device_id device_table[] __devinitconst = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3009)}, {USB_DEVICE(0x093a, 0x2620)}, {USB_DEVICE(0x093a, 0x2621)}, @@ -1201,7 +1201,7 @@ static const struct usb_device_id device_table[] __devinitconst = { MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ -static int __devinit sd_probe(struct usb_interface *intf, +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 6820f5d58b19..f8801b50e64f 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -837,7 +837,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const struct usb_device_id device_table[] __devinitconst = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2600)}, {USB_DEVICE(0x093a, 0x2601)}, {USB_DEVICE(0x093a, 0x2603)}, @@ -849,7 +849,7 @@ static const struct usb_device_id device_table[] __devinitconst = { MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ -static int __devinit sd_probe(struct usb_interface *intf, +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c index 40a06680502d..4271f86dfe01 100644 --- a/drivers/media/video/gspca/sn9c2028.c +++ b/drivers/media/video/gspca/sn9c2028.c @@ -703,7 +703,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */ /* The Genius Smart is untested. I can't find an owner ! */ /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */ diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index cb08d00d0a31..fcf29897b713 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -2470,7 +2470,7 @@ static const struct sd_desc sd_desc = { | (SENSOR_ ## sensor << 8) \ | (i2c_addr) -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)}, {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)}, diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index e45d1cfb4461..c6cd68d66b53 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1565,7 +1565,7 @@ static const struct sd_desc sd_desc = { .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge -static const struct usb_device_id device_table[] __devinitconst = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */ {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */ @@ -1599,7 +1599,7 @@ static const struct usb_device_id device_table[] __devinitconst = { MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ -static int __devinit sd_probe(struct usb_interface *intf, +static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index db35391d00e6..3e41653f11c1 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2909,7 +2909,7 @@ static const struct sd_desc sd_desc = { .driver_info = (BRIDGE_ ## bridge << 16) \ | (SENSOR_ ## sensor << 8) \ | (flags) -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)}, {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, PDN_INV)}, diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/video/gspca/spca1528.c index e64338664410..76c006b2bc83 100644 --- a/drivers/media/video/gspca/spca1528.c +++ b/drivers/media/video/gspca/spca1528.c @@ -555,7 +555,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x04fc, 0x1528)}, {} }; diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 8e202b9039f1..45552c3ff8d9 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -1051,7 +1051,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x040a, 0x0300), .driver_info = KodakEZ200}, {USB_DEVICE(0x041e, 0x400a), .driver_info = CreativePCCam300}, {USB_DEVICE(0x046d, 0x0890), .driver_info = LogitechTraveler}, diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 642839a11e8d..f7ef282cc600 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -2155,7 +2155,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x040a, 0x0002), .driver_info = KodakDVC325}, {USB_DEVICE(0x0497, 0xc001), .driver_info = SmileIntlCamera}, {USB_DEVICE(0x0506, 0x00df), .driver_info = ThreeComHomeConnectLite}, diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index bc9dd9034ab4..e5bf865147d7 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -786,7 +786,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x401d), .driver_info = Nxultra}, {USB_DEVICE(0x0733, 0x0430), .driver_info = IntelPCCameraPro}, /*fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 7307638ac91d..348319371523 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1509,7 +1509,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0130, 0x0130), .driver_info = HamaUSBSightcam}, {USB_DEVICE(0x041e, 0x4018), .driver_info = CreativeVista}, {USB_DEVICE(0x0733, 0x0110), .driver_info = ViewQuestVQ110}, diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index 3a162c6d5466..e836e778dfb6 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -1061,7 +1061,7 @@ static const struct sd_desc *sd_desc[2] = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A}, {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A}, {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A}, diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 404067745775..2e9c06175192 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -396,7 +396,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* Table of supported USB devices */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x2770, 0x9120)}, {} }; diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 8ba199543856..457563b7a71b 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -298,7 +298,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* Table of supported USB devices */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x2770, 0x905c)}, {USB_DEVICE(0x2770, 0x9050)}, {USB_DEVICE(0x2770, 0x9051)}, diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c index a4a98811b9e3..8215d5dcd456 100644 --- a/drivers/media/video/gspca/sq930x.c +++ b/drivers/media/video/gspca/sq930x.c @@ -1163,7 +1163,7 @@ static const struct sd_desc sd_desc = { #define ST(sensor, type) \ .driver_info = (SENSOR_ ## sensor << 8) \ | (type) -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)}, {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)}, {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)}, diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 11a192b95ed4..87be52b5e1e3 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -495,7 +495,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x05e1, 0x0893)}, {} }; diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c index b199ad4666bd..e2ef41cf72d7 100644 --- a/drivers/media/video/gspca/stv0680.c +++ b/drivers/media/video/gspca/stv0680.c @@ -327,7 +327,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0553, 0x0202)}, {USB_DEVICE(0x041e, 0x4007)}, {} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 28ea4175b80e..7e0661429293 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -564,7 +564,7 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { /* QuickCam Express */ {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, /* LEGO cam / QuickCam Web */ diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index a9cbcd6011d9..543542af2720 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -1162,7 +1162,7 @@ static const struct sd_desc sd_desc = { #define BS(bridge, subtype) \ .driver_info = (BRIDGE_ ## bridge << 8) \ | (subtype) -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)}, {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)}, {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)}, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 8f0c33116e0d..a3eccd815766 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -1416,7 +1416,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x17a1, 0x0128)}, {} }; diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 38c22f0a4263..933ef2ca658c 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -388,7 +388,7 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x0920)}, {USB_DEVICE(0x046d, 0x0921)}, {USB_DEVICE(0x0545, 0x808b)}, diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 9b2ae1b6cc75..6caed734a06a 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -4192,7 +4192,7 @@ static const struct sd_desc sd_desc = { #define BF(bridge, flags) \ .driver_info = (BRIDGE_ ## bridge << 8) \ | (flags) -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)}, {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)}, {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)}, diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c index 5b5039a02031..c089a0f6f1d0 100644 --- a/drivers/media/video/gspca/xirlink_cit.c +++ b/drivers/media/video/gspca/xirlink_cit.c @@ -3270,7 +3270,7 @@ static const struct sd_desc sd_desc_isoc_nego = { }; /* -- module initialisation -- */ -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 }, { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 }, { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 }, diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 14b85d483163..865216e9362c 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6909,7 +6909,7 @@ static const struct sd_desc sd_desc = { #endif }; -static const __devinitdata struct usb_device_id device_table[] = { +static const struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x041e)}, {USB_DEVICE(0x041e, 0x4017)}, {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, -- cgit v1.2.3 From ded5e903b75a9e2a9349e19063e3b2a63b05e828 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 05:27:37 -0300 Subject: [media] gspca: Remove useless instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/jpeg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h index de63c36806c0..ab54910418b4 100644 --- a/drivers/media/video/gspca/jpeg.h +++ b/drivers/media/video/gspca/jpeg.h @@ -141,9 +141,9 @@ static void jpeg_define(u8 *jpeg_hdr, memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); #ifndef CONEX_CAM jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height; jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; - jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width; jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY; #endif } -- cgit v1.2.3 From fc63de88e0f27ecdaab4d2d836fd7bacab643fc0 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 05:35:18 -0300 Subject: [media] gspca - ov519: Cleanup source and add a comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index e30791f05690..8ab2c452c25e 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -488,7 +488,6 @@ static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { #define R511_SNAP_PXDIV 0x1c #define R511_SNAP_LNDIV 0x1d #define R511_SNAP_UV_EN 0x1e -#define R511_SNAP_UV_EN 0x1e #define R511_SNAP_OPTS 0x1f #define R511_DRAM_FLOW_CTL 0x20 @@ -1847,8 +1846,7 @@ static const struct ov_i2c_regvals norm_7670[] = { { 0x6c, 0x0a }, { 0x6d, 0x55 }, { 0x6e, 0x11 }, - { 0x6f, 0x9f }, - /* "9e for advance AWB" */ + { 0x6f, 0x9f }, /* "9e for advance AWB" */ { 0x6a, 0x40 }, { OV7670_R01_BLUE, 0x40 }, { OV7670_R02_RED, 0x60 }, @@ -3054,7 +3052,7 @@ static void ov519_configure(struct sd *sd) { static const struct ov_regvals init_519[] = { { 0x5a, 0x6d }, /* EnableSystem */ - { 0x53, 0x9b }, + { 0x53, 0x9b }, /* don't enable the microcontroller */ { OV519_R54_EN_CLK1, 0xff }, /* set bit2 to enable jpeg */ { 0x5d, 0x03 }, { 0x49, 0x01 }, -- cgit v1.2.3 From ddffa49e257e2b28e23f1e2729c0560bcad89937 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 05:49:47 -0300 Subject: [media] gspca - ov534: Clearer debug messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov534.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index ba8e56c3fc83..a837f2942a8c 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -479,7 +479,7 @@ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) struct usb_device *udev = gspca_dev->dev; int ret; - PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); + PDEBUG(D_USBO, "SET 01 0000 %04x %02x", reg, val); gspca_dev->usb_buf[0] = val; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -500,7 +500,7 @@ static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) 0x01, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); + PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]); if (ret < 0) err("read failed %d", ret); return gspca_dev->usb_buf[0]; @@ -558,7 +558,7 @@ static int sccb_check_status(struct gspca_dev *gspca_dev) static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) { - PDEBUG(D_USBO, "reg: 0x%02x, val: 0x%02x", reg, val); + PDEBUG(D_USBO, "sccb write: %02x %02x", reg, val); ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); -- cgit v1.2.3 From 14b67c2969ebf50bd5534b2a0c441f8569a9361e Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 05:58:04 -0300 Subject: [media] gspca - ov534: Propagate errors to higher level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov534.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index a837f2942a8c..04da22802736 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -479,6 +479,9 @@ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) struct usb_device *udev = gspca_dev->dev; int ret; + if (gspca_dev->usb_err < 0) + return; + PDEBUG(D_USBO, "SET 01 0000 %04x %02x", reg, val); gspca_dev->usb_buf[0] = val; ret = usb_control_msg(udev, @@ -486,8 +489,10 @@ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - if (ret < 0) + if (ret < 0) { err("write failed %d", ret); + gspca_dev->usb_err = ret; + } } static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) @@ -495,14 +500,18 @@ static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) struct usb_device *udev = gspca_dev->dev; int ret; + if (gspca_dev->usb_err < 0) + return 0; ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); PDEBUG(D_USBI, "GET 01 0000 %04x %02x", reg, gspca_dev->usb_buf[0]); - if (ret < 0) + if (ret < 0) { err("read failed %d", ret); + gspca_dev->usb_err = ret; + } return gspca_dev->usb_buf[0]; } @@ -563,8 +572,10 @@ static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); - if (!sccb_check_status(gspca_dev)) + if (!sccb_check_status(gspca_dev)) { err("sccb_reg_write failed"); + gspca_dev->usb_err = -EIO; + } } static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) @@ -885,7 +896,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ov534_set_led(gspca_dev, 0); set_frame_rate(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_start(struct gspca_dev *gspca_dev) @@ -920,7 +931,7 @@ static int sd_start(struct gspca_dev *gspca_dev) ov534_set_led(gspca_dev, 1); ov534_reg_write(gspca_dev, 0xe0, 0x00); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) -- cgit v1.2.3 From 3afef85bfefa65a31f4dbf8e7921c7938adb24f4 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 06:39:11 -0300 Subject: [media] gspca - sonixj: Infrared bug fix and enhancement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The infrared was set by sensor write instead of bridge GPIO. It is now settable by the standard control ILLUMINATOR_1. A module parameter permits to set the right GPIO bit according to the StarCam model. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 80 ++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 3e41653f11c1..c634dc113f94 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -25,12 +25,12 @@ #include "gspca.h" #include "jpeg.h" -#define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0) - MODULE_AUTHOR("Jean-François Moine "); MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); MODULE_LICENSE("GPL"); +static int starcam; + /* controls */ enum e_ctrl { BRIGHTNESS, @@ -43,7 +43,7 @@ enum e_ctrl { HFLIP, VFLIP, SHARPNESS, - INFRARED, + ILLUM, FREQ, NCTRLS /* number of controls */ }; @@ -100,7 +100,8 @@ enum sensors { }; /* device flags */ -#define PDN_INV 1 /* inverse pin S_PWR_DN / sn_xxx tables */ +#define F_PDN_INV 0x01 /* inverse pin S_PWR_DN / sn_xxx tables */ +#define F_ILLUM 0x02 /* presence of illuminator */ /* sn9c1xx definitions */ /* register 0x01 */ @@ -124,7 +125,7 @@ static void setgamma(struct gspca_dev *gspca_dev); static void setautogain(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); -static void setinfrared(struct gspca_dev *gspca_dev); +static void setillum(struct gspca_dev *gspca_dev); static void setfreq(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[NCTRLS] = { @@ -251,18 +252,17 @@ static const struct ctrl sd_ctrls[NCTRLS] = { }, .set_control = setsharpness }, -/* mt9v111 only */ -[INFRARED] = { +[ILLUM] = { { - .id = V4L2_CID_INFRARED, + .id = V4L2_CID_ILLUMINATORS_1, .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Infrared", + .name = "Illuminator / infrared", .minimum = 0, .maximum = 1, .step = 1, .default_value = 0, }, - .set_control = setinfrared + .set_control = setillum }, /* ov7630/ov7648/ov7660 only */ [FREQ] = { @@ -282,32 +282,26 @@ static const struct ctrl sd_ctrls[NCTRLS] = { /* table of the disabled controls */ static const __u32 ctrl_dis[] = { [SENSOR_ADCM1700] = (1 << AUTOGAIN) | - (1 << INFRARED) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_GC0307] = (1 << INFRARED) | - (1 << HFLIP) | +[SENSOR_GC0307] = (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_HV7131R] = (1 << INFRARED) | - (1 << HFLIP) | +[SENSOR_HV7131R] = (1 << HFLIP) | (1 << FREQ), -[SENSOR_MI0360] = (1 << INFRARED) | - (1 << HFLIP) | +[SENSOR_MI0360] = (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MI0360B] = (1 << INFRARED) | - (1 << HFLIP) | +[SENSOR_MI0360B] = (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_MO4000] = (1 << INFRARED) | - (1 << HFLIP) | +[SENSOR_MO4000] = (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), @@ -315,40 +309,32 @@ static const __u32 ctrl_dis[] = { (1 << VFLIP) | (1 << FREQ), -[SENSOR_OM6802] = (1 << INFRARED) | - (1 << HFLIP) | +[SENSOR_OM6802] = (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), -[SENSOR_OV7630] = (1 << INFRARED) | - (1 << HFLIP), +[SENSOR_OV7630] = (1 << HFLIP), -[SENSOR_OV7648] = (1 << INFRARED) | - (1 << HFLIP), +[SENSOR_OV7648] = (1 << HFLIP), [SENSOR_OV7660] = (1 << AUTOGAIN) | - (1 << INFRARED) | (1 << HFLIP) | (1 << VFLIP), [SENSOR_PO1030] = (1 << AUTOGAIN) | - (1 << INFRARED) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), [SENSOR_PO2030N] = (1 << AUTOGAIN) | - (1 << INFRARED) | (1 << FREQ), [SENSOR_SOI768] = (1 << AUTOGAIN) | - (1 << INFRARED) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), [SENSOR_SP80708] = (1 << AUTOGAIN) | - (1 << INFRARED) | (1 << HFLIP) | (1 << VFLIP) | (1 << FREQ), @@ -1876,6 +1862,8 @@ static int sd_init(struct gspca_dev *gspca_dev) sd->i2c_addr = sn9c1xx[9]; gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; + if (!(sd->flags & F_ILLUM)) + gspca_dev->ctrl_dis |= (1 << ILLUM); return gspca_dev->usb_err; } @@ -2199,16 +2187,20 @@ static void setsharpness(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val); } -static void setinfrared(struct gspca_dev *gspca_dev) +static void setillum(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - if (gspca_dev->ctrl_dis & (1 << INFRARED)) + if (gspca_dev->ctrl_dis & (1 << ILLUM)) return; -/*fixme: different sequence for StarCam Clip and StarCam 370i */ -/* Clip */ - i2c_w1(gspca_dev, 0x02, /* gpio */ - sd->ctrls[INFRARED].val ? 0x66 : 0x64); + if (starcam) + reg_w1(gspca_dev, 0x02, /* gpio */ + sd->ctrls[ILLUM].val ? + 0x55 : 0x54); /* 370i */ + else + reg_w1(gspca_dev, 0x02, + sd->ctrls[ILLUM].val ? + 0x66 : 0x64); /* Clip */ } static void setfreq(struct gspca_dev *gspca_dev) @@ -2346,7 +2338,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* sensor clock already enabled in sd_init */ /* reg_w1(gspca_dev, 0xf1, 0x00); */ reg01 = sn9c1xx[1]; - if (sd->flags & PDN_INV) + if (sd->flags & F_PDN_INV) reg01 ^= S_PDN_INV; /* power down inverted */ reg_w1(gspca_dev, 0x01, reg01); @@ -2912,8 +2904,8 @@ static const struct sd_desc sd_desc = { static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)}, {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, - {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, PDN_INV)}, - {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, PDN_INV)}, + {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)}, + {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)}, {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)}, {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)}, @@ -2925,7 +2917,7 @@ static const struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */ {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)}, /* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */ - {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)}, /* or MT9V111 */ /* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */ /* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */ @@ -3004,3 +2996,7 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); + +module_param(starcam, int, 0644); +MODULE_PARM_DESC(starcam, + "StarCam model. 0: Clip, 1: 370i"); -- cgit v1.2.3 From a63d601803c2e3ba06ed51b9ed997fc6bf80e5bf Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 13 Jan 2011 07:56:00 -0300 Subject: [media] gspca - sonixj: Add LED (illuminator) control to the webcam 0c45:614a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index c634dc113f94..d6f39ce1b7e1 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2193,14 +2193,22 @@ static void setillum(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << ILLUM)) return; - if (starcam) - reg_w1(gspca_dev, 0x02, /* gpio */ - sd->ctrls[ILLUM].val ? - 0x55 : 0x54); /* 370i */ - else - reg_w1(gspca_dev, 0x02, - sd->ctrls[ILLUM].val ? - 0x66 : 0x64); /* Clip */ + switch (sd->sensor) { + case SENSOR_ADCM1700: + reg_w1(gspca_dev, 0x02, /* gpio */ + sd->ctrls[ILLUM].val ? 0x64 : 0x60); + break; + case SENSOR_MT9V111: + if (starcam) + reg_w1(gspca_dev, 0x02, + sd->ctrls[ILLUM].val ? + 0x55 : 0x54); /* 370i */ + else + reg_w1(gspca_dev, 0x02, + sd->ctrls[ILLUM].val ? + 0x66 : 0x64); /* Clip */ + break; + } } static void setfreq(struct gspca_dev *gspca_dev) @@ -2959,7 +2967,7 @@ static const struct usb_device_id device_table[] = { /* or GC0305 / GC0307 */ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ - {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)}, /* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/ {} }; -- cgit v1.2.3 From 36fd97884daf4e30b556a6c59b58db19a06d58af Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 2 Jan 2011 16:14:03 -0300 Subject: [media] frontends/ix2505v: Remember to free allocated memory in failure path We may leak the storage allocated to 'state' in drivers/media/dvb/frontends/ix2505v.c::ix2505v_attach() on error, as it is too early to be able to call ix2505v_release(). This patch makes sure we free the allocated memory in the failure case. Signed-off-by: Jesper Juhl Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/ix2505v.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/ix2505v.c b/drivers/media/dvb/frontends/ix2505v.c index 6360c681ded9..6c2e929bd79f 100644 --- a/drivers/media/dvb/frontends/ix2505v.c +++ b/drivers/media/dvb/frontends/ix2505v.c @@ -311,7 +311,7 @@ struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, return fe; error: - ix2505v_release(fe); + kfree(state); return NULL; } EXPORT_SYMBOL(ix2505v_attach); -- cgit v1.2.3 From fd01ad98945073faeb25391489caef4844f265c4 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 2 Jan 2011 17:57:24 -0300 Subject: [media] media, tlg2300: Fix memory leak in alloc_bulk_urbs_generic() Hi, While reading drivers/media/video/tlg2300/pd-video.c::alloc_bulk_urbs_generic() I noticed that - We don't free the memory allocated to 'urb' if the call to usb_alloc_coherent() fails. - If the 'num' argument to the function is ever <= 0 we'll return an uninitialized variable 'i' to the caller. The following patch addresses both of the above by a) calling usb_free_urb() when usb_alloc_coherent() fails and by explicitly initializing 'i' to zero. I also moved the variables 'mem' and 'urb' inside the for loop. This does not actually make any difference, it just seemed more correct to me to let variables exist only in the innermost scope they are used. Signed-off-by: Jesper Juhl Acked-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tlg2300/pd-video.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index a1ffe18640fe..df33a1d188bb 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -512,19 +512,20 @@ int alloc_bulk_urbs_generic(struct urb **urb_array, int num, int buf_size, gfp_t gfp_flags, usb_complete_t complete_fn, void *context) { - struct urb *urb; - void *mem; - int i; + int i = 0; - for (i = 0; i < num; i++) { - urb = usb_alloc_urb(0, gfp_flags); + for (; i < num; i++) { + void *mem; + struct urb *urb = usb_alloc_urb(0, gfp_flags); if (urb == NULL) return i; mem = usb_alloc_coherent(udev, buf_size, gfp_flags, &urb->transfer_dma); - if (mem == NULL) + if (mem == NULL) { + usb_free_urb(urb); return i; + } usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr), mem, buf_size, complete_fn, context); -- cgit v1.2.3 From a3bc5e3304c9ba8e7504597026d9ca93784d1239 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jan 2011 10:49:34 -0300 Subject: [media] v4l/cx18: update workqueue usage With cmwq, there's no reason to use separate out_work_queue. Drop it and use system_wq instead. The in_work_queue needs to be ordered so can't use one of the system wqs; however, as it isn't used to reclaim memory, allocate the workqueue with alloc_ordered_workqueue() without WQ_MEM_RECLAIM. Signed-off-by: Tejun Heo Reviewed-by: Andy Walls Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 24 ++---------------------- drivers/media/video/cx18/cx18-driver.h | 3 --- drivers/media/video/cx18/cx18-streams.h | 3 +-- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 133ec2bac180..944af8adbe0c 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -664,7 +664,7 @@ static int __devinit cx18_create_in_workq(struct cx18 *cx) { snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", cx->v4l2_dev.name); - cx->in_work_queue = create_singlethread_workqueue(cx->in_workq_name); + cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0); if (cx->in_work_queue == NULL) { CX18_ERR("Unable to create incoming mailbox handler thread\n"); return -ENOMEM; @@ -672,18 +672,6 @@ static int __devinit cx18_create_in_workq(struct cx18 *cx) return 0; } -static int __devinit cx18_create_out_workq(struct cx18 *cx) -{ - snprintf(cx->out_workq_name, sizeof(cx->out_workq_name), "%s-out", - cx->v4l2_dev.name); - cx->out_work_queue = create_workqueue(cx->out_workq_name); - if (cx->out_work_queue == NULL) { - CX18_ERR("Unable to create outgoing mailbox handler threads\n"); - return -ENOMEM; - } - return 0; -} - static void __devinit cx18_init_in_work_orders(struct cx18 *cx) { int i; @@ -710,15 +698,9 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->epu2apu_mb_lock); mutex_init(&cx->epu2cpu_mb_lock); - ret = cx18_create_out_workq(cx); - if (ret) - return ret; - ret = cx18_create_in_workq(cx); - if (ret) { - destroy_workqueue(cx->out_work_queue); + if (ret) return ret; - } cx18_init_in_work_orders(cx); @@ -1107,7 +1089,6 @@ free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); free_workqueues: destroy_workqueue(cx->in_work_queue); - destroy_workqueue(cx->out_work_queue); err: if (retval == 0) retval = -ENODEV; @@ -1259,7 +1240,6 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_halt_firmware(cx); destroy_workqueue(cx->in_work_queue); - destroy_workqueue(cx->out_work_queue); cx18_streams_cleanup(cx, 1); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index f6f3e50d4bdf..306caac6d3fc 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -617,9 +617,6 @@ struct cx18 { struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS]; char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ - struct workqueue_struct *out_work_queue; - char out_workq_name[12]; /* "cx18-NN-out" */ - /* i2c */ struct i2c_adapter i2c_adap[2]; struct i2c_algo_bit_data i2c_algo[2]; diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 51765eb12d39..713b0e61536d 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -42,8 +42,7 @@ static inline bool cx18_stream_enabled(struct cx18_stream *s) /* Related to submission of mdls to firmware */ static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) { - struct cx18 *cx = s->cx; - queue_work(cx->out_work_queue, &s->out_work_order); + schedule_work(&s->out_work_order); } static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s, -- cgit v1.2.3 From 34b8fc8e683cbcbbe47806260ef5dc505915b45f Mon Sep 17 00:00:00 2001 From: Matti Aaltonen Date: Tue, 4 Jan 2011 08:29:37 -0300 Subject: [media] V4L2: WL1273 FM Radio: Replace ioctl with unlocked_ioctl Use unlocked_ioctl in v4l2_file_operations. The locking is already in place. Signed-off-by: Matti J. Aaltonen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-wl1273.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index dd6bd364efa0..7ecc8e657663 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1407,7 +1407,7 @@ static const struct v4l2_file_operations wl1273_fops = { .read = wl1273_fm_fops_read, .write = wl1273_fm_fops_write, .poll = wl1273_fm_fops_poll, - .ioctl = video_ioctl2, + .unlocked_ioctl = video_ioctl2, .open = wl1273_fm_fops_open, .release = wl1273_fm_fops_release, }; -- cgit v1.2.3 From e95342f168f7a02441cec51b222bb7ae62df364d Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Thu, 6 Jan 2011 14:25:44 -0300 Subject: [media] DVB: cx231xx drivers does not use dummy frontend anymore Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx231xx/cx231xx-dvb.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c index fe59a1c3f064..363aa6004221 100644 --- a/drivers/media/video/cx231xx/cx231xx-dvb.c +++ b/drivers/media/video/cx231xx/cx231xx-dvb.c @@ -28,7 +28,6 @@ #include #include "xc5000.h" -#include "dvb_dummy_fe.h" #include "s5h1432.h" #include "tda18271.h" #include "s5h1411.h" @@ -619,7 +618,7 @@ static int dvb_init(struct cx231xx *dev) if (dev->dvb->frontend == NULL) { printk(DRIVER_NAME - ": Failed to attach dummy front end\n"); + ": Failed to attach s5h1411 front end\n"); result = -EINVAL; goto out_free; } @@ -665,7 +664,7 @@ static int dvb_init(struct cx231xx *dev) if (dev->dvb->frontend == NULL) { printk(DRIVER_NAME - ": Failed to attach dummy front end\n"); + ": Failed to attach s5h1411 front end\n"); result = -EINVAL; goto out_free; } -- cgit v1.2.3 From 7d2edfc23e9852591cb031a26093cdcd07a34a90 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 6 Jan 2011 16:57:14 -0300 Subject: [media] rc/imon: fix ffdc device detection oops There's a nasty bug that slipped in when the rc device interface was altered, only affecting the older 0xffdc imon devices. We were trying to access ictx->rdev->allowed_protos before ictx->rdev had been set. There's also an issue with call ordering that meant the correct keymap wasn't getting loaded for MCE IR type 0xffdc devices. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 6811512b4e83..a30bd99c5ca4 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -1756,7 +1756,6 @@ static void imon_get_ffdc_type(struct imon_context *ictx) printk(KERN_CONT " (id 0x%02x)\n", ffdc_cfg_byte); ictx->display_type = detected_display_type; - ictx->rdev->allowed_protos = allowed_protos; ictx->rc_type = allowed_protos; } @@ -1839,10 +1838,6 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) rdev->allowed_protos = RC_TYPE_OTHER | RC_TYPE_RC6; /* iMON PAD or MCE */ rdev->change_protocol = imon_ir_change_protocol; rdev->driver_name = MOD_NAME; - if (ictx->rc_type == RC_TYPE_RC6) - rdev->map_name = RC_MAP_IMON_MCE; - else - rdev->map_name = RC_MAP_IMON_PAD; /* Enable front-panel buttons and/or knobs */ memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet)); @@ -1851,11 +1846,18 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx) if (ret) dev_info(ictx->dev, "panel buttons/knobs setup failed\n"); - if (ictx->product == 0xffdc) + if (ictx->product == 0xffdc) { imon_get_ffdc_type(ictx); + rdev->allowed_protos = ictx->rc_type; + } imon_set_display_type(ictx); + if (ictx->rc_type == RC_TYPE_RC6) + rdev->map_name = RC_MAP_IMON_MCE; + else + rdev->map_name = RC_MAP_IMON_PAD; + ret = rc_register_device(rdev); if (ret < 0) { dev_err(ictx->dev, "remote input dev register failed\n"); -- cgit v1.2.3 From 2e4c55626a0c30b5b2bc9469c025a563a81c3785 Mon Sep 17 00:00:00 2001 From: Kyle McMartin Date: Thu, 6 Jan 2011 16:59:33 -0300 Subject: [media] rc/ene_ir: fix oops on module load dev->rdev is accessed in ene_setup_hw_settings, so it needs to be wired up before then. [Jarod Wilson]: Also fix a possible improper resource freeing bug while we're looking at possible probe issues here. Signed-off-by: Kyle McMartin CC: Maxim Levitsky Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ene_ir.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 80b3c319f698..885abdddfaa8 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1004,6 +1004,10 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) /* validate resources */ error = -ENODEV; + /* init these to -1, as 0 is valid for both */ + dev->hw_io = -1; + dev->irq = -1; + if (!pnp_port_valid(pnp_dev, 0) || pnp_port_len(pnp_dev, 0) < ENE_IO_SIZE) goto error; @@ -1072,6 +1076,8 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) rdev->input_name = "ENE eHome Infrared Remote Transceiver"; } + dev->rdev = rdev; + ene_rx_setup_hw_buffer(dev); ene_setup_default_settings(dev); ene_setup_hw_settings(dev); @@ -1083,7 +1089,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) if (error < 0) goto error; - dev->rdev = rdev; ene_notice("driver has been succesfully loaded"); return 0; error: -- cgit v1.2.3 From 9ad77eb57b45f81ac3e12077d19e5f121c4cff6d Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 6 Jan 2011 16:59:34 -0300 Subject: [media] rc/imon: need to submit urb before ffdc type check Otherwise, we have a null receive buffer, and the logic all falls down, goes boom, all ffdc devs wind up as imon IR w/VFD. Oops. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index a30bd99c5ca4..703420749243 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -2110,18 +2110,6 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) goto find_endpoint_failed; } - ictx->idev = imon_init_idev(ictx); - if (!ictx->idev) { - dev_err(dev, "%s: input device setup failed\n", __func__); - goto idev_setup_failed; - } - - ictx->rdev = imon_init_rdev(ictx); - if (!ictx->rdev) { - dev_err(dev, "%s: rc device setup failed\n", __func__); - goto rdev_setup_failed; - } - usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0, usb_rcvintpipe(ictx->usbdev_intf0, ictx->rx_endpoint_intf0->bEndpointAddress), @@ -2135,13 +2123,25 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) goto urb_submit_failed; } + ictx->idev = imon_init_idev(ictx); + if (!ictx->idev) { + dev_err(dev, "%s: input device setup failed\n", __func__); + goto idev_setup_failed; + } + + ictx->rdev = imon_init_rdev(ictx); + if (!ictx->rdev) { + dev_err(dev, "%s: rc device setup failed\n", __func__); + goto rdev_setup_failed; + } + return ictx; -urb_submit_failed: - rc_unregister_device(ictx->rdev); rdev_setup_failed: input_unregister_device(ictx->idev); idev_setup_failed: + usb_kill_urb(ictx->rx_urb_intf0); +urb_submit_failed: find_endpoint_failed: mutex_unlock(&ictx->lock); usb_free_urb(tx_urb); -- cgit v1.2.3 From 5aad724280b9f8ffff3a55311ef0ba35ebb4099a Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 6 Jan 2011 16:59:36 -0300 Subject: [media] rc: fix up and genericize some time unit conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ene_ir driver was using a private define of MS_TO_NS, which is meant to be microseconds to nanoseconds. The mceusb driver copied it, intending to use is a milliseconds to microseconds. Lets move the defines to a common location, expand and standardize them a touch, so that we now have: MS_TO_NS - milliseconds to nanoseconds MS_TO_US - milliseconds to microseconds US_TO_NS - microseconds to nanoseconds Reported-by: David Härdeman CC: Maxim Levitsky Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ene_ir.c | 16 ++++++++-------- drivers/media/rc/ene_ir.h | 2 -- drivers/media/rc/mceusb.c | 7 +++---- include/media/rc-core.h | 3 +++ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 885abdddfaa8..1ac49139158d 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -446,27 +446,27 @@ static void ene_rx_setup(struct ene_device *dev) select_timeout: if (dev->rx_fan_input_inuse) { - dev->rdev->rx_resolution = MS_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN); + dev->rdev->rx_resolution = US_TO_NS(ENE_FW_SAMPLE_PERIOD_FAN); /* Fan input doesn't support timeouts, it just ends the input with a maximum sample */ dev->rdev->min_timeout = dev->rdev->max_timeout = - MS_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK * + US_TO_NS(ENE_FW_SMPL_BUF_FAN_MSK * ENE_FW_SAMPLE_PERIOD_FAN); } else { - dev->rdev->rx_resolution = MS_TO_NS(sample_period); + dev->rdev->rx_resolution = US_TO_NS(sample_period); /* Theoreticly timeout is unlimited, but we cap it * because it was seen that on one device, it * would stop sending spaces after around 250 msec. * Besides, this is close to 2^32 anyway and timeout is u32. */ - dev->rdev->min_timeout = MS_TO_NS(127 * sample_period); - dev->rdev->max_timeout = MS_TO_NS(200000); + dev->rdev->min_timeout = US_TO_NS(127 * sample_period); + dev->rdev->max_timeout = US_TO_NS(200000); } if (dev->hw_learning_and_tx_capable) - dev->rdev->tx_resolution = MS_TO_NS(sample_period); + dev->rdev->tx_resolution = US_TO_NS(sample_period); if (dev->rdev->timeout > dev->rdev->max_timeout) dev->rdev->timeout = dev->rdev->max_timeout; @@ -801,7 +801,7 @@ static irqreturn_t ene_isr(int irq, void *data) dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space"); - ev.duration = MS_TO_NS(hw_sample); + ev.duration = US_TO_NS(hw_sample); ev.pulse = pulse; ir_raw_event_store_with_filter(dev->rdev, &ev); } @@ -821,7 +821,7 @@ static void ene_setup_default_settings(struct ene_device *dev) dev->learning_mode_enabled = learning_mode_force; /* Set reasonable default timeout */ - dev->rdev->timeout = MS_TO_NS(150000); + dev->rdev->timeout = US_TO_NS(150000); } /* Upload all hardware settings at once. Used at load and resume time */ diff --git a/drivers/media/rc/ene_ir.h b/drivers/media/rc/ene_ir.h index c179baf34cb4..337a41d4450b 100644 --- a/drivers/media/rc/ene_ir.h +++ b/drivers/media/rc/ene_ir.h @@ -201,8 +201,6 @@ #define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__) #define dbg_regs(format, ...) __dbg(3, format, ## __VA_ARGS__) -#define MS_TO_NS(msec) ((msec) * 1000) - struct ene_device { struct pnp_dev *pnp_dev; struct rc_dev *rdev; diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 0fef6efad537..2d9113493cf0 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -48,7 +48,6 @@ #define USB_BUFLEN 32 /* USB reception buffer length */ #define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ #define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ -#define MS_TO_NS(msec) ((msec) * 1000) /* MCE constants */ #define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ @@ -817,7 +816,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) switch (ir->buf_in[index]) { /* 2-byte return value commands */ case MCE_CMD_S_TIMEOUT: - ir->rc->timeout = MS_TO_NS((hi << 8 | lo) / 2); + ir->rc->timeout = MS_TO_US((hi << 8 | lo) / 2); break; /* 1-byte return value commands */ @@ -858,7 +857,7 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) ir->rem--; rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) - * MS_TO_NS(MCE_TIME_UNIT); + * MS_TO_US(MCE_TIME_UNIT); dev_dbg(ir->dev, "Storing %s with duration %d\n", rawir.pulse ? "pulse" : "space", @@ -1061,7 +1060,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->priv = ir; rc->driver_type = RC_DRIVER_IR_RAW; rc->allowed_protos = RC_TYPE_ALL; - rc->timeout = MS_TO_NS(1000); + rc->timeout = MS_TO_US(1000); if (!ir->flags.no_tx) { rc->s_tx_mask = mceusb_set_tx_mask; rc->s_tx_carrier = mceusb_set_tx_carrier; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index a23c1fc685a1..2963263f31e2 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -183,6 +183,9 @@ static inline void init_ir_raw_event(struct ir_raw_event *ev) } #define IR_MAX_DURATION 0xFFFFFFFF /* a bit more than 4 seconds */ +#define US_TO_NS(usec) ((usec) * 1000) +#define MS_TO_US(msec) ((msec) * 1000) +#define MS_TO_NS(msec) ((msec) * 1000 * 1000) void ir_raw_event_handle(struct rc_dev *dev); int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev); -- cgit v1.2.3 From 76f1ef427c0aab3d3c917b497562ea2cdaaae056 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 6 Jan 2011 16:59:35 -0300 Subject: [media] rc/imon: default to key mode instead of mouse mode My initial thinking was that we should default to mouse mode, so people could use the mouse function to click on something on a login screen, but a lot of systems where a remote is useful automatically log in a user and launch a media center application, some of which hide the mouse, which can be confusing to users if they punch buttons on the remote and don't see any feedback. Plus, first and foremost, its a remote, so lets default to being a remote, and only toggle into mouse mode when the user explicitly asks for it. As a nice side-effect, this actually simplifies some of the code a fair bit... Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/imon.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 703420749243..e7dc6b46fdfa 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -988,7 +988,6 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) int retval; struct imon_context *ictx = rc->priv; struct device *dev = ictx->dev; - bool pad_mouse; unsigned char ir_proto_packet[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 }; @@ -1000,29 +999,20 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) case RC_TYPE_RC6: dev_dbg(dev, "Configuring IR receiver for MCE protocol\n"); ir_proto_packet[0] = 0x01; - pad_mouse = false; break; case RC_TYPE_UNKNOWN: case RC_TYPE_OTHER: dev_dbg(dev, "Configuring IR receiver for iMON protocol\n"); - if (pad_stabilize && !nomouse) - pad_mouse = true; - else { + if (!pad_stabilize) dev_dbg(dev, "PAD stabilize functionality disabled\n"); - pad_mouse = false; - } /* ir_proto_packet[0] = 0x00; // already the default */ rc_type = RC_TYPE_OTHER; break; default: dev_warn(dev, "Unsupported IR protocol specified, overriding " "to iMON IR protocol\n"); - if (pad_stabilize && !nomouse) - pad_mouse = true; - else { + if (!pad_stabilize) dev_dbg(dev, "PAD stabilize functionality disabled\n"); - pad_mouse = false; - } /* ir_proto_packet[0] = 0x00; // already the default */ rc_type = RC_TYPE_OTHER; break; @@ -1035,7 +1025,7 @@ static int imon_ir_change_protocol(struct rc_dev *rc, u64 rc_type) goto out; ictx->rc_type = rc_type; - ictx->pad_mouse = pad_mouse; + ictx->pad_mouse = false; out: return retval; @@ -1517,7 +1507,7 @@ static void imon_incoming_packet(struct imon_context *ictx, spin_unlock_irqrestore(&ictx->kc_lock, flags); return; } else { - ictx->pad_mouse = 0; + ictx->pad_mouse = false; dev_dbg(dev, "mouse mode disabled, passing key value\n"); } } -- cgit v1.2.3 From cb26a24ee9706473f31d34cc259f4dcf45cd0644 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 7 Jan 2011 16:41:54 -0300 Subject: [media] [v3,media] av7110: check for negative array offset info->num comes from the user. It's type int. If the user passes in a negative value that would cause memory corruption. Signed-off-by: Dan Carpenter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ttpci/av7110_ca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c index 122c72806916..9fc1dd0ba4c3 100644 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ b/drivers/media/dvb/ttpci/av7110_ca.c @@ -277,7 +277,7 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) { ca_slot_info_t *info=(ca_slot_info_t *)parg; - if (info->num > 1) + if (info->num < 0 || info->num > 1) return -EINVAL; av7110->ci_slot[info->num].num = info->num; av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? -- cgit v1.2.3 From b7eccc46a1dae153d0102c161b4ce4bddb3f52ba Mon Sep 17 00:00:00 2001 From: Christian Gmeiner Date: Sat, 8 Jan 2011 18:45:35 -0300 Subject: [media] adv7175: support s_power This patch adds s_power support to adv7175 driver. Power-down is done by power-down all four DACs. Signed-off-by: Christian Gmeiner Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/adv7175.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index f318b51448b3..d2327dbb473f 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -303,11 +303,22 @@ static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); } +static int adv7175_s_power(struct v4l2_subdev *sd, int on) +{ + if (on) + adv7175_write(sd, 0x01, 0x00); + else + adv7175_write(sd, 0x01, 0x78); + + return 0; +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops adv7175_core_ops = { .g_chip_ident = adv7175_g_chip_ident, .init = adv7175_init, + .s_power = adv7175_s_power, }; static const struct v4l2_subdev_video_ops adv7175_video_ops = { -- cgit v1.2.3 From 2dbd61b4651f0bdc6cd5f75a983844b5f3831e17 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 9 Jan 2011 00:53:53 -0300 Subject: [media] ir-raw: fix sparse non-ANSI function warning Fix sparse warning for non-ANSI function declaration: drivers/media/rc/ir-raw.c:247:30: warning: non-ANSI function declaration of function 'ir_raw_get_allowed_protocols' Signed-off-by: Randy Dunlap Cc: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 185baddcbf14..73230ff93b8a 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -233,7 +233,7 @@ EXPORT_SYMBOL_GPL(ir_raw_event_handle); /* used internally by the sysfs interface */ u64 -ir_raw_get_allowed_protocols() +ir_raw_get_allowed_protocols(void) { u64 protocols; mutex_lock(&ir_raw_handler_lock); -- cgit v1.2.3 From 2400982a2e8a8e4e95f0a0e1517bbe63cc88038f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 16 Jan 2011 10:09:13 -0300 Subject: [media] radio-aimslab.c needs #include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit e3c92215198cb6aa00ad38db2780faa6b72e0a3f ("[media] radio-aimslab.c: Fix gcc 4.5+ bug") removed the include, but introduced new callers of msleep(): | drivers/media/radio/radio-aimslab.c: In function ‘rt_decvol’: | drivers/media/radio/radio-aimslab.c:76: error: implicit declaration of function ‘msleep’ Signed-off-by: Geert Uytterhoeven Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-aimslab.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 6cc5d130fbc8..4ce10dbeadd8 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -31,6 +31,7 @@ #include /* Modules */ #include /* Initdata */ #include /* request_region */ +#include /* msleep */ #include /* kernel radio structs */ #include /* for KERNEL_VERSION MACRO */ #include /* outb, outb_p */ -- cgit v1.2.3 From 3c61be446ae5c26e2829d37c6b5d02d3af536024 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Thu, 13 Jan 2011 00:46:07 -0300 Subject: [media] tm6000: rework init code Rework device init part. Move common code part to a function. Usefull for register multiple devices like video, radio, vbi etc. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/tm6000/tm6000-video.c | 46 +++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/staging/tm6000/tm6000-video.c b/drivers/staging/tm6000/tm6000-video.c index 8fe017c3721f..eb9b9f1bc138 100644 --- a/drivers/staging/tm6000/tm6000-video.c +++ b/drivers/staging/tm6000/tm6000-video.c @@ -1450,29 +1450,55 @@ static struct video_device tm6000_template = { * ------------------------------------------------------------------ */ -int tm6000_v4l2_register(struct tm6000_core *dev) +static struct video_device *vdev_init(struct tm6000_core *dev, + const struct video_device + *template, const char *type_name) { - int ret = -1; struct video_device *vfd; vfd = video_device_alloc(); - if(!vfd) { + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = tm6000_debug; + vfd->lock = &dev->lock; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + video_set_drvdata(vfd, dev); + return vfd; +} + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = -1; + + dev->vfd = vdev_init(dev, &tm6000_template, "video"); + + if (!dev->vfd) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); return -ENOMEM; } - dev->vfd = vfd; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); - memcpy(dev->vfd, &tm6000_template, sizeof(*(dev->vfd))); - dev->vfd->debug = tm6000_debug; - dev->vfd->lock = &dev->lock; + ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); - vfd->v4l2_dev = &dev->v4l2_dev; - video_set_drvdata(vfd, dev); + if (ret < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + return ret; + } + + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->vfd)); - ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); printk(KERN_INFO "Trident TVMaster TM5600/TM6000/TM6010 USB2 board (Load status: %d)\n", ret); return ret; } -- cgit v1.2.3 From 22f37712f29868b393025aa28bee807b4d2783ea Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 16 Jan 2011 05:39:21 -0300 Subject: [media] firedtv: fix remote control with newer Xorg evdev After a recent update of xf86-input-evdev and xorg-server, I noticed that X11 applications did not receive keypresses from the FireDTV infrared remote control anymore. Instead, the Xorg log featured lots of "FireDTV remote control: dropping event due to full queue!" exclamations. The Linux console did not have an issue with the FireDTV's RC though. The fix is to insert EV_SYN events after the key-down/-up events. Signed-off-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/firewire/firedtv-rc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c index fcf3828472b8..f82d4a93feb3 100644 --- a/drivers/media/dvb/firewire/firedtv-rc.c +++ b/drivers/media/dvb/firewire/firedtv-rc.c @@ -172,7 +172,8 @@ void fdtv_unregister_rc(struct firedtv *fdtv) void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) { - u16 *keycode = fdtv->remote_ctrl_dev->keycode; + struct input_dev *idev = fdtv->remote_ctrl_dev; + u16 *keycode = idev->keycode; if (code >= 0x0300 && code <= 0x031f) code = keycode[code - 0x0300]; @@ -188,6 +189,8 @@ void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) return; } - input_report_key(fdtv->remote_ctrl_dev, code, 1); - input_report_key(fdtv->remote_ctrl_dev, code, 0); + input_report_key(idev, code, 1); + input_sync(idev); + input_report_key(idev, code, 0); + input_sync(idev); } -- cgit v1.2.3 From 86e52428eed0507a11ba03c5de731c763bd88480 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 12 Jan 2011 22:50:10 -0300 Subject: [media] lirc_zilog: Reword debug message in ir_probe() Jean Delvare suggested this better format for debug output in ir_probe(). Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index ad29bb1275ab..bf81e3fddf80 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -1195,9 +1195,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) int ret; int have_rx = 0, have_tx = 0; - dprintk("%s: adapter name (%s) nr %d, i2c_device_id name (%s), " - "client addr=0x%02x\n", - __func__, adap->name, adap->nr, id->name, client->addr); + dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n", + __func__, id->name, adap->nr, adap->name, client->addr); /* * FIXME - This probe function probes both the Tx and Rx -- cgit v1.2.3 From 02fcaaa3a52b2bdad8a08a3ee5747f27f27df27d Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 12 Jan 2011 23:31:25 -0300 Subject: [media] lirc_zilog: Remove disable_tx module parameter The only reason to use the lirc_zilog module is for IR Tx, so remove the possibility of disabling IR Tx. If the user needs only IR Rx, then the ir-kbd-i2c module works just fine, and doesn't require a "firmware" image. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index bf81e3fddf80..91125336144e 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -131,7 +131,6 @@ static struct mutex tx_data_lock; /* module parameters */ static int debug; /* debug output */ static int disable_rx; /* disable RX device */ -static int disable_tx; /* disable TX device */ static int minor = -1; /* minor number */ #define dprintk(fmt, args...) \ @@ -1218,12 +1217,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) */ client->addr = 0x70; - if (!disable_tx) { - if (i2c_master_recv(client, &buf, 1) == 1) - have_tx = 1; - dprintk("probe 0x70 @ %s: %s\n", - adap->name, have_tx ? "success" : "failed"); - } + if (i2c_master_recv(client, &buf, 1) == 1) + have_tx = 1; + dprintk("probe 0x70 @ %s: %s\n", + adap->name, have_tx ? "success" : "failed"); if (!disable_rx) { client->addr = 0x71; @@ -1398,6 +1395,3 @@ MODULE_PARM_DESC(debug, "Enable debugging messages"); module_param(disable_rx, bool, 0644); MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device"); - -module_param(disable_tx, bool, 0644); -MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device"); -- cgit v1.2.3 From 06da95a3ec831dee23f6af58934944a8eb9c3a56 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 13 Jan 2011 02:00:33 -0300 Subject: [media] lirc_zilog: Split struct IR into structs IR, IR_tx, and IR_rx This change is a mostly mechanical break of the main struct IR data structure into common, Rx, and Tx structures. There were some small logical changes required as well, such as eliminating "is_hdpvr", to accomplish this. This change is an intiial step in reworking lirc_zilog to decouple the Rx and Tx handling as much as possible to fit with the new I2C binding model. This change actually makes lirc_zilog a little more broken than it already was - memory deallocation in particular got worse. However, this change makes the remaining problems easier to see and address. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 298 ++++++++++++++++++++++---------------- 1 file changed, 171 insertions(+), 127 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 91125336144e..85e312f33513 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -60,17 +60,9 @@ #include #include -struct IR { - struct lirc_driver l; - - /* Device info */ - struct mutex ir_lock; - int open; - bool is_hdpvr; - +struct IR_rx { /* RX device */ struct i2c_client c_rx; - int have_rx; /* RX device buffer & lock */ struct lirc_buffer buf; @@ -84,11 +76,26 @@ struct IR { /* RX read data */ unsigned char b[3]; + bool hdpvr_data_fmt; +}; +struct IR_tx { /* TX device */ struct i2c_client c_tx; + + /* TX additional actions needed */ int need_boot; - int have_tx; + bool post_tx_ready_poll; +}; + +struct IR { + struct lirc_driver l; + + struct mutex ir_lock; + int open; + + struct IR_rx *rx; + struct IR_tx *tx; }; /* Minor -> data mapping */ @@ -149,8 +156,12 @@ static int add_to_buf(struct IR *ir) int ret; int failures = 0; unsigned char sendbuf[1] = { 0 }; + struct IR_rx *rx = ir->rx; - if (lirc_buffer_full(&ir->buf)) { + if (rx == NULL) + return -ENXIO; + + if (lirc_buffer_full(&rx->buf)) { dprintk("buffer overflow\n"); return -EOVERFLOW; } @@ -170,7 +181,7 @@ static int add_to_buf(struct IR *ir) * Send random "poll command" (?) Windows driver does this * and it is a good point to detect chip failure. */ - ret = i2c_master_send(&ir->c_rx, sendbuf, 1); + ret = i2c_master_send(&rx->c_rx, sendbuf, 1); if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); if (failures >= 3) { @@ -186,44 +197,45 @@ static int add_to_buf(struct IR *ir) set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ + 999) / 1000); - ir->need_boot = 1; + if (ir->tx != NULL) + ir->tx->need_boot = 1; ++failures; mutex_unlock(&ir->ir_lock); continue; } - ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf)); + ret = i2c_master_recv(&rx->c_rx, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); if (ret != sizeof(keybuf)) { zilog_error("i2c_master_recv failed with %d -- " "keeping last read buffer\n", ret); } else { - ir->b[0] = keybuf[3]; - ir->b[1] = keybuf[4]; - ir->b[2] = keybuf[5]; - dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]); + rx->b[0] = keybuf[3]; + rx->b[1] = keybuf[4]; + rx->b[2] = keybuf[5]; + dprintk("key (0x%02x/0x%02x)\n", rx->b[0], rx->b[1]); } /* key pressed ? */ - if (ir->is_hdpvr) { + if (rx->hdpvr_data_fmt) { if (got_data && (keybuf[0] == 0x80)) return 0; else if (got_data && (keybuf[0] == 0x00)) return -ENODATA; - } else if ((ir->b[0] & 0x80) == 0) + } else if ((rx->b[0] & 0x80) == 0) return got_data ? 0 : -ENODATA; /* look what we have */ - code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2); + code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2); codes[0] = (code >> 8) & 0xff; codes[1] = code & 0xff; /* return it */ - lirc_buffer_write(&ir->buf, codes); + lirc_buffer_write(&rx->buf, codes); ++got_data; - } while (!lirc_buffer_full(&ir->buf)); + } while (!lirc_buffer_full(&rx->buf)); return 0; } @@ -241,9 +253,13 @@ static int add_to_buf(struct IR *ir) static int lirc_thread(void *arg) { struct IR *ir = arg; + struct IR_rx *rx = ir->rx; + + if (rx == NULL) + return -ENXIO; - if (ir->t_notify != NULL) - complete(ir->t_notify); + if (rx->t_notify != NULL) + complete(rx->t_notify); dprintk("poll thread started\n"); @@ -264,23 +280,23 @@ static int lirc_thread(void *arg) * lost keypresses. */ schedule_timeout((260 * HZ) / 1000); - if (ir->shutdown) + if (rx->shutdown) break; if (!add_to_buf(ir)) - wake_up_interruptible(&ir->buf.wait_poll); + wake_up_interruptible(&rx->buf.wait_poll); } else { /* if device not opened so we can sleep half a second */ set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ/2); } - } while (!ir->shutdown); + } while (!rx->shutdown); - if (ir->t_notify2 != NULL) - wait_for_completion(ir->t_notify2); + if (rx->t_notify2 != NULL) + wait_for_completion(rx->t_notify2); - ir->task = NULL; - if (ir->t_notify != NULL) - complete(ir->t_notify); + rx->task = NULL; + if (rx->t_notify != NULL) + complete(rx->t_notify); dprintk("poll thread ended\n"); return 0; @@ -298,10 +314,10 @@ static int set_use_inc(void *data) * this is completely broken code. lirc_unregister_driver() * must be possible even when the device is open */ - if (ir->c_rx.addr) - i2c_use_client(&ir->c_rx); - if (ir->c_tx.addr) - i2c_use_client(&ir->c_tx); + if (ir->rx != NULL) + i2c_use_client(&ir->rx->c_rx); + if (ir->tx != NULL) + i2c_use_client(&ir->tx->c_tx); return 0; } @@ -310,10 +326,10 @@ static void set_use_dec(void *data) { struct IR *ir = data; - if (ir->c_rx.addr) - i2c_release_client(&ir->c_rx); - if (ir->c_tx.addr) - i2c_release_client(&ir->c_tx); + if (ir->rx) + i2c_release_client(&ir->rx->c_rx); + if (ir->tx) + i2c_release_client(&ir->tx->c_tx); if (ir->l.owner != NULL) module_put(ir->l.owner); } @@ -452,7 +468,7 @@ corrupt: } /* send a block of data to the IR TX device */ -static int send_data_block(struct IR *ir, unsigned char *data_block) +static int send_data_block(struct IR_tx *tx, unsigned char *data_block) { int i, j, ret; unsigned char buf[5]; @@ -466,7 +482,7 @@ static int send_data_block(struct IR *ir, unsigned char *data_block) buf[1 + j] = data_block[i + j]; dprintk("%02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4]); - ret = i2c_master_send(&ir->c_tx, buf, tosend + 1); + ret = i2c_master_send(&tx->c_tx, buf, tosend + 1); if (ret != tosend + 1) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; @@ -477,32 +493,32 @@ static int send_data_block(struct IR *ir, unsigned char *data_block) } /* send boot data to the IR TX device */ -static int send_boot_data(struct IR *ir) +static int send_boot_data(struct IR_tx *tx) { int ret; unsigned char buf[4]; /* send the boot block */ - ret = send_data_block(ir, tx_data->boot_data); + ret = send_data_block(tx, tx_data->boot_data); if (ret != 0) return ret; /* kick it off? */ buf[0] = 0x00; buf[1] = 0x20; - ret = i2c_master_send(&ir->c_tx, buf, 2); + ret = i2c_master_send(&tx->c_tx, buf, 2); if (ret != 2) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } - ret = i2c_master_send(&ir->c_tx, buf, 1); + ret = i2c_master_send(&tx->c_tx, buf, 1); if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } /* Here comes the firmware version... (hopefully) */ - ret = i2c_master_recv(&ir->c_tx, buf, 4); + ret = i2c_master_recv(&tx->c_tx, buf, 4); if (ret != 4) { zilog_error("i2c_master_recv failed with %d\n", ret); return 0; @@ -542,7 +558,7 @@ static void fw_unload(void) } /* load "firmware" for the IR TX device */ -static int fw_load(struct IR *ir) +static int fw_load(struct IR_tx *tx) { int ret; unsigned int i; @@ -557,7 +573,7 @@ static int fw_load(struct IR *ir) } /* Request codeset data file */ - ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev); + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &tx->c_tx.dev); if (ret != 0) { zilog_error("firmware haup-ir-blaster.bin not available " "(%d)\n", ret); @@ -684,20 +700,20 @@ out: } /* initialise the IR TX device */ -static int tx_init(struct IR *ir) +static int tx_init(struct IR_tx *tx) { int ret; /* Load 'firmware' */ - ret = fw_load(ir); + ret = fw_load(tx); if (ret != 0) return ret; /* Send boot block */ - ret = send_boot_data(ir); + ret = send_boot_data(tx); if (ret != 0) return ret; - ir->need_boot = 0; + tx->need_boot = 0; /* Looks good */ return 0; @@ -713,20 +729,20 @@ static loff_t lseek(struct file *filep, loff_t offset, int orig) static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; - unsigned char buf[ir->buf.chunk_size]; + struct IR_rx *rx = ir->rx; int ret = 0, written = 0; DECLARE_WAITQUEUE(wait, current); dprintk("read called\n"); - if (ir->c_rx.addr == 0) + if (rx == NULL) return -ENODEV; - if (mutex_lock_interruptible(&ir->buf_lock)) + if (mutex_lock_interruptible(&rx->buf_lock)) return -ERESTARTSYS; - if (n % ir->buf.chunk_size) { + if (n % rx->buf.chunk_size) { dprintk("read result = -EINVAL\n"); - mutex_unlock(&ir->buf_lock); + mutex_unlock(&rx->buf_lock); return -EINVAL; } @@ -735,7 +751,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) * to avoid losing scan code (in case when queue is awaken somewhere * between while condition checking and scheduling) */ - add_wait_queue(&ir->buf.wait_poll, &wait); + add_wait_queue(&rx->buf.wait_poll, &wait); set_current_state(TASK_INTERRUPTIBLE); /* @@ -743,7 +759,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) * mode and 'copy_to_user' is happy, wait for data. */ while (written < n && ret == 0) { - if (lirc_buffer_empty(&ir->buf)) { + if (lirc_buffer_empty(&rx->buf)) { /* * According to the read(2) man page, 'written' can be * returned as less than 'n', instead of blocking @@ -763,16 +779,17 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) schedule(); set_current_state(TASK_INTERRUPTIBLE); } else { - lirc_buffer_read(&ir->buf, buf); + unsigned char buf[rx->buf.chunk_size]; + lirc_buffer_read(&rx->buf, buf); ret = copy_to_user((void *)outbuf+written, buf, - ir->buf.chunk_size); - written += ir->buf.chunk_size; + rx->buf.chunk_size); + written += rx->buf.chunk_size; } } - remove_wait_queue(&ir->buf.wait_poll, &wait); + remove_wait_queue(&rx->buf.wait_poll, &wait); set_current_state(TASK_RUNNING); - mutex_unlock(&ir->buf_lock); + mutex_unlock(&rx->buf_lock); dprintk("read result = %s (%d)\n", ret ? "-EFAULT" : "OK", ret); @@ -781,7 +798,7 @@ static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) } /* send a keypress to the IR TX device */ -static int send_code(struct IR *ir, unsigned int code, unsigned int key) +static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) { unsigned char data_block[TX_BLOCK_SIZE]; unsigned char buf[2]; @@ -798,26 +815,26 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key) return ret; /* Send the data block */ - ret = send_data_block(ir, data_block); + ret = send_data_block(tx, data_block); if (ret != 0) return ret; /* Send data block length? */ buf[0] = 0x00; buf[1] = 0x40; - ret = i2c_master_send(&ir->c_tx, buf, 2); + ret = i2c_master_send(&tx->c_tx, buf, 2); if (ret != 2) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } - ret = i2c_master_send(&ir->c_tx, buf, 1); + ret = i2c_master_send(&tx->c_tx, buf, 1); if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } /* Send finished download? */ - ret = i2c_master_recv(&ir->c_tx, buf, 1); + ret = i2c_master_recv(&tx->c_tx, buf, 1); if (ret != 1) { zilog_error("i2c_master_recv failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; @@ -831,7 +848,7 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key) /* Send prepare command? */ buf[0] = 0x00; buf[1] = 0x80; - ret = i2c_master_send(&ir->c_tx, buf, 2); + ret = i2c_master_send(&tx->c_tx, buf, 2); if (ret != 2) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; @@ -842,7 +859,7 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key) * last i2c_master_recv always fails with a -5, so for now, we're * going to skip this whole mess and say we're done on the HD PVR */ - if (ir->is_hdpvr) { + if (!tx->post_tx_ready_poll) { dprintk("sent code %u, key %u\n", code, key); return 0; } @@ -856,7 +873,7 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key) for (i = 0; i < 20; ++i) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((50 * HZ + 999) / 1000); - ret = i2c_master_send(&ir->c_tx, buf, 1); + ret = i2c_master_send(&tx->c_tx, buf, 1); if (ret == 1) break; dprintk("NAK expected: i2c_master_send " @@ -869,7 +886,7 @@ static int send_code(struct IR *ir, unsigned int code, unsigned int key) } /* Seems to be an 'ok' response */ - i = i2c_master_recv(&ir->c_tx, buf, 1); + i = i2c_master_recv(&tx->c_tx, buf, 1); if (i != 1) { zilog_error("i2c_master_recv failed with %d\n", ret); return -EFAULT; @@ -894,10 +911,11 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, loff_t *ppos) { struct IR *ir = filep->private_data; + struct IR_tx *tx = ir->tx; size_t i; int failures = 0; - if (ir->c_tx.addr == 0) + if (tx == NULL) return -ENODEV; /* Validate user parameters */ @@ -918,15 +936,15 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, } /* Send boot data first if required */ - if (ir->need_boot == 1) { - ret = send_boot_data(ir); + if (tx->need_boot == 1) { + ret = send_boot_data(tx); if (ret == 0) - ir->need_boot = 0; + tx->need_boot = 0; } /* Send the code */ if (ret == 0) { - ret = send_code(ir, (unsigned)command >> 16, + ret = send_code(tx, (unsigned)command >> 16, (unsigned)command & 0xFFFF); if (ret == -EPROTO) { mutex_unlock(&ir->ir_lock); @@ -951,7 +969,7 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, } set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((100 * HZ + 999) / 1000); - ir->need_boot = 1; + tx->need_boot = 1; ++failures; } else i += sizeof(int); @@ -968,22 +986,23 @@ static ssize_t write(struct file *filep, const char *buf, size_t n, static unsigned int poll(struct file *filep, poll_table *wait) { struct IR *ir = filep->private_data; + struct IR_rx *rx = ir->rx; unsigned int ret; dprintk("poll called\n"); - if (ir->c_rx.addr == 0) + if (rx == NULL) return -ENODEV; - mutex_lock(&ir->buf_lock); + mutex_lock(&rx->buf_lock); - poll_wait(filep, &ir->buf.wait_poll, wait); + poll_wait(filep, &rx->buf.wait_poll, wait); dprintk("poll result = %s\n", - lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM"); + lirc_buffer_empty(&rx->buf) ? "0" : "POLLIN|POLLRDNORM"); - ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM); + ret = lirc_buffer_empty(&rx->buf) ? 0 : (POLLIN|POLLRDNORM); - mutex_unlock(&ir->buf_lock); + mutex_unlock(&rx->buf_lock); return ret; } @@ -993,9 +1012,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) int result; unsigned long mode, features = 0; - if (ir->c_rx.addr != 0) + if (ir->rx != NULL) features |= LIRC_CAN_REC_LIRCCODE; - if (ir->c_tx.addr != 0) + if (ir->tx != NULL) features |= LIRC_CAN_SEND_PULSE; switch (cmd) { @@ -1146,23 +1165,26 @@ static const struct file_operations lirc_fops = { static int ir_remove(struct i2c_client *client) { struct IR *ir = i2c_get_clientdata(client); + struct IR_rx *rx = ir->rx; + struct IR_tx *tx = ir->tx; + /* FIXME make tx, rx senitive */ mutex_lock(&ir->ir_lock); - if (ir->have_rx || ir->have_tx) { + if (rx != NULL || tx != NULL) { DECLARE_COMPLETION(tn); DECLARE_COMPLETION(tn2); /* end up polling thread */ - if (ir->task && !IS_ERR(ir->task)) { - ir->t_notify = &tn; - ir->t_notify2 = &tn2; - ir->shutdown = 1; - wake_up_process(ir->task); + if (rx->task && !IS_ERR(rx->task)) { + rx->t_notify = &tn; + rx->t_notify2 = &tn2; + rx->shutdown = 1; + wake_up_process(rx->task); complete(&tn2); wait_for_completion(&tn); - ir->t_notify = NULL; - ir->t_notify2 = NULL; + rx->t_notify = NULL; + rx->t_notify2 = NULL; } } else { @@ -1173,13 +1195,15 @@ static int ir_remove(struct i2c_client *client) } /* unregister lirc driver */ + /* FIXME make tx, rx senitive */ if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { lirc_unregister_driver(ir->l.minor); ir_devices[ir->l.minor] = NULL; } /* free memory */ - lirc_buffer_free(&ir->buf); + /* FIXME make tx, rx senitive */ + lirc_buffer_free(&rx->buf); mutex_unlock(&ir->ir_lock); kfree(ir); @@ -1240,18 +1264,37 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) have_rx ? "RX only" : "TX only"); ir = kzalloc(sizeof(struct IR), GFP_KERNEL); - if (!ir) goto out_nomem; - ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2); - if (ret) - goto out_nomem; + if (have_tx) { + ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); + if (ir->tx != NULL) { + ir->tx->need_boot = 1; + ir->tx->post_tx_ready_poll = + (id->driver_data & ID_FLAG_HDPVR) ? false : true; + } + } + + if (have_rx) { + ir->rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); + + if (ir->rx == NULL) { + ret = -ENOMEM; + } else { + ir->rx->hdpvr_data_fmt = + (id->driver_data & ID_FLAG_HDPVR) ? true : false; + mutex_init(&ir->rx->buf_lock); + ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2); + } + + if (ret && (ir->rx != NULL)) { + kfree(ir->rx); + ir->rx = NULL; + } + } mutex_init(&ir->ir_lock); - mutex_init(&ir->buf_lock); - ir->need_boot = 1; - ir->is_hdpvr = (id->driver_data & ID_FLAG_HDPVR) ? true : false; memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); ir->l.minor = -1; @@ -1260,40 +1303,38 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) i2c_set_clientdata(client, ir); /* initialise RX device */ - if (have_rx) { + if (ir->rx != NULL) { DECLARE_COMPLETION(tn); - memcpy(&ir->c_rx, client, sizeof(struct i2c_client)); + memcpy(&ir->rx->c_rx, client, sizeof(struct i2c_client)); - ir->c_rx.addr = 0x71; - strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME, + ir->rx->c_rx.addr = 0x71; + strlcpy(ir->rx->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME, I2C_NAME_SIZE); /* try to fire up polling thread */ - ir->t_notify = &tn; - ir->task = kthread_run(lirc_thread, ir, "lirc_zilog"); - if (IS_ERR(ir->task)) { - ret = PTR_ERR(ir->task); + ir->rx->t_notify = &tn; + ir->rx->task = kthread_run(lirc_thread, ir, "lirc_zilog"); + if (IS_ERR(ir->rx->task)) { + ret = PTR_ERR(ir->rx->task); zilog_error("lirc_register_driver: cannot run " "poll thread %d\n", ret); goto err; } wait_for_completion(&tn); - ir->t_notify = NULL; - ir->have_rx = 1; + ir->rx->t_notify = NULL; } /* initialise TX device */ - if (have_tx) { - memcpy(&ir->c_tx, client, sizeof(struct i2c_client)); - ir->c_tx.addr = 0x70; - strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME, + if (ir->tx) { + memcpy(&ir->tx->c_tx, client, sizeof(struct i2c_client)); + ir->tx->c_tx.addr = 0x70; + strlcpy(ir->tx->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME, I2C_NAME_SIZE); - ir->have_tx = 1; } /* set lirc_dev stuff */ ir->l.code_length = 13; - ir->l.rbuf = &ir->buf; + ir->l.rbuf = (ir->rx == NULL) ? NULL : &ir->rx->buf; ir->l.fops = &lirc_fops; ir->l.data = ir; ir->l.minor = minor; @@ -1317,9 +1358,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * after registering with lirc as otherwise hotplug seems to take * 10s to create the lirc device. */ - if (have_tx) { + if (ir->tx != NULL) { /* Special TX init */ - ret = tx_init(ir); + ret = tx_init(ir->tx); if (ret != 0) goto err; } @@ -1327,18 +1368,21 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; err: + /* FIXME - memory deallocation for all error cases needs work */ /* undo everything, hopefully... */ - if (ir->c_rx.addr) - ir_remove(&ir->c_rx); - if (ir->c_tx.addr) - ir_remove(&ir->c_tx); + if (ir->rx != NULL) + ir_remove(&ir->rx->c_rx); + if (ir->tx != NULL) + ir_remove(&ir->tx->c_tx); return ret; out_nodev: + /* FIXME - memory deallocation for all error cases needs work */ zilog_error("no device found\n"); return -ENODEV; out_nomem: + /* FIXME - memory deallocation for all error cases needs work */ zilog_error("memory allocation failure\n"); kfree(ir); return -ENOMEM; -- cgit v1.2.3 From e9b351f64f5c02acf6552d2109bc213bfa133388 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 14 Jan 2011 21:11:22 -0300 Subject: [media] lirc_zilog: Don't make private copies of i2c clients Don't make private copies of the i2c clients provided by the I2C subsystem, don't change the client address field, and don't probe the client addresses - the bridge driver already did that. This moves us to the proper I2C and binding model. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 87 +++++++++++++-------------------------- 1 file changed, 28 insertions(+), 59 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 85e312f33513..00f2e7d3b3e5 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -62,7 +62,7 @@ struct IR_rx { /* RX device */ - struct i2c_client c_rx; + struct i2c_client *c; /* RX device buffer & lock */ struct lirc_buffer buf; @@ -81,7 +81,7 @@ struct IR_rx { struct IR_tx { /* TX device */ - struct i2c_client c_tx; + struct i2c_client *c; /* TX additional actions needed */ int need_boot; @@ -132,9 +132,6 @@ static struct mutex tx_data_lock; ## args) #define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) -#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX" -#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX" - /* module parameters */ static int debug; /* debug output */ static int disable_rx; /* disable RX device */ @@ -181,7 +178,7 @@ static int add_to_buf(struct IR *ir) * Send random "poll command" (?) Windows driver does this * and it is a good point to detect chip failure. */ - ret = i2c_master_send(&rx->c_rx, sendbuf, 1); + ret = i2c_master_send(rx->c, sendbuf, 1); if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); if (failures >= 3) { @@ -205,7 +202,7 @@ static int add_to_buf(struct IR *ir) continue; } - ret = i2c_master_recv(&rx->c_rx, keybuf, sizeof(keybuf)); + ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); if (ret != sizeof(keybuf)) { zilog_error("i2c_master_recv failed with %d -- " @@ -315,9 +312,9 @@ static int set_use_inc(void *data) * must be possible even when the device is open */ if (ir->rx != NULL) - i2c_use_client(&ir->rx->c_rx); + i2c_use_client(ir->rx->c); if (ir->tx != NULL) - i2c_use_client(&ir->tx->c_tx); + i2c_use_client(ir->tx->c); return 0; } @@ -327,9 +324,9 @@ static void set_use_dec(void *data) struct IR *ir = data; if (ir->rx) - i2c_release_client(&ir->rx->c_rx); + i2c_release_client(ir->rx->c); if (ir->tx) - i2c_release_client(&ir->tx->c_tx); + i2c_release_client(ir->tx->c); if (ir->l.owner != NULL) module_put(ir->l.owner); } @@ -482,7 +479,7 @@ static int send_data_block(struct IR_tx *tx, unsigned char *data_block) buf[1 + j] = data_block[i + j]; dprintk("%02x %02x %02x %02x %02x", buf[0], buf[1], buf[2], buf[3], buf[4]); - ret = i2c_master_send(&tx->c_tx, buf, tosend + 1); + ret = i2c_master_send(tx->c, buf, tosend + 1); if (ret != tosend + 1) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; @@ -506,19 +503,19 @@ static int send_boot_data(struct IR_tx *tx) /* kick it off? */ buf[0] = 0x00; buf[1] = 0x20; - ret = i2c_master_send(&tx->c_tx, buf, 2); + ret = i2c_master_send(tx->c, buf, 2); if (ret != 2) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } - ret = i2c_master_send(&tx->c_tx, buf, 1); + ret = i2c_master_send(tx->c, buf, 1); if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } /* Here comes the firmware version... (hopefully) */ - ret = i2c_master_recv(&tx->c_tx, buf, 4); + ret = i2c_master_recv(tx->c, buf, 4); if (ret != 4) { zilog_error("i2c_master_recv failed with %d\n", ret); return 0; @@ -573,7 +570,7 @@ static int fw_load(struct IR_tx *tx) } /* Request codeset data file */ - ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &tx->c_tx.dev); + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &tx->c->dev); if (ret != 0) { zilog_error("firmware haup-ir-blaster.bin not available " "(%d)\n", ret); @@ -822,19 +819,19 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) /* Send data block length? */ buf[0] = 0x00; buf[1] = 0x40; - ret = i2c_master_send(&tx->c_tx, buf, 2); + ret = i2c_master_send(tx->c, buf, 2); if (ret != 2) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } - ret = i2c_master_send(&tx->c_tx, buf, 1); + ret = i2c_master_send(tx->c, buf, 1); if (ret != 1) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } /* Send finished download? */ - ret = i2c_master_recv(&tx->c_tx, buf, 1); + ret = i2c_master_recv(tx->c, buf, 1); if (ret != 1) { zilog_error("i2c_master_recv failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; @@ -848,7 +845,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) /* Send prepare command? */ buf[0] = 0x00; buf[1] = 0x80; - ret = i2c_master_send(&tx->c_tx, buf, 2); + ret = i2c_master_send(tx->c, buf, 2); if (ret != 2) { zilog_error("i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; @@ -873,7 +870,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) for (i = 0; i < 20; ++i) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((50 * HZ + 999) / 1000); - ret = i2c_master_send(&tx->c_tx, buf, 1); + ret = i2c_master_send(tx->c, buf, 1); if (ret == 1) break; dprintk("NAK expected: i2c_master_send " @@ -886,7 +883,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) } /* Seems to be an 'ok' response */ - i = i2c_master_recv(&tx->c_tx, buf, 1); + i = i2c_master_recv(tx->c, buf, 1); if (i != 1) { zilog_error("i2c_master_recv failed with %d\n", ret); return -EFAULT; @@ -1214,7 +1211,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct IR *ir = NULL; struct i2c_adapter *adap = client->adapter; - char buf; int ret; int have_rx = 0, have_tx = 0; @@ -1239,24 +1235,13 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * The external IR receiver is at i2c address 0x71. * The IR transmitter is at 0x70. */ - client->addr = 0x70; - if (i2c_master_recv(client, &buf, 1) == 1) + if (id->driver_data & ID_FLAG_TX) { have_tx = 1; - dprintk("probe 0x70 @ %s: %s\n", - adap->name, have_tx ? "success" : "failed"); - - if (!disable_rx) { - client->addr = 0x71; - if (i2c_master_recv(client, &buf, 1) == 1) - have_rx = 1; - dprintk("probe 0x71 @ %s: %s\n", - adap->name, have_rx ? "success" : "failed"); - } - - if (!(have_rx || have_tx)) { - zilog_error("%s: no devices found\n", adap->name); - goto out_nodev; + } else if (!disable_rx) { + have_rx = 1; + } else { + return -ENXIO; } printk(KERN_INFO "lirc_zilog: chip found with %s\n", @@ -1270,6 +1255,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) if (have_tx) { ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); if (ir->tx != NULL) { + ir->tx->c = client; ir->tx->need_boot = 1; ir->tx->post_tx_ready_poll = (id->driver_data & ID_FLAG_HDPVR) ? false : true; @@ -1282,6 +1268,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) if (ir->rx == NULL) { ret = -ENOMEM; } else { + ir->rx->c = client; ir->rx->hdpvr_data_fmt = (id->driver_data & ID_FLAG_HDPVR) ? true : false; mutex_init(&ir->rx->buf_lock); @@ -1305,11 +1292,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* initialise RX device */ if (ir->rx != NULL) { DECLARE_COMPLETION(tn); - memcpy(&ir->rx->c_rx, client, sizeof(struct i2c_client)); - - ir->rx->c_rx.addr = 0x71; - strlcpy(ir->rx->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME, - I2C_NAME_SIZE); /* try to fire up polling thread */ ir->rx->t_notify = &tn; @@ -1324,14 +1306,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->rx->t_notify = NULL; } - /* initialise TX device */ - if (ir->tx) { - memcpy(&ir->tx->c_tx, client, sizeof(struct i2c_client)); - ir->tx->c_tx.addr = 0x70; - strlcpy(ir->tx->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME, - I2C_NAME_SIZE); - } - /* set lirc_dev stuff */ ir->l.code_length = 13; ir->l.rbuf = (ir->rx == NULL) ? NULL : &ir->rx->buf; @@ -1371,16 +1345,11 @@ err: /* FIXME - memory deallocation for all error cases needs work */ /* undo everything, hopefully... */ if (ir->rx != NULL) - ir_remove(&ir->rx->c_rx); + ir_remove(ir->rx->c); if (ir->tx != NULL) - ir_remove(&ir->tx->c_tx); + ir_remove(ir->tx->c); return ret; -out_nodev: - /* FIXME - memory deallocation for all error cases needs work */ - zilog_error("no device found\n"); - return -ENODEV; - out_nomem: /* FIXME - memory deallocation for all error cases needs work */ zilog_error("memory allocation failure\n"); -- cgit v1.2.3 From a68a9b73fbb05144a71b573f262fbc8ed8f71179 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 15 Jan 2011 01:04:06 -0300 Subject: [media] lirc_zilog: Extensive rework of ir_probe()/ir_remove() This patch is an extensive rework of the ir_probe() and ir_remove() functions. It removes all the double binding and allocation problems on module load. It removes almost all the memory leaks on module exit and on device instantiation failure. Proper destruction of the Rx polling kthread still needs investigation and more work, but it is no worse than it already was. This rework also had side effects that include: - encapsulation of the ir_devices[] array - serialization of access to the ir_devices[] array - semantic change of the module parameter "disable_rx" to "tx_only" If tx_only is true, the module does not claim the i2c_client for the IR Rx function, and only claims and handles the i2c_client for the IR Tx function. This is a first step in providing the option of letting ir-kbd-i2c.c handle IR Rx function, while lirc_zilog handles the IR Tx function. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 344 +++++++++++++++++++++++--------------- 1 file changed, 212 insertions(+), 132 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 00f2e7d3b3e5..f2e8c63fa5bd 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -94,11 +94,13 @@ struct IR { struct mutex ir_lock; int open; + struct i2c_adapter *adapter; struct IR_rx *rx; struct IR_tx *tx; }; /* Minor -> data mapping */ +static struct mutex ir_devices_lock; static struct IR *ir_devices[MAX_IRCTL_DEVICES]; /* Block size for IR transmitter */ @@ -131,10 +133,11 @@ static struct mutex tx_data_lock; #define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \ ## args) #define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) +#define zilog_info(s, args...) printk(KERN_INFO KBUILD_MODNAME ": " s, ## args) /* module parameters */ static int debug; /* debug output */ -static int disable_rx; /* disable RX device */ +static int tx_only; /* only handle the IR Tx function */ static int minor = -1; /* minor number */ #define dprintk(fmt, args...) \ @@ -252,9 +255,6 @@ static int lirc_thread(void *arg) struct IR *ir = arg; struct IR_rx *rx = ir->rx; - if (rx == NULL) - return -ENXIO; - if (rx->t_notify != NULL) complete(rx->t_notify); @@ -296,6 +296,7 @@ static int lirc_thread(void *arg) complete(rx->t_notify); dprintk("poll thread ended\n"); + /* FIXME - investigate if this is the proper way to shutdown a kthread*/ return 0; } @@ -1058,6 +1059,15 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) return result; } +/* ir_devices_lock must be held */ +static struct IR *find_ir_device_by_minor(unsigned int minor) +{ + if (minor >= MAX_IRCTL_DEVICES) + return NULL; + + return ir_devices[minor]; +} + /* * Open the IR device. Get hold of our IR structure and * stash it in private_data for the file @@ -1066,15 +1076,15 @@ static int open(struct inode *node, struct file *filep) { struct IR *ir; int ret; + unsigned int minor = MINOR(node->i_rdev); /* find our IR struct */ - unsigned minor = MINOR(node->i_rdev); - if (minor >= MAX_IRCTL_DEVICES) { - dprintk("minor %d: open result = -ENODEV\n", - minor); + mutex_lock(&ir_devices_lock); + ir = find_ir_device_by_minor(minor); + mutex_unlock(&ir_devices_lock); + + if (ir == NULL) return -ENODEV; - } - ir = ir_devices[minor]; /* increment in use count */ mutex_lock(&ir->ir_lock); @@ -1159,136 +1169,203 @@ static const struct file_operations lirc_fops = { .release = close }; -static int ir_remove(struct i2c_client *client) +/* FIXME - investigate if this is the proper way to shutdown a kthread */ +static void destroy_rx_kthread(struct IR_rx *rx) { - struct IR *ir = i2c_get_clientdata(client); - struct IR_rx *rx = ir->rx; - struct IR_tx *tx = ir->tx; + DECLARE_COMPLETION(tn); + DECLARE_COMPLETION(tn2); - /* FIXME make tx, rx senitive */ - mutex_lock(&ir->ir_lock); + if (rx == NULL) + return; + + /* end up polling thread */ + if (rx->task && !IS_ERR(rx->task)) { + rx->t_notify = &tn; + rx->t_notify2 = &tn2; + rx->shutdown = 1; + wake_up_process(rx->task); + complete(&tn2); + wait_for_completion(&tn); + rx->t_notify = NULL; + rx->t_notify2 = NULL; + } +} - if (rx != NULL || tx != NULL) { - DECLARE_COMPLETION(tn); - DECLARE_COMPLETION(tn2); - - /* end up polling thread */ - if (rx->task && !IS_ERR(rx->task)) { - rx->t_notify = &tn; - rx->t_notify2 = &tn2; - rx->shutdown = 1; - wake_up_process(rx->task); - complete(&tn2); - wait_for_completion(&tn); - rx->t_notify = NULL; - rx->t_notify2 = NULL; +/* ir_devices_lock must be held */ +static int add_ir_device(struct IR *ir) +{ + int i; + + for (i = 0; i < MAX_IRCTL_DEVICES; i++) + if (ir_devices[i] == NULL) { + ir_devices[i] = ir; + break; } - } else { - mutex_unlock(&ir->ir_lock); - zilog_error("%s: detached from something we didn't " - "attach to\n", __func__); - return -ENODEV; + return i == MAX_IRCTL_DEVICES ? -ENOMEM : i; +} + +/* ir_devices_lock must be held */ +static void del_ir_device(struct IR *ir) +{ + int i; + + for (i = 0; i < MAX_IRCTL_DEVICES; i++) + if (ir_devices[i] == ir) { + ir_devices[i] = NULL; + break; + } +} + +static int ir_remove(struct i2c_client *client) +{ + struct IR *ir = i2c_get_clientdata(client); + + mutex_lock(&ir_devices_lock); + + if (ir == NULL) { + /* We destroyed everything when the first client came through */ + mutex_unlock(&ir_devices_lock); + return 0; } - /* unregister lirc driver */ - /* FIXME make tx, rx senitive */ - if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { - lirc_unregister_driver(ir->l.minor); - ir_devices[ir->l.minor] = NULL; + /* Good-bye LIRC */ + lirc_unregister_driver(ir->l.minor); + + /* Good-bye Rx */ + destroy_rx_kthread(ir->rx); + if (ir->rx != NULL) { + if (ir->rx->buf.fifo_initialized) + lirc_buffer_free(&ir->rx->buf); + i2c_set_clientdata(ir->rx->c, NULL); + kfree(ir->rx); } - /* free memory */ - /* FIXME make tx, rx senitive */ - lirc_buffer_free(&rx->buf); - mutex_unlock(&ir->ir_lock); + /* Good-bye Tx */ + i2c_set_clientdata(ir->tx->c, NULL); + kfree(ir->tx); + + /* Good-bye IR */ + del_ir_device(ir); kfree(ir); + mutex_unlock(&ir_devices_lock); return 0; } -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) + +/* ir_devices_lock must be held */ +static struct IR *find_ir_device_by_adapter(struct i2c_adapter *adapter) { + int i; struct IR *ir = NULL; + + for (i = 0; i < MAX_IRCTL_DEVICES; i++) + if (ir_devices[i] != NULL && + ir_devices[i]->adapter == adapter) { + ir = ir_devices[i]; + break; + } + + return ir; +} + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct IR *ir; struct i2c_adapter *adap = client->adapter; int ret; - int have_rx = 0, have_tx = 0; + bool tx_probe = false; dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n", __func__, id->name, adap->nr, adap->name, client->addr); /* - * FIXME - This probe function probes both the Tx and Rx - * addresses of the IR microcontroller. - * - * However, the I2C subsystem is passing along one I2C client at a - * time, based on matches to the ir_transceiver_id[] table above. - * The expectation is that each i2c_client address will be probed - * individually by drivers so the I2C subsystem can mark all client - * addresses as claimed or not. - * - * This probe routine causes only one of the client addresses, TX or RX, - * to be claimed. This will cause a problem if the I2C subsystem is - * subsequently triggered to probe unclaimed clients again. - */ - /* - * The external IR receiver is at i2c address 0x71. - * The IR transmitter is at 0x70. + * The IR receiver is at i2c address 0x71. + * The IR transmitter is at i2c address 0x70. */ - if (id->driver_data & ID_FLAG_TX) { - have_tx = 1; - } else if (!disable_rx) { - have_rx = 1; - } else { + if (id->driver_data & ID_FLAG_TX) + tx_probe = true; + else if (tx_only) /* module option */ return -ENXIO; - } - printk(KERN_INFO "lirc_zilog: chip found with %s\n", - have_rx && have_tx ? "RX and TX" : - have_rx ? "RX only" : "TX only"); + zilog_info("%s: probing IR %s on %s (i2c-%d)\n", + __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr); - ir = kzalloc(sizeof(struct IR), GFP_KERNEL); - if (!ir) - goto out_nomem; + mutex_lock(&ir_devices_lock); - if (have_tx) { - ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); - if (ir->tx != NULL) { - ir->tx->c = client; - ir->tx->need_boot = 1; - ir->tx->post_tx_ready_poll = - (id->driver_data & ID_FLAG_HDPVR) ? false : true; + /* Use a single struct IR instance for both the Rx and Tx functions */ + ir = find_ir_device_by_adapter(adap); + if (ir == NULL) { + ir = kzalloc(sizeof(struct IR), GFP_KERNEL); + if (ir == NULL) { + ret = -ENOMEM; + goto out_no_ir; } + /* store for use in ir_probe() again, and open() later on */ + ret = add_ir_device(ir); + if (ret) + goto out_free_ir; + + ir->adapter = adap; + mutex_init(&ir->ir_lock); + + /* set lirc_dev stuff */ + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); + ir->l.minor = minor; /* module option */ + ir->l.code_length = 13; + ir->l.rbuf = NULL; + ir->l.fops = &lirc_fops; + ir->l.data = ir; + ir->l.dev = &adap->dev; + ir->l.sample_rate = 0; } - if (have_rx) { - ir->rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); + if (tx_probe) { + /* Set up a struct IR_tx instance */ + ir->tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); + if (ir->tx == NULL) { + ret = -ENOMEM; + goto out_free_xx; + } + ir->tx->c = client; + ir->tx->need_boot = 1; + ir->tx->post_tx_ready_poll = + (id->driver_data & ID_FLAG_HDPVR) ? false : true; + } else { + /* Set up a struct IR_rx instance */ + ir->rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); if (ir->rx == NULL) { ret = -ENOMEM; - } else { - ir->rx->c = client; - ir->rx->hdpvr_data_fmt = - (id->driver_data & ID_FLAG_HDPVR) ? true : false; - mutex_init(&ir->rx->buf_lock); - ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2); + goto out_free_xx; } - if (ret && (ir->rx != NULL)) { - kfree(ir->rx); - ir->rx = NULL; - } - } + ret = lirc_buffer_init(&ir->rx->buf, 2, BUFLEN / 2); + if (ret) + goto out_free_xx; - mutex_init(&ir->ir_lock); + mutex_init(&ir->rx->buf_lock); + ir->rx->c = client; + ir->rx->hdpvr_data_fmt = + (id->driver_data & ID_FLAG_HDPVR) ? true : false; - memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); - ir->l.minor = -1; + /* set lirc_dev stuff */ + ir->l.rbuf = &ir->rx->buf; + } - /* I2C attach to device */ i2c_set_clientdata(client, ir); + /* Proceed only if we have the required Tx and Rx clients ready to go */ + if (ir->tx == NULL || + (ir->rx == NULL && !tx_only)) { + zilog_info("%s: probe of IR %s on %s (i2c-%d) done, waiting on " + "IR %s\n", __func__, tx_probe ? "Tx" : "Rx", + adap->name, adap->nr, tx_probe ? "Rx" : "Tx"); + goto out_ok; + } + /* initialise RX device */ if (ir->rx != NULL) { DECLARE_COMPLETION(tn); @@ -1298,35 +1375,23 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->rx->task = kthread_run(lirc_thread, ir, "lirc_zilog"); if (IS_ERR(ir->rx->task)) { ret = PTR_ERR(ir->rx->task); - zilog_error("lirc_register_driver: cannot run " - "poll thread %d\n", ret); - goto err; + zilog_error("%s: could not start IR Rx polling thread" + "\n", __func__); + goto out_free_xx; } wait_for_completion(&tn); ir->rx->t_notify = NULL; } - /* set lirc_dev stuff */ - ir->l.code_length = 13; - ir->l.rbuf = (ir->rx == NULL) ? NULL : &ir->rx->buf; - ir->l.fops = &lirc_fops; - ir->l.data = ir; - ir->l.minor = minor; - ir->l.dev = &adap->dev; - ir->l.sample_rate = 0; - /* register with lirc */ ir->l.minor = lirc_register_driver(&ir->l); if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { - zilog_error("ir_attach: \"minor\" must be between 0 and %d " - "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor); + zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n", + __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); ret = -EBADRQC; - goto err; + goto out_free_thread; } - /* store this for getting back in open() later on */ - ir_devices[ir->l.minor] = ir; - /* * if we have the tx device, load the 'firmware'. We do this * after registering with lirc as otherwise hotplug seems to take @@ -1336,25 +1401,39 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Special TX init */ ret = tx_init(ir->tx); if (ret != 0) - goto err; + goto out_unregister; } +out_ok: + mutex_unlock(&ir_devices_lock); return 0; -err: - /* FIXME - memory deallocation for all error cases needs work */ - /* undo everything, hopefully... */ - if (ir->rx != NULL) - ir_remove(ir->rx->c); - if (ir->tx != NULL) - ir_remove(ir->tx->c); - return ret; - -out_nomem: - /* FIXME - memory deallocation for all error cases needs work */ - zilog_error("memory allocation failure\n"); +out_unregister: + lirc_unregister_driver(ir->l.minor); +out_free_thread: + destroy_rx_kthread(ir->rx); +out_free_xx: + if (ir->rx != NULL) { + if (ir->rx->buf.fifo_initialized) + lirc_buffer_free(&ir->rx->buf); + if (ir->rx->c != NULL) + i2c_set_clientdata(ir->rx->c, NULL); + kfree(ir->rx); + } + if (ir->tx != NULL) { + if (ir->tx->c != NULL) + i2c_set_clientdata(ir->tx->c, NULL); + kfree(ir->tx); + } +out_free_ir: + del_ir_device(ir); kfree(ir); - return -ENOMEM; +out_no_ir: + zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n", + __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, + ret); + mutex_unlock(&ir_devices_lock); + return ret; } static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg) @@ -1370,6 +1449,7 @@ static int __init zilog_init(void) zilog_notify("Zilog/Hauppauge IR driver initializing\n"); mutex_init(&tx_data_lock); + mutex_init(&ir_devices_lock); request_module("firmware_class"); @@ -1406,5 +1486,5 @@ MODULE_PARM_DESC(minor, "Preferred minor device number"); module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Enable debugging messages"); -module_param(disable_rx, bool, 0644); -MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device"); +module_param(tx_only, bool, 0644); +MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function"); -- cgit v1.2.3 From b757730b022b4b1367d0435fcaa1b0a01e8aef42 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 15 Jan 2011 22:02:05 -0300 Subject: [media] lirc_zilog: Update IR Rx polling kthread start/stop and some printks The IR Rx polling thread was originally a kernel_thread long ago, and had only been minimally converted to a kthread. This patch finishes that conversion by - cleaning up all the unneeded completions - destroying the kthread properly by calling kthread_stop() - changing lirc_thread() to test kthread_should_stop() just before every point where it may sleep - reorganizing the lirc_thread() function so it uses fewer lines - modifying the name of the kthread from "lirc_zilog" to "zilog-rx-i2c-N", so ps will show which kthread polls which Zilog Z8 IR unit. Also some minor tweaks were made to logging emitted by the ir_probe() function. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 113 +++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 64 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index f2e8c63fa5bd..f7aa5e4e98b3 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -69,9 +69,6 @@ struct IR_rx { struct mutex buf_lock; /* RX polling thread data */ - struct completion *t_notify; - struct completion *t_notify2; - int shutdown; struct task_struct *task; /* RX read data */ @@ -171,12 +168,20 @@ static int add_to_buf(struct IR *ir) * data and we have space */ do { + if (kthread_should_stop()) + return -ENODATA; + /* * Lock i2c bus for the duration. RX/TX chips interfere so * this is worth it */ mutex_lock(&ir->ir_lock); + if (kthread_should_stop()) { + mutex_unlock(&ir->ir_lock); + return -ENODATA; + } + /* * Send random "poll command" (?) Windows driver does this * and it is a good point to detect chip failure. @@ -196,6 +201,10 @@ static int add_to_buf(struct IR *ir) "trying reset\n"); set_current_state(TASK_UNINTERRUPTIBLE); + if (kthread_should_stop()) { + mutex_unlock(&ir->ir_lock); + return -ENODATA; + } schedule_timeout((100 * HZ + 999) / 1000); if (ir->tx != NULL) ir->tx->need_boot = 1; @@ -205,6 +214,10 @@ static int add_to_buf(struct IR *ir) continue; } + if (kthread_should_stop()) { + mutex_unlock(&ir->ir_lock); + return -ENODATA; + } ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); if (ret != sizeof(keybuf)) { @@ -255,48 +268,35 @@ static int lirc_thread(void *arg) struct IR *ir = arg; struct IR_rx *rx = ir->rx; - if (rx->t_notify != NULL) - complete(rx->t_notify); - dprintk("poll thread started\n"); - do { - if (ir->open) { - set_current_state(TASK_INTERRUPTIBLE); + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); - /* - * This is ~113*2 + 24 + jitter (2*repeat gap + - * code length). We use this interval as the chip - * resets every time you poll it (bad!). This is - * therefore just sufficient to catch all of the - * button presses. It makes the remote much more - * responsive. You can see the difference by - * running irw and holding down a button. With - * 100ms, the old polling interval, you'll notice - * breaks in the repeat sequence corresponding to - * lost keypresses. - */ - schedule_timeout((260 * HZ) / 1000); - if (rx->shutdown) - break; - if (!add_to_buf(ir)) - wake_up_interruptible(&rx->buf.wait_poll); - } else { - /* if device not opened so we can sleep half a second */ - set_current_state(TASK_INTERRUPTIBLE); + /* if device not opened, we can sleep half a second */ + if (!ir->open) { schedule_timeout(HZ/2); + continue; } - } while (!rx->shutdown); - if (rx->t_notify2 != NULL) - wait_for_completion(rx->t_notify2); - - rx->task = NULL; - if (rx->t_notify != NULL) - complete(rx->t_notify); + /* + * This is ~113*2 + 24 + jitter (2*repeat gap + code length). + * We use this interval as the chip resets every time you poll + * it (bad!). This is therefore just sufficient to catch all + * of the button presses. It makes the remote much more + * responsive. You can see the difference by running irw and + * holding down a button. With 100ms, the old polling + * interval, you'll notice breaks in the repeat sequence + * corresponding to lost keypresses. + */ + schedule_timeout((260 * HZ) / 1000); + if (kthread_should_stop()) + break; + if (!add_to_buf(ir)) + wake_up_interruptible(&rx->buf.wait_poll); + } dprintk("poll thread ended\n"); - /* FIXME - investigate if this is the proper way to shutdown a kthread*/ return 0; } @@ -1169,25 +1169,12 @@ static const struct file_operations lirc_fops = { .release = close }; -/* FIXME - investigate if this is the proper way to shutdown a kthread */ static void destroy_rx_kthread(struct IR_rx *rx) { - DECLARE_COMPLETION(tn); - DECLARE_COMPLETION(tn2); - - if (rx == NULL) - return; - /* end up polling thread */ - if (rx->task && !IS_ERR(rx->task)) { - rx->t_notify = &tn; - rx->t_notify2 = &tn2; - rx->shutdown = 1; - wake_up_process(rx->task); - complete(&tn2); - wait_for_completion(&tn); - rx->t_notify = NULL; - rx->t_notify2 = NULL; + if (rx != NULL && !IS_ERR_OR_NULL(rx->task)) { + kthread_stop(rx->task); + rx->task = NULL; } } @@ -1290,8 +1277,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) else if (tx_only) /* module option */ return -ENXIO; - zilog_info("%s: probing IR %s on %s (i2c-%d)\n", - __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr); + zilog_info("probing IR %s on %s (i2c-%d)\n", + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); mutex_lock(&ir_devices_lock); @@ -1360,27 +1347,23 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Proceed only if we have the required Tx and Rx clients ready to go */ if (ir->tx == NULL || (ir->rx == NULL && !tx_only)) { - zilog_info("%s: probe of IR %s on %s (i2c-%d) done, waiting on " - "IR %s\n", __func__, tx_probe ? "Tx" : "Rx", - adap->name, adap->nr, tx_probe ? "Rx" : "Tx"); + zilog_info("probe of IR %s on %s (i2c-%d) done. Waiting on " + "IR %s.\n", tx_probe ? "Tx" : "Rx", adap->name, + adap->nr, tx_probe ? "Rx" : "Tx"); goto out_ok; } /* initialise RX device */ if (ir->rx != NULL) { - DECLARE_COMPLETION(tn); - /* try to fire up polling thread */ - ir->rx->t_notify = &tn; - ir->rx->task = kthread_run(lirc_thread, ir, "lirc_zilog"); + ir->rx->task = kthread_run(lirc_thread, ir, + "zilog-rx-i2c-%d", adap->nr); if (IS_ERR(ir->rx->task)) { ret = PTR_ERR(ir->rx->task); zilog_error("%s: could not start IR Rx polling thread" "\n", __func__); goto out_free_xx; } - wait_for_completion(&tn); - ir->rx->t_notify = NULL; } /* register with lirc */ @@ -1404,6 +1387,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_unregister; } + zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); out_ok: mutex_unlock(&ir_devices_lock); return 0; -- cgit v1.2.3 From 8090232a237ab62e22307fc060097da1a283dd66 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 15 Jan 2011 22:32:33 -0300 Subject: [media] lirc_zilog: Remove unneeded tests for existence of the IR Tx function The driver is now structured so that it must handle an IR Tx unit for a Z8 IR chip, or it refuses to handle that Z8 IR chip. This allows us to assume that ir->tx != NULL in a few places in the driver, and also allows us to always report Tx is available to userspace. Get rid of unneeded tests for ir->tx == NULL and always report that Tx is available. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index f7aa5e4e98b3..24d4b52bd79e 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -206,8 +206,7 @@ static int add_to_buf(struct IR *ir) return -ENODATA; } schedule_timeout((100 * HZ + 999) / 1000); - if (ir->tx != NULL) - ir->tx->need_boot = 1; + ir->tx->need_boot = 1; ++failures; mutex_unlock(&ir->ir_lock); @@ -1010,10 +1009,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) int result; unsigned long mode, features = 0; + features |= LIRC_CAN_SEND_PULSE; if (ir->rx != NULL) features |= LIRC_CAN_REC_LIRCCODE; - if (ir->tx != NULL) - features |= LIRC_CAN_SEND_PULSE; switch (cmd) { case LIRC_GET_LENGTH: @@ -1040,15 +1038,9 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) result = -EINVAL; break; case LIRC_GET_SEND_MODE: - if (!(features&LIRC_CAN_SEND_MASK)) - return -ENOSYS; - result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); break; case LIRC_SET_SEND_MODE: - if (!(features&LIRC_CAN_SEND_MASK)) - return -ENOSYS; - result = get_user(mode, (unsigned long *) arg); if (!result && mode != LIRC_MODE_PULSE) return -EINVAL; @@ -1380,12 +1372,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * after registering with lirc as otherwise hotplug seems to take * 10s to create the lirc device. */ - if (ir->tx != NULL) { - /* Special TX init */ - ret = tx_init(ir->tx); - if (ret != 0) - goto out_unregister; - } + ret = tx_init(ir->tx); + if (ret != 0) + goto out_unregister; zilog_info("probe of IR %s on %s (i2c-%d) done. IR unit ready.\n", tx_probe ? "Tx" : "Rx", adap->name, adap->nr); -- cgit v1.2.3 From 6830661ead850c0722e698da5c389db4d85079be Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 15 Jan 2011 22:56:42 -0300 Subject: [media] lirc_zilog: Remove useless struct i2c_driver.command function The ir_command() function is a do-nothing stub; remove it. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 24d4b52bd79e..18fae5442c36 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -1123,7 +1123,6 @@ static struct lirc_driver lirc_template = { static int ir_remove(struct i2c_client *client); static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); -static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg); #define ID_FLAG_TX 0x01 #define ID_FLAG_HDPVR 0x02 @@ -1143,7 +1142,6 @@ static struct i2c_driver driver = { }, .probe = ir_probe, .remove = ir_remove, - .command = ir_command, .id_table = ir_transceiver_id, }; @@ -1410,12 +1408,6 @@ out_no_ir: return ret; } -static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - /* nothing */ - return 0; -} - static int __init zilog_init(void) { int ret; -- cgit v1.2.3 From c2790c7192be661e14b3d13d8bc187fd87bb9802 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Jan 2011 15:20:07 -0300 Subject: [media] lirc_zilog: Add Andy Walls to copyright notice and authors list Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_zilog.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c index 18fae5442c36..3fe5f4160194 100644 --- a/drivers/staging/lirc/lirc_zilog.c +++ b/drivers/staging/lirc/lirc_zilog.c @@ -20,6 +20,9 @@ * * parts are cut&pasted from the lirc_i2c.c driver * + * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are + * Copyright (C) 2011 Andy Walls + * * 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 @@ -1441,7 +1444,8 @@ module_exit(zilog_exit); MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)"); MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, " - "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver"); + "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, " + "Andy Walls"); MODULE_LICENSE("GPL"); /* for compat with old name, which isn't all that accurate anymore */ MODULE_ALIAS("lirc_pvr150"); -- cgit v1.2.3 From 1f1bfaa0a2e97fbeaf6cc5a07212de0afa3a6232 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Jan 2011 15:21:27 -0300 Subject: [media] lirc_zilog: Update TODO.lirc_zilog Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/TODO.lirc_zilog | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/staging/lirc/TODO.lirc_zilog b/drivers/staging/lirc/TODO.lirc_zilog index 6aa312df4018..2d0263f07937 100644 --- a/drivers/staging/lirc/TODO.lirc_zilog +++ b/drivers/staging/lirc/TODO.lirc_zilog @@ -1,13 +1,37 @@ -The binding between hdpvr and lirc_zilog is currently disabled, +1. Both ir-kbd-i2c and lirc_zilog provide support for RX events. +The 'tx_only' lirc_zilog module parameter will allow ir-kbd-i2c +and lirc_zilog to coexist in the kernel, if the user requires such a set-up. +However the IR unit will not work well without coordination between the +two modules. A shared mutex, for transceiver access locking, needs to be +supplied by bridge drivers, in struct IR_i2_init_data, to both ir-kbd-i2c +and lirc_zilog, before they will coexist usefully. This should be fixed +before moving out of staging. + +2. References and locking need careful examination. For cx18 and ivtv PCI +cards, which are not easily "hot unplugged", the imperfect state of reference +counting and locking is acceptable if not correct. For USB connected units +like HD PVR, PVR USB2, HVR-1900, and HVR1950, the likelyhood of an Ooops on +unplug is probably great. Proper reference counting and locking needs to be +implemented before this module is moved out of staging. + +3. The binding between hdpvr and lirc_zilog is currently disabled, due to an OOPS reported a few years ago when both the hdpvr and cx18 drivers were loaded in his system. More details can be seen at: http://www.mail-archive.com/linux-media@vger.kernel.org/msg09163.html More tests need to be done, in order to fix the reported issue. -There's a conflict between ir-kbd-i2c: Both provide support for RX events. -Such conflict needs to be fixed, before moving it out of staging. +4. In addition to providing a shared mutex for transceiver access +locking, bridge drivers, if able, should provide a chip reset() callback +to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines +to perform Z8 chip resets via GPIO manipulations. This will allow lirc_zilog +to bring the chip back to normal when it hangs, in the same places the +original lirc_pvr150 driver code does. This is not strictly needed, so it +is not required to move lirc_zilog out of staging. + +5. Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed +and installed on Hauppauge products. When working on either module, developers +must consider at least the following bridge drivers which mention an IR Rx unit +at address 0x71 (indicative of a Z8): -The way I2C probe works, it will try to register the driver twice, one -for RX and another for TX. The logic needs to be fixed to avoid such -issue. + ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134 -- cgit v1.2.3 From c69a4af6f06916936c8afd44a175e2bf1fbefaec Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Jan 2011 15:45:32 -0300 Subject: [media] ir-kbd-i2c: Add back defaults setting for Zilog Z8's at addr 0x71 This reverts a portion of commit 44243fc2ef99948bc9b046901880885616dd5e89 A commit for which I errantly recommended that defaults for I2C address 0x71 not be set by ir-kbd-i2c.c The pvrusb2 and bttv drivers currently rely on ir-kbd-i2c setting defaults for that address. Until I can get those bridge drivers fixed to properly send IR_i2c_init_data for boards with Zilog Z8 chips, just add back the default settings for I2C address 0x71. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ir-kbd-i2c.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index b173e409cd28..d2b20ad383a3 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -323,6 +323,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) rc_type = RC_TYPE_OTHER; ir_codes = RC_MAP_AVERMEDIA_CARDBUS; break; + case 0x71: + name = "Hauppauge/Zilog Z8"; + ir->get_key = get_key_haup_xvr; + rc_type = RC_TYPE_RC5; + ir_codes = hauppauge ? RC_MAP_HAUPPAUGE_NEW : RC_MAP_RC5_TV; + break; } /* Let the caller override settings */ -- cgit v1.2.3 From 4999e27a62eaf28e88bc69ab8cf11697e0dda261 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 16 Jan 2011 21:21:03 -0300 Subject: [media] pvrusb2: Provide more information about IR units to lirc_zilog and ir-kbd-i2c When registering an IR Rx device with the I2C subsystem, provide more detailed information about the IR device and default remote configuration for the IR driver modules. Also explicitly register any IR Tx device with the I2C subsystem. Signed-off-by: Andy Walls Acked-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 2 + drivers/media/video/pvrusb2/pvrusb2-i2c-core.c | 62 +++++++++++++++------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index ac94a8bf883e..305e6aaa844a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -40,6 +40,7 @@ #include "pvrusb2-io.h" #include #include +#include #include "pvrusb2-devattr.h" /* Legal values for PVR2_CID_HSM */ @@ -202,6 +203,7 @@ struct pvr2_hdw { /* IR related */ unsigned int ir_scheme_active; /* IR scheme as seen from the outside */ + struct IR_i2c_init_data ir_init_data; /* params passed to IR modules */ /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 7cbe18c4ca95..ccc884948f34 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -19,6 +19,7 @@ */ #include +#include #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" @@ -48,13 +49,6 @@ module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video, MODULE_PARM_DESC(disable_autoload_ir_video, "1=do not try to autoload ir_video IR receiver"); -/* Mapping of IR schemes to known I2C addresses - if any */ -static const unsigned char ir_video_addresses[] = { - [PVR2_IR_SCHEME_ZILOG] = 0x71, - [PVR2_IR_SCHEME_29XXX] = 0x18, - [PVR2_IR_SCHEME_24XXX] = 0x18, -}; - static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ u8 i2c_addr, /* I2C address we're talking to */ u8 *data, /* Data to write */ @@ -574,26 +568,56 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) { struct i2c_board_info info; - unsigned char addr = 0; + struct IR_i2c_init_data *init_data = &hdw->ir_init_data; if (pvr2_disable_ir_video) { pvr2_trace(PVR2_TRACE_INFO, "Automatic binding of ir_video has been disabled."); return; } - if (hdw->ir_scheme_active < ARRAY_SIZE(ir_video_addresses)) { - addr = ir_video_addresses[hdw->ir_scheme_active]; - } - if (!addr) { + memset(&info, 0, sizeof(struct i2c_board_info)); + switch (hdw->ir_scheme_active) { + case PVR2_IR_SCHEME_24XXX: /* FX2-controlled IR */ + case PVR2_IR_SCHEME_29XXX: /* Original 29xxx device */ + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; + init_data->type = RC_TYPE_RC5; + init_data->name = hdw->hdw_desc->description; + init_data->polling_interval = 100; /* ms From ir-kbd-i2c */ + /* IR Receiver */ + info.addr = 0x18; + info.platform_data = init_data; + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", + info.type, info.addr); + i2c_new_device(&hdw->i2c_adap, &info); + break; + case PVR2_IR_SCHEME_ZILOG: /* HVR-1950 style */ + case PVR2_IR_SCHEME_24XXX_MCE: /* 24xxx MCE device */ + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = RC_TYPE_RC5; + init_data->name = hdw->hdw_desc->description; + init_data->polling_interval = 260; /* ms From lirc_zilog */ + /* IR Receiver */ + info.addr = 0x71; + info.platform_data = init_data; + strlcpy(info.type, "ir_rx_z8f0811_haup", I2C_NAME_SIZE); + pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", + info.type, info.addr); + i2c_new_device(&hdw->i2c_adap, &info); + /* IR Trasmitter */ + info.addr = 0x70; + info.platform_data = init_data; + strlcpy(info.type, "ir_tx_z8f0811_haup", I2C_NAME_SIZE); + pvr2_trace(PVR2_TRACE_INFO, "Binding %s to i2c address 0x%02x.", + info.type, info.addr); + i2c_new_device(&hdw->i2c_adap, &info); + break; + default: /* The device either doesn't support I2C-based IR or we don't know (yet) how to operate IR on the device. */ - return; + break; } - pvr2_trace(PVR2_TRACE_INFO, - "Binding ir_video to i2c address 0x%02x.", addr); - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - info.addr = addr; - i2c_new_device(&hdw->i2c_adap, &info); } void pvr2_i2c_core_init(struct pvr2_hdw *hdw) -- cgit v1.2.3 From c959acfddb3d24ecfdae1a280a7a1785d9df81d1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 16 Jan 2011 11:03:28 -0300 Subject: [media] v4l2-ctrls: fix missing 'read-only' check VIDIOC_S_CTRL did not check against read-only controls. Even worse, for controls of type CTRL_CLASS it would cause a kernel oops since those controls do not have a s_ctrl op. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 0d1a3d831374..22a9a722f520 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -1824,6 +1824,9 @@ static int set_ctrl(struct v4l2_ctrl *ctrl, s32 *val) int ret; int i; + if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) + return -EACCES; + v4l2_ctrl_lock(ctrl); /* Reset the 'is_new' flags of the cluster */ -- cgit v1.2.3 From 829fb2dcb5252c5064d12cdaf65d2828420e07b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 16 Jan 2011 11:21:40 -0300 Subject: [media] v4l2-ctrls: queryctrl shouldn't attempt to replace V4L2_CID_PRIVATE_BASE IDs When queryctrl is called with a V4L2_CID_PRIVATE_BASE control ID, then currently it is replaced by the real internal ID. This is not according to the spec so keep the V4L2_CID_PRIVATE_BASE ID in this case. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 22a9a722f520..01251431fd2a 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -1344,7 +1344,10 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) ctrl = ref->ctrl; memset(qc, 0, sizeof(*qc)); - qc->id = ctrl->id; + if (id >= V4L2_CID_PRIVATE_BASE) + qc->id = id; + else + qc->id = ctrl->id; strlcpy(qc->name, ctrl->name, sizeof(qc->name)); qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; -- cgit v1.2.3 From 3a6be8d83c82253692f788a65f2d65aa33e37db1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 16 Jan 2011 17:09:54 -0300 Subject: [media] DocBook/v4l: fix validation error in dev-rds.xml Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/dev-rds.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/v4l/dev-rds.xml b/Documentation/DocBook/v4l/dev-rds.xml index 360d2737e649..2427f54397e7 100644 --- a/Documentation/DocBook/v4l/dev-rds.xml +++ b/Documentation/DocBook/v4l/dev-rds.xml @@ -75,6 +75,7 @@ as follows:

+ RDS datastructures struct <structname>v4l2_rds_data</structname> @@ -129,10 +130,11 @@ as follows:
Block defines - + - + + V4L2_RDS_BLOCK_MSK -- cgit v1.2.3 From bda50bcd0cc21d9d6dd8c82628f763ab108260a6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 16 Jan 2011 17:44:17 -0300 Subject: [media] DocBook/v4l: update V4L2 revision and update copyright years Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/dvb/dvbapi.xml | 2 +- Documentation/DocBook/media.tmpl | 4 ++-- Documentation/DocBook/v4l/v4l2.xml | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/DocBook/dvb/dvbapi.xml b/Documentation/DocBook/dvb/dvbapi.xml index e3a97fdd62a6..ad8678d48916 100644 --- a/Documentation/DocBook/dvb/dvbapi.xml +++ b/Documentation/DocBook/dvb/dvbapi.xml @@ -28,7 +28,7 @@ Convergence GmbH - 2009-2010 + 2009-2011 Mauro Carvalho Chehab diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media.tmpl index f11048d4053f..a99088aae1aa 100644 --- a/Documentation/DocBook/media.tmpl +++ b/Documentation/DocBook/media.tmpl @@ -28,7 +28,7 @@ LINUX MEDIA INFRASTRUCTURE API - 2009-2010 + 2009-2011 LinuxTV Developers @@ -86,7 +86,7 @@ Foundation. A copy of the license is included in the chapter entitled - 2009-2010 + 2009-2011 Mauro Carvalho Chehab diff --git a/Documentation/DocBook/v4l/v4l2.xml b/Documentation/DocBook/v4l/v4l2.xml index 839e93e875ae..9288af96de34 100644 --- a/Documentation/DocBook/v4l/v4l2.xml +++ b/Documentation/DocBook/v4l/v4l2.xml @@ -100,6 +100,7 @@ Remote Controller chapter. 2008 2009 2010 + 2011 Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab @@ -381,7 +382,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 2.6.33 + Revision 2.6.38 &sub-common; -- cgit v1.2.3 From 01c40c048b0f3f377e6d27b35fd99f04efcc21dd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 19 Nov 2010 11:20:06 -0300 Subject: [media] v4l: Include linux/videodev2.h in media/v4l2-ctrls.h The later makes extensive use of structures defined in the former. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-ctrls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index fcc9a0cf8ff1..97d063837b61 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -23,6 +23,7 @@ #include #include +#include /* forward references */ struct v4l2_ctrl_handler; -- cgit v1.2.3 From eac9aa005a2e0676d07f1906f9933fd055eb1f0b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 7 Dec 2010 08:57:25 -0300 Subject: [media] v4l: Fix a use-before-set in the control framework v4l2_queryctrl sets the step value based on the control type. That would be fine if it used the control type stored in the V4L2 kernel control object, not the one stored in the userspace ioctl structure that has just been memset to 0. Fix this. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 01251431fd2a..ef66d2af0c57 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -1352,7 +1352,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; - if (qc->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU) qc->step = 1; else qc->step = ctrl->step; -- cgit v1.2.3 From bd5ba3ba2ead7248124723dac2481d0992e75e91 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Dec 2010 11:27:38 -0300 Subject: [media] w9966: zero device state after a detach After a detach zero the whole device state to ensure a clean slate on the next attach. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/w9966.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 019ee206cbee..fa35639d0c15 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -937,6 +937,7 @@ static void w9966_term(struct w9966 *cam) parport_unregister_device(cam->pdev); w9966_set_state(cam, W9966_STATE_PDEV, 0); } + memset(cam, 0, sizeof(*cam)); } -- cgit v1.2.3 From 6ce3ced4f73e3f0c345f47dc99fd21f2248724a8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Dec 2010 11:28:51 -0300 Subject: [media] zoran: use video_device_alloc instead of kmalloc Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/zoran/zoran_card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index 9cdc3bb15b15..9f2bac519647 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -1041,7 +1041,7 @@ zr36057_init (struct zoran *zr) /* allocate memory *before* doing anything to the hardware * in case allocation fails */ zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL); - zr->video_dev = kmalloc(sizeof(struct video_device), GFP_KERNEL); + zr->video_dev = video_device_alloc(); if (!zr->stat_com || !zr->video_dev) { dprintk(1, KERN_ERR -- cgit v1.2.3 From 46b633779b299c7fb3d78f153a5034055f99cd45 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 31 Dec 2010 11:47:23 -0300 Subject: [media] v4l2-dev: don't memset video_device.dev Zeroing video_device.dev causes a memory leak if video_set_drvdata was called before video_register_device was called. video_set_drvdata calls dev_set_drvdata which allocates video_device.dev.p. memsetting this will prevent freeing of that memory. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-dev.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 359e23290a7e..341764a3a990 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -419,6 +419,10 @@ static int get_index(struct video_device *vdev) * The registration code assigns minor numbers and device node numbers * based on the requested type and registers the new device node with * the kernel. + * + * This function assumes that struct video_device was zeroed when it + * was allocated and does not contain any stale date. + * * An error is returned if no free minor or device node number could be * found, or if the registration of the device node failed. * @@ -440,7 +444,6 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, int minor_offset = 0; int minor_cnt = VIDEO_NUM_DEVICES; const char *name_base; - void *priv = vdev->dev.p; /* A minor value of -1 marks this video device as never having been registered */ @@ -559,10 +562,6 @@ static int __video_register_device(struct video_device *vdev, int type, int nr, } /* Part 4: register the device with sysfs */ - memset(&vdev->dev, 0, sizeof(vdev->dev)); - /* The memset above cleared the device's device_private, so - put back the copy we made earlier. */ - vdev->dev.p = priv; vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); if (vdev->parent) -- cgit v1.2.3 From 672dcd54774ea1b03da8f2baa1cdbf827927fc85 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Jan 2011 17:48:21 -0300 Subject: [media] v4l2-device: fix 'use-after-freed' oops Fix a bug in v4l2_device_unregister where the sd pointer can be dereferenced after it was freed. Normally the i2c adapter is removed before this function is called. Removing the adapter will also unregister all subdevs on that adapter, so generally v4l2_device_unregister has nothing to do. However, in the case of a platform i2c bus that bus is generally not freed. In that case, after freeing the i2c subdevice the code will fall into the second block when it tests if the subdev is a SPI device. But by that time the subdev is already freed and the kernel oopses. The fix is trivial: continue with the loop after freeing the i2c or spi subdevice. Signed-off-by: Hans Verkuil Reported-by: Daniel Drake Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-device.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index b24f002ffa67..ce64fe16bc60 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -100,6 +100,7 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) is a platform bus, then it is never deleted. */ if (client) i2c_unregister_device(client); + continue; } #endif #if defined(CONFIG_SPI) @@ -108,6 +109,7 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) if (spi) spi_unregister_device(spi); + continue; } #endif } -- cgit v1.2.3 From 706c57d802394e2fe720ebc929234a678f94e716 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Thu, 6 Jan 2011 13:23:13 -0300 Subject: [media] rc/mceusb: timeout should be in ns, not us Fixes an egregious bug in mceusb driver, where the receiver was being put into idle mode far sooner than it should have, thanks to storing a timeout value that in us where it should be ns. Basically, the receiver kept going into idle mode before a trailing space had been fully received, which was causing problems for some protocols, most notably manifesting as lirc userspace never receiving a trailing space for any rc5 signals. Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/mceusb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 2d9113493cf0..079353e5d558 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -816,7 +816,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index) switch (ir->buf_in[index]) { /* 2-byte return value commands */ case MCE_CMD_S_TIMEOUT: - ir->rc->timeout = MS_TO_US((hi << 8 | lo) / 2); + ir->rc->timeout = MS_TO_NS((hi << 8 | lo) / 2); break; /* 1-byte return value commands */ @@ -1060,7 +1060,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir) rc->priv = ir; rc->driver_type = RC_DRIVER_IR_RAW; rc->allowed_protos = RC_TYPE_ALL; - rc->timeout = MS_TO_US(1000); + rc->timeout = MS_TO_NS(1000); if (!ir->flags.no_tx) { rc->s_tx_mask = mceusb_set_tx_mask; rc->s_tx_carrier = mceusb_set_tx_carrier; -- cgit v1.2.3 From 324b04ba5da7918a2409f8113e46843bfbd89e67 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 14 Jan 2011 16:25:21 -0300 Subject: [media] hdpvr: enable IR part A number of things going on here, but the end result is that the IR part on the hdpvr gets enabled, and can be used with ir-kbd-i2c and/or lirc_zilog. First up, there are some conditional build fixes that come into play whether i2c is built-in or modular. Second, we're swapping out i2c_new_probed_device() for i2c_new_device(), as in my testing, probing always fails, but we *know* that all hdpvr devices have a z8 chip at 0x70 and 0x71. Third, we're poking at an i2c address directly without a client, and writing some magic bits to actually turn on this IR part (this could use some improvement in the future). Fourth, some of the i2c_adapter storage has been reworked, as the existing implementation used to lead to an oops following i2c changes c. 2.6.31. Earlier editions of this patch have been floating around the 'net for a while, including being patched into Fedora kernels, and they *do* work. This specific version isn't yet tested, beyond loading ir-kbd-i2c and confirming that it does bind to the RX address of the hdpvr. [mchehab@redhat.com: I2C_CLASS_TV_ANALOG is not defined. Fix compilation bug] Signed-off-by: Jarod Wilson Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hdpvr/Makefile | 4 +- drivers/media/video/hdpvr/hdpvr-core.c | 10 ++- drivers/media/video/hdpvr/hdpvr-i2c.c | 119 +++++++++++++++----------------- drivers/media/video/hdpvr/hdpvr-video.c | 7 +- drivers/media/video/hdpvr/hdpvr.h | 2 +- 5 files changed, 65 insertions(+), 77 deletions(-) diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile index e0230fcb2e36..3baa9f613ca3 100644 --- a/drivers/media/video/hdpvr/Makefile +++ b/drivers/media/video/hdpvr/Makefile @@ -1,6 +1,4 @@ -hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o - -hdpvr-$(CONFIG_I2C) += hdpvr-i2c.o +hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index f7d1ee55185a..a6572e5ae369 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -378,19 +378,17 @@ static int hdpvr_probe(struct usb_interface *interface, goto error; } -#ifdef CONFIG_I2C - /* until i2c is working properly */ - retval = 0; /* hdpvr_register_i2c_adapter(dev); */ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + retval = hdpvr_register_i2c_adapter(dev); if (retval < 0) { v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n"); goto error; } - /* until i2c is working properly */ - retval = 0; /* hdpvr_register_i2c_ir(dev); */ + retval = hdpvr_register_i2c_ir(dev); if (retval < 0) v4l2_err(&dev->v4l2_dev, "registering i2c IR devices failed\n"); -#endif /* CONFIG_I2C */ +#endif /* let the user know what node this device is now attached to */ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n", diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index 24966aa02a70..45b88cf60d68 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -13,6 +13,8 @@ * */ +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + #include #include @@ -28,55 +30,31 @@ #define Z8F0811_IR_TX_I2C_ADDR 0x70 #define Z8F0811_IR_RX_I2C_ADDR 0x71 -static const u8 ir_i2c_addrs[] = { - Z8F0811_IR_TX_I2C_ADDR, - Z8F0811_IR_RX_I2C_ADDR, -}; -static const char * const ir_devicenames[] = { - "ir_tx_z8f0811_hdpvr", - "ir_rx_z8f0811_hdpvr", +static struct i2c_board_info hdpvr_i2c_board_info = { + I2C_BOARD_INFO("ir_tx_z8f0811_hdpvr", Z8F0811_IR_TX_I2C_ADDR), + I2C_BOARD_INFO("ir_rx_z8f0811_hdpvr", Z8F0811_IR_RX_I2C_ADDR), }; -static int hdpvr_new_i2c_ir(struct hdpvr_device *dev, struct i2c_adapter *adap, - const char *type, u8 addr) +int hdpvr_register_i2c_ir(struct hdpvr_device *dev) { - struct i2c_board_info info; + struct i2c_client *c; struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; - unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; - - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, type, I2C_NAME_SIZE); /* Our default information for ir-kbd-i2c.c to use */ - switch (addr) { - case Z8F0811_IR_RX_I2C_ADDR: - init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; - init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - init_data->type = RC_TYPE_RC5; - init_data->name = "HD PVR"; - info.platform_data = init_data; - break; - } - - return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? - -1 : 0; -} + init_data->ir_codes = RC_MAP_HAUPPAUGE_NEW; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = RC_TYPE_RC5; + init_data->name = "HD PVR"; + hdpvr_i2c_board_info.platform_data = init_data; -int hdpvr_register_i2c_ir(struct hdpvr_device *dev) -{ - int i; - int ret = 0; + c = i2c_new_device(&dev->i2c_adapter, &hdpvr_i2c_board_info); - for (i = 0; i < ARRAY_SIZE(ir_i2c_addrs); i++) - ret += hdpvr_new_i2c_ir(dev, dev->i2c_adapter, - ir_devicenames[i], ir_i2c_addrs[i]); - - return ret; + return (c == NULL) ? -ENODEV : 0; } -static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr, - char *data, int len) +static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, + unsigned char addr, char *data, int len) { int ret; char *buf = kmalloc(len, GFP_KERNEL); @@ -86,7 +64,7 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr, ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_READ, CTRL_READ_REQUEST, - 0x100|addr, 0, buf, len, 1000); + (bus << 8) | addr, 0, buf, len, 1000); if (ret == len) { memcpy(data, buf, len); @@ -99,8 +77,8 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr, return ret; } -static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr, - char *data, int len) +static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, + unsigned char addr, char *data, int len) { int ret; char *buf = kmalloc(len, GFP_KERNEL); @@ -111,7 +89,7 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr, ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - 0x100|addr, 0, buf, len, 1000); + (bus << 8) | addr, 0, buf, len, 1000); if (ret < 0) goto error; @@ -121,7 +99,7 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr, REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, 0, 0, buf, 2, 1000); - if (ret == 2) + if ((ret == 2) && (buf[1] == (len - 1))) ret = 0; else if (ret >= 0) ret = -EIO; @@ -146,10 +124,10 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, addr = msgs[i].addr << 1; if (msgs[i].flags & I2C_M_RD) - retval = hdpvr_i2c_read(dev, addr, msgs[i].buf, + retval = hdpvr_i2c_read(dev, 1, addr, msgs[i].buf, msgs[i].len); else - retval = hdpvr_i2c_write(dev, addr, msgs[i].buf, + retval = hdpvr_i2c_write(dev, 1, addr, msgs[i].buf, msgs[i].len); } @@ -168,30 +146,47 @@ static struct i2c_algorithm hdpvr_algo = { .functionality = hdpvr_functionality, }; +static struct i2c_adapter hdpvr_i2c_adapter_template = { + .name = "Hauppage HD PVR I2C", + .owner = THIS_MODULE, + .algo = &hdpvr_algo, +}; + +static int hdpvr_activate_ir(struct hdpvr_device *dev) +{ + char buffer[8]; + + mutex_lock(&dev->i2c_mutex); + + hdpvr_i2c_read(dev, 0, 0x54, buffer, 1); + + buffer[0] = 0; + buffer[1] = 0x8; + hdpvr_i2c_write(dev, 1, 0x54, buffer, 2); + + buffer[1] = 0x18; + hdpvr_i2c_write(dev, 1, 0x54, buffer, 2); + + mutex_unlock(&dev->i2c_mutex); + + return 0; +} + int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) { - struct i2c_adapter *i2c_adap; int retval = -ENOMEM; - i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); - if (i2c_adap == NULL) - goto error; + hdpvr_activate_ir(dev); - strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C", - sizeof(i2c_adap->name)); - i2c_adap->algo = &hdpvr_algo; - i2c_adap->owner = THIS_MODULE; - i2c_adap->dev.parent = &dev->udev->dev; + memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template, + sizeof(struct i2c_adapter)); + dev->i2c_adapter.dev.parent = &dev->udev->dev; - i2c_set_adapdata(i2c_adap, dev); + i2c_set_adapdata(&dev->i2c_adapter, dev); - retval = i2c_add_adapter(i2c_adap); + retval = i2c_add_adapter(&dev->i2c_adapter); - if (!retval) - dev->i2c_adapter = i2c_adap; - else - kfree(i2c_adap); - -error: return retval; } + +#endif diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index d38fe1043e47..514aea76eaa5 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -1220,12 +1220,9 @@ static void hdpvr_device_release(struct video_device *vdev) v4l2_device_unregister(&dev->v4l2_dev); /* deregister I2C adapter */ -#ifdef CONFIG_I2C +#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE) mutex_lock(&dev->i2c_mutex); - if (dev->i2c_adapter) - i2c_del_adapter(dev->i2c_adapter); - kfree(dev->i2c_adapter); - dev->i2c_adapter = NULL; + i2c_del_adapter(&dev->i2c_adapter); mutex_unlock(&dev->i2c_mutex); #endif /* CONFIG_I2C */ diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h index 37f1e4c7675d..29f74269400c 100644 --- a/drivers/media/video/hdpvr/hdpvr.h +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -106,7 +106,7 @@ struct hdpvr_device { struct work_struct worker; /* I2C adapter */ - struct i2c_adapter *i2c_adapter; + struct i2c_adapter i2c_adapter; /* I2C lock */ struct mutex i2c_mutex; -- cgit v1.2.3 From 559d162e1ebcdb61e89f154f2c2db376af072b0e Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Fri, 14 Jan 2011 16:40:32 -0300 Subject: [media] hdpvr: reduce latency of i2c read/write w/recycled buffer The current hdpvr code kmalloc's a new buffer for every i2c read and write. Rather than do that, lets allocate a buffer in the driver's device struct and just use that every time. The size I've chosen for the buffer is the maximum size I could ascertain might be used by either ir-kbd-i2c or lirc_zilog, plus a bit of padding (lirc_zilog may use up to 100 bytes on tx, rounded that up to 128). Note that this might also remedy user reports of very sluggish behavior of IR receive with hdpvr hardware. v2: make sure (len <= (dev->i2c_buf)) [Jean Delvare] Reported-by: Jean Delvare Acked-by: Jean Delvare Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hdpvr/hdpvr-i2c.c | 30 +++++++++++++----------------- drivers/media/video/hdpvr/hdpvr.h | 3 +++ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index 45b88cf60d68..89b71faeaac2 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -57,23 +57,21 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus, unsigned char addr, char *data, int len) { int ret; - char *buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; + + if (len > sizeof(dev->i2c_buf)) + return -EINVAL; ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_READ, CTRL_READ_REQUEST, - (bus << 8) | addr, 0, buf, len, 1000); + (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); if (ret == len) { - memcpy(data, buf, len); + memcpy(data, &dev->i2c_buf, len); ret = 0; } else if (ret >= 0) ret = -EIO; - kfree(buf); - return ret; } @@ -81,31 +79,29 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus, unsigned char addr, char *data, int len) { int ret; - char *buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - memcpy(buf, data, len); + if (len > sizeof(dev->i2c_buf)) + return -EINVAL; + + memcpy(&dev->i2c_buf, data, len); ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, - (bus << 8) | addr, 0, buf, len, 1000); + (bus << 8) | addr, 0, &dev->i2c_buf, len, 1000); if (ret < 0) - goto error; + return ret; ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, - 0, 0, buf, 2, 1000); + 0, 0, &dev->i2c_buf, 2, 1000); - if ((ret == 2) && (buf[1] == (len - 1))) + if ((ret == 2) && (dev->i2c_buf[1] == (len - 1))) ret = 0; else if (ret >= 0) ret = -EIO; -error: - kfree(buf); return ret; } diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h index 29f74269400c..ee74e3be9a6a 100644 --- a/drivers/media/video/hdpvr/hdpvr.h +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -25,6 +25,7 @@ KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE) #define HDPVR_MAX 8 +#define HDPVR_I2C_MAX_SIZE 128 /* Define these values to match your devices */ #define HD_PVR_VENDOR_ID 0x2040 @@ -109,6 +110,8 @@ struct hdpvr_device { struct i2c_adapter i2c_adapter; /* I2C lock */ struct mutex i2c_mutex; + /* I2C message buffer space */ + char i2c_buf[HDPVR_I2C_MAX_SIZE]; /* For passing data to ir-kbd-i2c */ struct IR_i2c_init_data ir_i2c_init_data; -- cgit v1.2.3 From 88914bdf8c677ebd7e797adac05e47303fd6ac77 Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Mon, 17 Jan 2011 16:02:00 -0300 Subject: [media] staging/lirc: fix mem leaks and ptr err usage When the lirc drivers were converted over to using memdup_user, I mistakenly also removed corresponding calls to kfree. Add those back. I also screwed up on the allocation error check in lirc_serial, using if (PTR_ERR()) instead of if (IS_ERR()), which broke transmit. Reported-by: Jiri Fojtasek Signed-off-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/lirc/lirc_imon.c | 1 + drivers/staging/lirc/lirc_it87.c | 1 + drivers/staging/lirc/lirc_parallel.c | 19 ++++++++++++++----- drivers/staging/lirc/lirc_sasem.c | 1 + drivers/staging/lirc/lirc_serial.c | 3 ++- drivers/staging/lirc/lirc_sir.c | 1 + 6 files changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c index 0da6b9518af9..235cab0eb087 100644 --- a/drivers/staging/lirc/lirc_imon.c +++ b/drivers/staging/lirc/lirc_imon.c @@ -447,6 +447,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, exit: mutex_unlock(&context->ctx_lock); + kfree(data_buf); return (!retval) ? n_bytes : retval; } diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c index 929ae5795467..5938616f3e8f 100644 --- a/drivers/staging/lirc/lirc_it87.c +++ b/drivers/staging/lirc/lirc_it87.c @@ -232,6 +232,7 @@ static ssize_t lirc_write(struct file *file, const char *buf, i++; } terminate_send(tx_buf[i - 1]); + kfree(tx_buf); return n; } diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c index dfd2c447e67d..3a9c09881b2b 100644 --- a/drivers/staging/lirc/lirc_parallel.c +++ b/drivers/staging/lirc/lirc_parallel.c @@ -376,6 +376,7 @@ static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, unsigned long flags; int counttimer; int *wbuf; + ssize_t ret; if (!is_claimed) return -EBUSY; @@ -393,8 +394,10 @@ static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, if (timer == 0) { /* try again if device is ready */ timer = init_lirc_timer(); - if (timer == 0) - return -EIO; + if (timer == 0) { + ret = -EIO; + goto out; + } } /* adjust values from usecs */ @@ -420,7 +423,8 @@ static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, if (check_pselecd && (in(1) & LP_PSELECD)) { lirc_off(); local_irq_restore(flags); - return -EIO; + ret = -EIO; + goto out; } } while (counttimer < wbuf[i]); i++; @@ -436,7 +440,8 @@ static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, level = newlevel; if (check_pselecd && (in(1) & LP_PSELECD)) { local_irq_restore(flags); - return -EIO; + ret = -EIO; + goto out; } } while (counttimer < wbuf[i]); i++; @@ -445,7 +450,11 @@ static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, #else /* place code that handles write without external timer here */ #endif - return n; + ret = n; +out: + kfree(wbuf); + + return ret; } static unsigned int lirc_poll(struct file *file, poll_table *wait) diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/lirc/lirc_sasem.c index 998485ebdbce..925eabe14854 100644 --- a/drivers/staging/lirc/lirc_sasem.c +++ b/drivers/staging/lirc/lirc_sasem.c @@ -448,6 +448,7 @@ static ssize_t vfd_write(struct file *file, const char *buf, exit: mutex_unlock(&context->ctx_lock); + kfree(data_buf); return (!retval) ? n_bytes : retval; } diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c index 9bcf149c4260..1c3099b388e0 100644 --- a/drivers/staging/lirc/lirc_serial.c +++ b/drivers/staging/lirc/lirc_serial.c @@ -966,7 +966,7 @@ static ssize_t lirc_write(struct file *file, const char *buf, if (n % sizeof(int) || count % 2 == 0) return -EINVAL; wbuf = memdup_user(buf, n); - if (PTR_ERR(wbuf)) + if (IS_ERR(wbuf)) return PTR_ERR(wbuf); spin_lock_irqsave(&hardware[type].lock, flags); if (type == LIRC_IRDEO) { @@ -981,6 +981,7 @@ static ssize_t lirc_write(struct file *file, const char *buf, } off(); spin_unlock_irqrestore(&hardware[type].lock, flags); + kfree(wbuf); return n; } diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c index c553ab626238..76be7b8c6209 100644 --- a/drivers/staging/lirc/lirc_sir.c +++ b/drivers/staging/lirc/lirc_sir.c @@ -330,6 +330,7 @@ static ssize_t lirc_write(struct file *file, const char *buf, size_t n, /* enable receiver */ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; #endif + kfree(tx_buf); return count; } -- cgit v1.2.3 From 5734a07cbb8d4600a74a374c839620ddc62b2cf2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Jan 2011 17:07:12 +0100 Subject: ALSA: hda - Add quirk for HP Z-series workstation It seems working well with model=hp-bpc. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4f006eedd7ef..7874023db0f3 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -12629,6 +12629,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = { ALC262_HP_BPC), SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1300, "HP xw series", ALC262_HP_BPC), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series", + ALC262_HP_BPC), SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series", ALC262_HP_BPC), SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL), -- cgit v1.2.3 From 5d1034f01b3616d6636ab7851ad9e63d42abbbd6 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 19 Jan 2011 08:59:33 -0700 Subject: powerpc/83xx: fix build failures on dt compatible list. Commit a4f740cf, "of/flattree: Add of_flat_dt_match() helper function" introduced build failures in arch/powerpc/platform/83xx by mistyping 'static' as 'struct' in the compatible string list, and omitting a few semicolons. This patch fixes it. Reported-by: Stephen Rothwell Signed-off-by: Grant Likely --- arch/powerpc/platforms/83xx/mpc830x_rdb.c | 4 ++-- arch/powerpc/platforms/83xx/mpc831x_rdb.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/83xx/mpc830x_rdb.c b/arch/powerpc/platforms/83xx/mpc830x_rdb.c index 661d354e4ff2..d0c4e15b7794 100644 --- a/arch/powerpc/platforms/83xx/mpc830x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc830x_rdb.c @@ -57,12 +57,12 @@ static void __init mpc830x_rdb_init_IRQ(void) ipic_set_default_priority(); } -struct const char *board[] __initdata = { +static const char *board[] __initdata = { "MPC8308RDB", "fsl,mpc8308rdb", "denx,mpc8308_p1m", NULL -} +}; /* * Called very early, MMU is off, device-tree isn't unflattened diff --git a/arch/powerpc/platforms/83xx/mpc831x_rdb.c b/arch/powerpc/platforms/83xx/mpc831x_rdb.c index b54cd736a895..f859ead49a8d 100644 --- a/arch/powerpc/platforms/83xx/mpc831x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc831x_rdb.c @@ -60,11 +60,11 @@ static void __init mpc831x_rdb_init_IRQ(void) ipic_set_default_priority(); } -struct const char *board[] __initdata = { +static const char *board[] __initdata = { "MPC8313ERDB", "fsl,mpc8315erdb", NULL -} +}; /* * Called very early, MMU is off, device-tree isn't unflattened -- cgit v1.2.3 From aa1d0c5261f17d48636bf6d10bde0f38045511c0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Jan 2011 17:27:58 +0100 Subject: ALSA: hda - Fix "unused variable" compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sound/pci/hda/patch_realtek.c: In function ‘alc_apply_fixup’: sound/pci/hda/patch_realtek.c:1724:14: warning: unused variable ‘modelname’ snd_printdd() is evaluated only when CONFIG_SND_DEBUG_VERBOSE=y. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7874023db0f3..2b055e2780bb 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1721,7 +1721,9 @@ static void alc_apply_fixup(struct hda_codec *codec, int action) { struct alc_spec *spec = codec->spec; int id = spec->fixup_id; +#ifdef CONFIG_SND_DEBUG_VERBOSE const char *modelname = spec->fixup_name; +#endif int depth = 0; if (!spec->fixup_list) -- cgit v1.2.3 From 12fed00de963433128b5366a21a55808fab2f756 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Mon, 17 Jan 2011 20:15:44 +0300 Subject: CIFS: Fix oplock break handling (try #2) When we get oplock break notification we should set the appropriate value of OplockLevel field in oplock break acknowledge according to the oplock level held by the client in this time. As we only can have level II oplock or no oplock in the case of oplock break, we should be aware only about clientCanCacheRead field in cifsInodeInfo structure. Also fix bug connected with wrong interpretation of OplockLevel field during oplock break notification processing. Signed-off-by: Pavel Shilovsky Cc: Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 2 +- fs/cifs/cifssmb.c | 4 +++- fs/cifs/file.c | 21 +++++++++++---------- fs/cifs/misc.c | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index e6d1481b16c1..95d5dbbb4c7a 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -347,7 +347,7 @@ extern int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 netfid, const __u64 len, const __u64 offset, const __u32 numUnlock, const __u32 numLock, const __u8 lockType, - const bool waitFlag); + const bool waitFlag, const __u8 oplock_level); extern int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const int get_flag, const __u64 len, struct file_lock *, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 2f6795e524d3..3652cc60314c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1663,7 +1663,8 @@ int CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, const __u16 smb_file_id, const __u64 len, const __u64 offset, const __u32 numUnlock, - const __u32 numLock, const __u8 lockType, const bool waitFlag) + const __u32 numLock, const __u8 lockType, + const bool waitFlag, const __u8 oplock_level) { int rc = 0; LOCK_REQ *pSMB = NULL; @@ -1691,6 +1692,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon, pSMB->NumberOfLocks = cpu_to_le16(numLock); pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock); pSMB->LockType = lockType; + pSMB->OplockLevel = oplock_level; pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = smb_file_id; /* netfid stays le */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d843631c028d..af371910f543 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -726,12 +726,12 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) /* BB we could chain these into one lock request BB */ rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, - 0, 1, lockType, 0 /* wait flag */ ); + 0, 1, lockType, 0 /* wait flag */, 0); if (rc == 0) { rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 1 /* numUnlock */ , 0 /* numLock */ , lockType, - 0 /* wait flag */ ); + 0 /* wait flag */, 0); pfLock->fl_type = F_UNLCK; if (rc != 0) cERROR(1, "Error unlocking previously locked " @@ -748,13 +748,13 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 0, 1, lockType | LOCKING_ANDX_SHARED_LOCK, - 0 /* wait flag */); + 0 /* wait flag */, 0); if (rc == 0) { rc = CIFSSMBLock(xid, tcon, netfid, length, pfLock->fl_start, 1, 0, lockType | LOCKING_ANDX_SHARED_LOCK, - 0 /* wait flag */); + 0 /* wait flag */, 0); pfLock->fl_type = F_RDLCK; if (rc != 0) cERROR(1, "Error unlocking " @@ -797,8 +797,8 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) if (numLock) { rc = CIFSSMBLock(xid, tcon, netfid, length, - pfLock->fl_start, - 0, numLock, lockType, wait_flag); + pfLock->fl_start, 0, numLock, lockType, + wait_flag, 0); if (rc == 0) { /* For Windows locks we must store them. */ @@ -818,9 +818,9 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) (pfLock->fl_start + length) >= (li->offset + li->length)) { stored_rc = CIFSSMBLock(xid, tcon, - netfid, - li->length, li->offset, - 1, 0, li->type, false); + netfid, li->length, + li->offset, 1, 0, + li->type, false, 0); if (stored_rc) rc = stored_rc; else { @@ -2192,7 +2192,8 @@ void cifs_oplock_break(struct work_struct *work) */ if (!cfile->oplock_break_cancelled) { rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0, - 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false); + 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false, + cinode->clientCanCacheRead ? 1 : 0); cFYI(1, "Oplock release rc = %d", rc); } diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 43f10281bc19..09bfcf08a90f 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -571,7 +571,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) pCifsInode = CIFS_I(netfile->dentry->d_inode); cifs_set_oplock_level(pCifsInode, - pSMB->OplockLevel); + pSMB->OplockLevel ? OPLOCK_READ : 0); /* * cifs_oplock_break_put() can't be called * from here. Get reference after queueing -- cgit v1.2.3 From 941b853d779de3298e39f1eb4e252984464eaea8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:01 -0500 Subject: cifs: don't fail writepages on -EAGAIN errors If CIFSSMBWrite2 returns -EAGAIN, then the error should be considered temporary. CIFS should retry the write instead of setting an error on the mapping and returning. For WB_SYNC_ALL, just retry the write immediately. In the WB_SYNC_NONE case, call redirty_page_for_writeback on all of the pages that didn't get written out and then move on. Also, fix up the handling of a short write with a successful return code. MS-CIFS says that 0 bytes_written means ENOSPC or EFBIG. It doesn't mention what a short, but non-zero write means, so for now treat it as we would an -EAGAIN return. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/file.c | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index af371910f543..cfa2e5ebcafe 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1377,6 +1377,7 @@ retry: break; } if (n_iov) { +retry_write: open_file = find_writable_file(CIFS_I(mapping->host), false); if (!open_file) { @@ -1389,31 +1390,55 @@ retry: &bytes_written, iov, n_iov, long_op); cifsFileInfo_put(open_file); - cifs_update_eof(cifsi, offset, bytes_written); } - if (rc || bytes_written < bytes_to_write) { - cERROR(1, "Write2 ret %d, wrote %d", - rc, bytes_written); - mapping_set_error(mapping, rc); - } else { + cFYI(1, "Write2 rc=%d, wrote=%u", rc, bytes_written); + + /* + * For now, treat a short write as if nothing got + * written. A zero length write however indicates + * ENOSPC or EFBIG. We have no way to know which + * though, so call it ENOSPC for now. EFBIG would + * get translated to AS_EIO anyway. + * + * FIXME: make it take into account the data that did + * get written + */ + if (rc == 0) { + if (bytes_written == 0) + rc = -ENOSPC; + else if (bytes_written < bytes_to_write) + rc = -EAGAIN; + } + + /* retry on data-integrity flush */ + if (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN) + goto retry_write; + + /* fix the stats and EOF */ + if (bytes_written > 0) { cifs_stats_bytes_written(tcon, bytes_written); + cifs_update_eof(cifsi, offset, bytes_written); } for (i = 0; i < n_iov; i++) { page = pvec.pages[first + i]; - /* Should we also set page error on - success rc but too little data written? */ - /* BB investigate retry logic on temporary - server crash cases and how recovery works - when page marked as error */ - if (rc) + /* on retryable write error, redirty page */ + if (rc == -EAGAIN) + redirty_page_for_writepage(wbc, page); + else if (rc != 0) SetPageError(page); kunmap(page); unlock_page(page); end_page_writeback(page); page_cache_release(page); } + + if (rc != -EAGAIN) + mapping_set_error(mapping, rc); + else + rc = 0; + if ((wbc->nr_to_write -= n_iov) <= 0) done = 1; index = next; -- cgit v1.2.3 From 9d78315b03fc91228306db42edc533efa69cb518 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:01 -0500 Subject: cifs: no need to mark smb_ses_list as cifs_demultiplex_thread is exiting The TCP_Server_Info is refcounted and every SMB session holds a reference to it. Thus, smb_ses_list is always going to be empty when cifsd is coming down. This is dead code. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 9f59887badd2..75b538f50b12 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -346,7 +346,6 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) struct kvec iov; struct socket *csocket = server->ssocket; struct list_head *tmp; - struct cifsSesInfo *ses; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; char temp; @@ -677,44 +676,19 @@ multi_t2_fnd: if (smallbuf) /* no sense logging a debug message if NULL */ cifs_small_buf_release(smallbuf); - /* - * BB: we shouldn't have to do any of this. It shouldn't be - * possible to exit from the thread with active SMB sessions - */ - spin_lock(&cifs_tcp_ses_lock); - if (list_empty(&server->pending_mid_q)) { - /* loop through server session structures attached to this and - mark them dead */ - list_for_each(tmp, &server->smb_ses_list) { - ses = list_entry(tmp, struct cifsSesInfo, - smb_ses_list); - ses->status = CifsExiting; - ses->server = NULL; - } - spin_unlock(&cifs_tcp_ses_lock); - } else { - /* although we can not zero the server struct pointer yet, - since there are active requests which may depnd on them, - mark the corresponding SMB sessions as exiting too */ - list_for_each(tmp, &server->smb_ses_list) { - ses = list_entry(tmp, struct cifsSesInfo, - smb_ses_list); - ses->status = CifsExiting; - } - + if (!list_empty(&server->pending_mid_q)) { spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { - mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if (mid_entry->midState == MID_REQUEST_SUBMITTED) { cFYI(1, "Clearing Mid 0x%x - waking up ", - mid_entry->mid); + mid_entry->mid); task_to_wake = mid_entry->tsk; if (task_to_wake) wake_up_process(task_to_wake); } } spin_unlock(&GlobalMid_Lock); - spin_unlock(&cifs_tcp_ses_lock); /* 1/8th of sec is more than enough time for them to exit */ msleep(125); } @@ -732,18 +706,6 @@ multi_t2_fnd: coming home not much else we can do but free the memory */ } - /* last chance to mark ses pointers invalid - if there are any pointing to this (e.g - if a crazy root user tried to kill cifsd - kernel thread explicitly this might happen) */ - /* BB: This shouldn't be necessary, see above */ - spin_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &server->smb_ses_list) { - ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); - ses->server = NULL; - } - spin_unlock(&cifs_tcp_ses_lock); - kfree(server->hostname); task_to_wake = xchg(&server->tsk, NULL); kfree(server); -- cgit v1.2.3 From c5797a945cac4c470f0113fc839c521aab0d799d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:01 -0500 Subject: cifs: make wait_for_free_request take a TCP_Server_Info pointer The cifsSesInfo pointer is only used to get at the server. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 59ca81b16919..9a14f77e0ab2 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -244,31 +244,31 @@ smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer, return smb_sendv(server, &iov, 1); } -static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) +static int wait_for_free_request(struct TCP_Server_Info *server, + const int long_op) { if (long_op == CIFS_ASYNC_OP) { /* oplock breaks must not be held up */ - atomic_inc(&ses->server->inFlight); + atomic_inc(&server->inFlight); return 0; } spin_lock(&GlobalMid_Lock); while (1) { - if (atomic_read(&ses->server->inFlight) >= - cifs_max_pending){ + if (atomic_read(&server->inFlight) >= cifs_max_pending) { spin_unlock(&GlobalMid_Lock); #ifdef CONFIG_CIFS_STATS2 - atomic_inc(&ses->server->num_waiters); + atomic_inc(&server->num_waiters); #endif - wait_event(ses->server->request_q, - atomic_read(&ses->server->inFlight) + wait_event(server->request_q, + atomic_read(&server->inFlight) < cifs_max_pending); #ifdef CONFIG_CIFS_STATS2 - atomic_dec(&ses->server->num_waiters); + atomic_dec(&server->num_waiters); #endif spin_lock(&GlobalMid_Lock); } else { - if (ses->server->tcpStatus == CifsExiting) { + if (server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); return -ENOENT; } @@ -278,7 +278,7 @@ static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) /* update # of requests on the wire to server */ if (long_op != CIFS_BLOCKING_OP) - atomic_inc(&ses->server->inFlight); + atomic_inc(&server->inFlight); spin_unlock(&GlobalMid_Lock); break; } @@ -413,7 +413,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, to the same server. We may make this configurable later or use ses->maxReq */ - rc = wait_for_free_request(ses, long_op); + rc = wait_for_free_request(ses->server, long_op); if (rc) { cifs_small_buf_release(in_buf); return rc; @@ -610,7 +610,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, return -EIO; } - rc = wait_for_free_request(ses, long_op); + rc = wait_for_free_request(ses->server, long_op); if (rc) return rc; @@ -845,7 +845,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, return -EIO; } - rc = wait_for_free_request(ses, CIFS_BLOCKING_OP); + rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP); if (rc) return rc; -- cgit v1.2.3 From 8097531a5cb55c6472118da094dc88caf9be66ac Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:02 -0500 Subject: cifs: clean up accesses to midCount It's an atomic_t and the code accesses the "counter" field in it directly instead of using atomic_read(). It also is sometimes accessed under a spinlock and sometimes not. Move it out of the spinlock since we don't need belt-and-suspenders for something that's just informational. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 2 +- fs/cifs/connect.c | 2 +- fs/cifs/transport.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index ede98300a8cd..e2d0d5d455fa 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -331,7 +331,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) atomic_read(&totSmBufAllocCount)); #endif /* CONFIG_CIFS_STATS2 */ - seq_printf(m, "Operations (MIDs): %d\n", midCount.counter); + seq_printf(m, "Operations (MIDs): %d\n", atomic_read(&midCount)); seq_printf(m, "\n%d session %d share reconnects\n", tcpSesReconnectCount.counter, tconInfoReconnectCount.counter); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 75b538f50b12..465ecad6d7cc 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -628,7 +628,7 @@ multi_t2_fnd: } else if (!is_valid_oplock_break(smb_buffer, server) && !isMultiRsp) { cERROR(1, "No task to wake, unknown frame received! " - "NumMids %d", midCount.counter); + "NumMids %d", atomic_read(&midCount)); cifs_dump_mem("Received Data is: ", (char *)smb_buffer, sizeof(struct smb_hdr)); #ifdef CONFIG_CIFS_DEBUG2 diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 9a14f77e0ab2..b9eb0cffa003 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -61,10 +61,10 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) temp->tsk = current; } - spin_lock(&GlobalMid_Lock); - list_add_tail(&temp->qhead, &server->pending_mid_q); atomic_inc(&midCount); temp->midState = MID_REQUEST_ALLOCATED; + spin_lock(&GlobalMid_Lock); + list_add_tail(&temp->qhead, &server->pending_mid_q); spin_unlock(&GlobalMid_Lock); return temp; } @@ -78,8 +78,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) spin_lock(&GlobalMid_Lock); midEntry->midState = MID_FREE; list_del(&midEntry->qhead); - atomic_dec(&midCount); spin_unlock(&GlobalMid_Lock); + atomic_dec(&midCount); if (midEntry->largeBuf) cifs_buf_release(midEntry->resp_buf); else -- cgit v1.2.3 From ddc8cf8fc718587a3788330bf4f32b379f08b250 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:02 -0500 Subject: cifs: move locked sections out of DeleteMidQEntry and AllocMidQEntry In later patches, we're going to need to have finer-grained control over the addition and removal of these structs from the pending_mid_q and we'll need to be able to call the destructor while holding the spinlock. Move the locked sections out of both routines and into the callers. Fix up current callers of DeleteMidQEntry to call a new routine that dequeues the entry and then destroys it. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index b9eb0cffa003..801726b11e1e 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -63,9 +63,6 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) atomic_inc(&midCount); temp->midState = MID_REQUEST_ALLOCATED; - spin_lock(&GlobalMid_Lock); - list_add_tail(&temp->qhead, &server->pending_mid_q); - spin_unlock(&GlobalMid_Lock); return temp; } @@ -75,10 +72,7 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) #ifdef CONFIG_CIFS_STATS2 unsigned long now; #endif - spin_lock(&GlobalMid_Lock); midEntry->midState = MID_FREE; - list_del(&midEntry->qhead); - spin_unlock(&GlobalMid_Lock); atomic_dec(&midCount); if (midEntry->largeBuf) cifs_buf_release(midEntry->resp_buf); @@ -103,6 +97,16 @@ DeleteMidQEntry(struct mid_q_entry *midEntry) mempool_free(midEntry, cifs_mid_poolp); } +static void +delete_mid(struct mid_q_entry *mid) +{ + spin_lock(&GlobalMid_Lock); + list_del(&mid->qhead); + spin_unlock(&GlobalMid_Lock); + + DeleteMidQEntry(mid); +} + static int smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) { @@ -308,6 +312,9 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, *ppmidQ = AllocMidQEntry(in_buf, ses->server); if (*ppmidQ == NULL) return -ENOMEM; + spin_lock(&GlobalMid_Lock); + list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q); + spin_unlock(&GlobalMid_Lock); return 0; } @@ -508,7 +515,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, } } spin_unlock(&GlobalMid_Lock); - DeleteMidQEntry(midQ); + delete_mid(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); @@ -564,14 +571,14 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if ((flags & CIFS_NO_RESP) == 0) midQ->resp_buf = NULL; /* mark it so buf will not be freed by - DeleteMidQEntry */ + delete_mid */ } else { rc = -EIO; cFYI(1, "Bad MID state?"); } out: - DeleteMidQEntry(midQ); + delete_mid(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); @@ -699,7 +706,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, } } spin_unlock(&GlobalMid_Lock); - DeleteMidQEntry(midQ); + delete_mid(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); @@ -755,7 +762,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, } out: - DeleteMidQEntry(midQ); + delete_mid(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); @@ -863,7 +870,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { - DeleteMidQEntry(midQ); + delete_mid(midQ); mutex_unlock(&ses->server->srv_mutex); return rc; } @@ -880,7 +887,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { - DeleteMidQEntry(midQ); + delete_mid(midQ); return rc; } @@ -902,7 +909,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, rc = send_nt_cancel(tcon, in_buf, midQ); if (rc) { - DeleteMidQEntry(midQ); + delete_mid(midQ); return rc; } } else { @@ -914,7 +921,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, /* If we get -ENOLCK back the lock may have already been removed. Don't exit in this case. */ if (rc && rc != -ENOLCK) { - DeleteMidQEntry(midQ); + delete_mid(midQ); return rc; } } @@ -951,7 +958,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, } } spin_unlock(&GlobalMid_Lock); - DeleteMidQEntry(midQ); + delete_mid(midQ); return rc; } @@ -1001,7 +1008,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); out: - DeleteMidQEntry(midQ); + delete_mid(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; return rc; -- cgit v1.2.3 From 053d50344568e5a4054266b44040297531125281 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:02 -0500 Subject: cifs: move mid result processing into common function Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 121 +++++++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 78 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 801726b11e1e..15059c7ef2ae 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -389,6 +389,42 @@ SendReceiveNoRsp(const unsigned int xid, struct cifsSesInfo *ses, return rc; } +static int +sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) +{ + int rc = 0; + + spin_lock(&GlobalMid_Lock); + + if (mid->resp_buf) { + spin_unlock(&GlobalMid_Lock); + return rc; + } + + cERROR(1, "No response to cmd %d mid %d", mid->command, mid->mid); + if (mid->midState == MID_REQUEST_SUBMITTED) { + if (server->tcpStatus == CifsExiting) + rc = -EHOSTDOWN; + else { + server->tcpStatus = CifsNeedReconnect; + mid->midState = MID_RETRY_NEEDED; + } + } + + if (rc != -EHOSTDOWN) { + if (mid->midState == MID_RETRY_NEEDED) { + rc = -EAGAIN; + cFYI(1, "marking request for retry"); + } else { + rc = -EIO; + } + } + spin_unlock(&GlobalMid_Lock); + + delete_mid(mid); + return rc; +} + int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, @@ -492,37 +528,13 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); - spin_lock(&GlobalMid_Lock); - - if (midQ->resp_buf == NULL) { - cERROR(1, "No response to cmd %d mid %d", - midQ->command, midQ->mid); - if (midQ->midState == MID_REQUEST_SUBMITTED) { - if (ses->server->tcpStatus == CifsExiting) - rc = -EHOSTDOWN; - else { - ses->server->tcpStatus = CifsNeedReconnect; - midQ->midState = MID_RETRY_NEEDED; - } - } - - if (rc != -EHOSTDOWN) { - if (midQ->midState == MID_RETRY_NEEDED) { - rc = -EAGAIN; - cFYI(1, "marking request for retry"); - } else { - rc = -EIO; - } - } - spin_unlock(&GlobalMid_Lock); - delete_mid(midQ); - /* Update # of requests on wire to server */ + rc = sync_mid_result(midQ, ses->server); + if (rc != 0) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } - spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { @@ -684,36 +696,13 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); - spin_lock(&GlobalMid_Lock); - if (midQ->resp_buf == NULL) { - cERROR(1, "No response for cmd %d mid %d", - midQ->command, midQ->mid); - if (midQ->midState == MID_REQUEST_SUBMITTED) { - if (ses->server->tcpStatus == CifsExiting) - rc = -EHOSTDOWN; - else { - ses->server->tcpStatus = CifsNeedReconnect; - midQ->midState = MID_RETRY_NEEDED; - } - } - - if (rc != -EHOSTDOWN) { - if (midQ->midState == MID_RETRY_NEEDED) { - rc = -EAGAIN; - cFYI(1, "marking request for retry"); - } else { - rc = -EIO; - } - } - spin_unlock(&GlobalMid_Lock); - delete_mid(midQ); - /* Update # of requests on wire to server */ + rc = sync_mid_result(midQ, ses->server); + if (rc != 0) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } - spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { @@ -933,35 +922,11 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, } } - spin_lock(&GlobalMid_Lock); - if (midQ->resp_buf) { - spin_unlock(&GlobalMid_Lock); - receive_len = midQ->resp_buf->smb_buf_length; - } else { - cERROR(1, "No response for cmd %d mid %d", - midQ->command, midQ->mid); - if (midQ->midState == MID_REQUEST_SUBMITTED) { - if (ses->server->tcpStatus == CifsExiting) - rc = -EHOSTDOWN; - else { - ses->server->tcpStatus = CifsNeedReconnect; - midQ->midState = MID_RETRY_NEEDED; - } - } - - if (rc != -EHOSTDOWN) { - if (midQ->midState == MID_RETRY_NEEDED) { - rc = -EAGAIN; - cFYI(1, "marking request for retry"); - } else { - rc = -EIO; - } - } - spin_unlock(&GlobalMid_Lock); - delete_mid(midQ); + rc = sync_mid_result(midQ, ses->server); + if (rc != 0) return rc; - } + receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, "Frame too large received. Length: %d Xid: %d", receive_len, xid); -- cgit v1.2.3 From 1cd3508d5eab6a88fa643119cedd34b04215cffe Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 19 Jan 2011 17:53:44 +0000 Subject: [CIFS] Update cifs version number Signed-off-by: Steve French --- fs/cifs/cifsfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 851030f74939..4739a531cded 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -118,5 +118,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.68" +#define CIFS_VERSION "1.69" #endif /* _CIFSFS_H */ -- cgit v1.2.3 From 540b2e377797d8715469d408b887baa9310c5f3e Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Tue, 18 Jan 2011 22:33:54 -0600 Subject: cifs: Fix regression during share-level security mounts (Repost) NTLM response length was changed to 16 bytes instead of 24 bytes that are sent in Tree Connection Request during share-level security share mounts. Revert it back to 24 bytes. Reported-and-Tested-by: Grzegorz Ozanski Acked-by: Jeff Layton Signed-off-by: Shirish Pargaonkar Acked-by: Suresh Jayaraman Cc: stable@kernel.org Signed-off-by: Steve French --- fs/cifs/connect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 465ecad6d7cc..5c7f8450dbe0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2927,7 +2927,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr++; /* skip password */ /* already aligned so no need to do it below */ } else { - pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); + pSMB->PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); /* BB FIXME add code to fail this if NTLMv2 or Kerberos specified as required (when that support is added to the vfs in the future) as only NTLM or the much @@ -2945,7 +2945,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, #endif /* CIFS_WEAK_PW_HASH */ SMBNTencrypt(tcon->password, ses->server->cryptkey, bcc_ptr); - bcc_ptr += CIFS_SESS_KEY_SIZE; + bcc_ptr += CIFS_AUTH_RESP_SIZE; if (ses->capabilities & CAP_UNICODE) { /* must align unicode strings */ *bcc_ptr = 0; /* null byte password */ -- cgit v1.2.3 From 05b5ca9b100300c8b98429962071aa66c5d2460e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 18 Jan 2011 12:42:23 -0800 Subject: omap1: Fix booting for 15xx and 730 with omap1_defconfig For omap15xx and 730 we need to use the MPU timer as the 32K timer is not available. For omap16xx we want to use the 32K timer because of PM. Fix this by allowing to build in both timers. Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/Kconfig | 2 ++ arch/arm/mach-omap1/Makefile | 3 +- arch/arm/mach-omap1/time.c | 48 +++++++++++++++++++++++--------- arch/arm/mach-omap1/timer32k.c | 13 ++++----- arch/arm/plat-omap/Kconfig | 8 ++---- arch/arm/plat-omap/counter_32k.c | 5 ---- arch/arm/plat-omap/include/plat/common.h | 1 + 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 8d2f2daba0c0..e0a028161dde 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -9,6 +9,7 @@ config ARCH_OMAP730 depends on ARCH_OMAP1 bool "OMAP730 Based System" select CPU_ARM926T + select OMAP_MPU_TIMER select ARCH_OMAP_OTG config ARCH_OMAP850 @@ -22,6 +23,7 @@ config ARCH_OMAP15XX default y bool "OMAP15xx Based System" select CPU_ARM925T + select OMAP_MPU_TIMER config ARCH_OMAP16XX depends on ARCH_OMAP1 diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index 6ee19504845f..ba6009f27677 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -3,12 +3,11 @@ # # Common support -obj-y := io.o id.o sram.o irq.o mux.o flash.o serial.o devices.o dma.o +obj-y := io.o id.o sram.o time.o irq.o mux.o flash.o serial.o devices.o dma.o obj-y += clock.o clock_data.o opp_data.o obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o -obj-$(CONFIG_OMAP_MPU_TIMER) += time.o obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o # Power Management diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index a39a15e4f3f9..b03f34d55d88 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -57,6 +57,8 @@ #include +#ifdef CONFIG_OMAP_MPU_TIMER + #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE #define OMAP_MPU_TIMER_OFFSET 0x100 @@ -236,12 +238,7 @@ static void __init omap_init_clocksource(unsigned long rate) printk(err, clocksource_mpu.name); } -/* - * --------------------------------------------------------------------------- - * Timer initialization - * --------------------------------------------------------------------------- - */ -static void __init omap_timer_init(void) +static void __init omap_mpu_timer_init(void) { struct clk *ck_ref = clk_get(NULL, "ck_ref"); unsigned long rate; @@ -256,13 +253,38 @@ static void __init omap_timer_init(void) omap_init_mpu_timer(rate); omap_init_clocksource(rate); - /* - * XXX Since this file seems to deal mostly with the MPU timer, - * this doesn't seem like the correct place for the sync timer - * clocksource init. - */ - if (!cpu_is_omap7xx() && !cpu_is_omap15xx()) - omap_init_clocksource_32k(); +} + +#else +static inline void omap_mpu_timer_init(void) +{ + pr_err("Bogus timer, should not happen\n"); +} +#endif /* CONFIG_OMAP_MPU_TIMER */ + +static inline int omap_32k_timer_usable(void) +{ + int res = false; + + if (cpu_is_omap730() || cpu_is_omap15xx()) + return res; + +#ifdef CONFIG_OMAP_32K_TIMER + res = omap_32k_timer_init(); +#endif + + return res; +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init omap_timer_init(void) +{ + if (!omap_32k_timer_usable()) + omap_mpu_timer_init(); } struct sys_timer omap_timer = { diff --git a/arch/arm/mach-omap1/timer32k.c b/arch/arm/mach-omap1/timer32k.c index 20cfbcc6c60c..13d7b8f145bd 100644 --- a/arch/arm/mach-omap1/timer32k.c +++ b/arch/arm/mach-omap1/timer32k.c @@ -52,10 +52,9 @@ #include #include #include +#include #include -struct sys_timer omap_timer; - /* * --------------------------------------------------------------------------- * 32KHz OS timer @@ -181,14 +180,14 @@ static __init void omap_init_32k_timer(void) * Timer initialization * --------------------------------------------------------------------------- */ -static void __init omap_timer_init(void) +bool __init omap_32k_timer_init(void) { + omap_init_clocksource_32k(); + #ifdef CONFIG_OMAP_DM_TIMER omap_dm_timer_init(); #endif omap_init_32k_timer(); -} -struct sys_timer omap_timer = { - .init = omap_timer_init, -}; + return true; +} diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 18fe3cb195dc..b6333ae3f92a 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -144,12 +144,9 @@ config OMAP_IOMMU_DEBUG config OMAP_IOMMU_IVA2 bool -choice - prompt "System timer" - default OMAP_32K_TIMER if !ARCH_OMAP15XX - config OMAP_MPU_TIMER bool "Use mpu timer" + depends on ARCH_OMAP1 help Select this option if you want to use the OMAP mpu timer. This timer provides more intra-tick resolution than the 32KHz timer, @@ -158,6 +155,7 @@ config OMAP_MPU_TIMER config OMAP_32K_TIMER bool "Use 32KHz timer" depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS + default y if (ARCH_OMAP16XX || ARCH_OMAP2PLUS) help Select this option if you want to enable the OMAP 32KHz timer. This timer saves power compared to the OMAP_MPU_TIMER, and has @@ -165,8 +163,6 @@ config OMAP_32K_TIMER intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is currently only available for OMAP16XX, 24XX, 34XX and OMAP4. -endchoice - config OMAP3_L2_AUX_SECURE_SAVE_RESTORE bool "OMAP3 HS/EMU save and restore for L2 AUX control register" depends on ARCH_OMAP3 && PM diff --git a/arch/arm/plat-omap/counter_32k.c b/arch/arm/plat-omap/counter_32k.c index 0367998ff685..5d7b08b5a13a 100644 --- a/arch/arm/plat-omap/counter_32k.c +++ b/arch/arm/plat-omap/counter_32k.c @@ -36,8 +36,6 @@ #define OMAP16XX_TIMER_32K_SYNCHRONIZED 0xfffbc410 -#if !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX)) - #include /* @@ -195,6 +193,3 @@ int __init omap_init_clocksource_32k(void) } return 0; } - -#endif /* !(defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP15XX)) */ - diff --git a/arch/arm/plat-omap/include/plat/common.h b/arch/arm/plat-omap/include/plat/common.h index 84c707f713b1..ef683e01701b 100644 --- a/arch/arm/plat-omap/include/plat/common.h +++ b/arch/arm/plat-omap/include/plat/common.h @@ -35,6 +35,7 @@ struct sys_timer; extern void omap_map_common_io(void); extern struct sys_timer omap_timer; +extern bool omap_32k_timer_init(void); extern int __init omap_init_clocksource_32k(void); extern void omap_reserve(void); -- cgit v1.2.3 From 4912cf04b202a9d0bdc4082ecb9247943584450d Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 18 Jan 2011 17:00:00 -0800 Subject: omap1: Fix sched_clock implementation when both MPU timer and 32K timer are used Earlier patches select HAVE_SCHED_CLOCK for omaps. To have working sched_clock also for MPU timer, we need to implement it in a way where the right one gets selected during the runtime. Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/time.c | 48 +++++++++++++++++++++++++++++++- arch/arm/plat-omap/counter_32k.c | 14 +++++++++- arch/arm/plat-omap/include/plat/common.h | 1 + 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index b03f34d55d88..f83fc335c613 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -219,6 +219,24 @@ static struct clocksource clocksource_mpu = { static DEFINE_CLOCK_DATA(cd); +static inline unsigned long long notrace _omap_mpu_sched_clock(void) +{ + u32 cyc = mpu_read(&clocksource_mpu); + return cyc_to_sched_clock(&cd, cyc, (u32)~0); +} + +#ifndef CONFIG_OMAP_32K_TIMER +unsigned long long notrace sched_clock(void) +{ + return _omap_mpu_sched_clock(); +} +#else +static unsigned long long notrace omap_mpu_sched_clock(void) +{ + return _omap_mpu_sched_clock(); +} +#endif + static void notrace mpu_update_sched_clock(void) { u32 cyc = mpu_read(&clocksource_mpu); @@ -262,6 +280,30 @@ static inline void omap_mpu_timer_init(void) } #endif /* CONFIG_OMAP_MPU_TIMER */ +#if defined(CONFIG_OMAP_MPU_TIMER) && defined(CONFIG_OMAP_32K_TIMER) +static unsigned long long (*preferred_sched_clock)(void); + +unsigned long long notrace sched_clock(void) +{ + if (!preferred_sched_clock) + return 0; + + return preferred_sched_clock(); +} + +static inline void preferred_sched_clock_init(bool use_32k_sched_clock) +{ + if (use_32k_sched_clock) + preferred_sched_clock = omap_32k_sched_clock; + else + preferred_sched_clock = omap_mpu_sched_clock; +} +#else +static inline void preferred_sched_clock_init(bool use_32k_sched_clcok) +{ +} +#endif + static inline int omap_32k_timer_usable(void) { int res = false; @@ -283,8 +325,12 @@ static inline int omap_32k_timer_usable(void) */ static void __init omap_timer_init(void) { - if (!omap_32k_timer_usable()) + if (omap_32k_timer_usable()) { + preferred_sched_clock_init(1); + } else { omap_mpu_timer_init(); + preferred_sched_clock_init(0); + } } struct sys_timer omap_timer = { diff --git a/arch/arm/plat-omap/counter_32k.c b/arch/arm/plat-omap/counter_32k.c index 5d7b08b5a13a..862dda95d61d 100644 --- a/arch/arm/plat-omap/counter_32k.c +++ b/arch/arm/plat-omap/counter_32k.c @@ -120,12 +120,24 @@ static DEFINE_CLOCK_DATA(cd); #define SC_MULT 4000000000u #define SC_SHIFT 17 -unsigned long long notrace sched_clock(void) +static inline unsigned long long notrace _omap_32k_sched_clock(void) { u32 cyc = clocksource_32k.read(&clocksource_32k); return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT); } +#ifndef CONFIG_OMAP_MPU_TIMER +unsigned long long notrace sched_clock(void) +{ + return _omap_32k_sched_clock(); +} +#else +unsigned long long notrace omap_32k_sched_clock(void) +{ + return _omap_32k_sched_clock(); +} +#endif + static void notrace omap_update_sched_clock(void) { u32 cyc = clocksource_32k.read(&clocksource_32k); diff --git a/arch/arm/plat-omap/include/plat/common.h b/arch/arm/plat-omap/include/plat/common.h index ef683e01701b..29b2afb4288f 100644 --- a/arch/arm/plat-omap/include/plat/common.h +++ b/arch/arm/plat-omap/include/plat/common.h @@ -37,6 +37,7 @@ extern void omap_map_common_io(void); extern struct sys_timer omap_timer; extern bool omap_32k_timer_init(void); extern int __init omap_init_clocksource_32k(void); +extern unsigned long long notrace omap_32k_sched_clock(void); extern void omap_reserve(void); -- cgit v1.2.3 From dbe08d82ce3967ccdf459f7951d02589cf967300 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 19 Jan 2011 19:22:07 +0100 Subject: perf: Fix find_get_context() vs perf_event_exit_task() race find_get_context() must not install the new perf_event_context if the task has already passed perf_event_exit_task(). If nothing else, this means the memory leak. Initially ctx->refcount == 2, it is supposed that perf_event_exit_task_context() should participate and do the necessary put_ctx(). find_lively_task_by_vpid() checks PF_EXITING but this buys nothing, by the time we call find_get_context() this task can be already dead. To the point, cmpxchg() can succeed when the task has already done the last schedule(). Change find_get_context() to populate task->perf_event_ctxp[] under task->perf_event_mutex, this way we can trust PF_EXITING because perf_event_exit_task() takes the same mutex. Also, change perf_event_exit_task_context() to use rcu_dereference(). Probably this is not strictly needed, but with or without this change find_get_context() can race with setup_new_exec()->perf_event_exit_task(), rcu_dereference() looks better. Signed-off-by: Oleg Nesterov Acked-by: Peter Zijlstra Cc: Alan Stern Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Prasad Cc: Roland McGrath LKML-Reference: <20110119182207.GB12183@redhat.com> Signed-off-by: Ingo Molnar --- kernel/perf_event.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 84522c796987..4ec55ef5810c 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -2201,13 +2201,6 @@ find_lively_task_by_vpid(pid_t vpid) if (!task) return ERR_PTR(-ESRCH); - /* - * Can't attach events to a dying task. - */ - err = -ESRCH; - if (task->flags & PF_EXITING) - goto errout; - /* Reuse ptrace permission checks for now. */ err = -EACCES; if (!ptrace_may_access(task, PTRACE_MODE_READ)) @@ -2268,14 +2261,27 @@ retry: get_ctx(ctx); - if (cmpxchg(&task->perf_event_ctxp[ctxn], NULL, ctx)) { - /* - * We raced with some other task; use - * the context they set. - */ + err = 0; + mutex_lock(&task->perf_event_mutex); + /* + * If it has already passed perf_event_exit_task(). + * we must see PF_EXITING, it takes this mutex too. + */ + if (task->flags & PF_EXITING) + err = -ESRCH; + else if (task->perf_event_ctxp[ctxn]) + err = -EAGAIN; + else + rcu_assign_pointer(task->perf_event_ctxp[ctxn], ctx); + mutex_unlock(&task->perf_event_mutex); + + if (unlikely(err)) { put_task_struct(task); kfree(ctx); - goto retry; + + if (err == -EAGAIN) + goto retry; + goto errout; } } @@ -6127,7 +6133,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) * scheduled, so we are now safe from rescheduling changing * our context. */ - child_ctx = child->perf_event_ctxp[ctxn]; + child_ctx = rcu_dereference(child->perf_event_ctxp[ctxn]); task_ctx_sched_out(child_ctx, EVENT_ALL); /* -- cgit v1.2.3 From 8550d7cb6ed6c89add49c3b6ad4c753ab8a3d7f9 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 19 Jan 2011 19:22:28 +0100 Subject: perf: Fix perf_event_init_task()/perf_event_free_task() interaction perf_event_init_task() should clear child->perf_event_ctxp[] before anything else. Otherwise, if perf_event_init_context(perf_hw_context) fails, perf_event_free_task() can free perf_event_ctxp[perf_sw_context] copied from parent->perf_event_ctxp[] by dup_task_struct(). Also move the initialization of perf_event_mutex and perf_event_list from perf_event_init_context() to perf_event_init_context(). Signed-off-by: Oleg Nesterov Acked-by: Peter Zijlstra Cc: Alan Stern Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Prasad Cc: Roland McGrath LKML-Reference: <20110119182228.GC12183@redhat.com> Signed-off-by: Ingo Molnar --- kernel/perf_event.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 4ec55ef5810c..244ca3acb0ee 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -6446,11 +6446,6 @@ int perf_event_init_context(struct task_struct *child, int ctxn) unsigned long flags; int ret = 0; - child->perf_event_ctxp[ctxn] = NULL; - - mutex_init(&child->perf_event_mutex); - INIT_LIST_HEAD(&child->perf_event_list); - if (likely(!parent->perf_event_ctxp[ctxn])) return 0; @@ -6539,6 +6534,10 @@ int perf_event_init_task(struct task_struct *child) { int ctxn, ret; + memset(child->perf_event_ctxp, 0, sizeof(child->perf_event_ctxp)); + mutex_init(&child->perf_event_mutex); + INIT_LIST_HEAD(&child->perf_event_list); + for_each_task_context_nr(ctxn) { ret = perf_event_init_context(child, ctxn); if (ret) -- cgit v1.2.3 From 8c8a9b25b5de3f1eeac721cf34f4379e56d5d694 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 18 Jan 2011 21:44:04 +1100 Subject: powerpc, perf: Fix frequency calculation for overflowing counters (FSL version) When fixing the frequency calculations for perf on powerpc I forgot to fix the FSL version. If we dont set event->hw.last_period the frequency to period calculations in perf go haywire and we continually throttle/unthrottle the PMU. Signed-off-by: Anton Blanchard Acked-by: Benjamin Herrenschmidt Cc: Scott Wood Cc: Paul Mackerras Cc: Arnaldo Carvalho de Melo Cc: linuxppc-dev@lists.ozlabs.org Cc: Peter Zijlstra LKML-Reference: <20110118214404.2f42e634@kryten> Signed-off-by: Ingo Molnar --- arch/powerpc/kernel/perf_event_fsl_emb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/kernel/perf_event_fsl_emb.c b/arch/powerpc/kernel/perf_event_fsl_emb.c index 4dcf5f831e9d..b0dc8f7069cd 100644 --- a/arch/powerpc/kernel/perf_event_fsl_emb.c +++ b/arch/powerpc/kernel/perf_event_fsl_emb.c @@ -596,6 +596,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val, if (left <= 0) left = period; record = 1; + event->hw.last_period = event->hw.sample_period; } if (left < 0x80000000LL) val = 0x80000000LL - left; -- cgit v1.2.3 From 2d0640b47da74cff7c11642c798d40de861ed524 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 18 Jan 2011 22:46:34 -0800 Subject: hrtimers: Notify hrtimer users of switches to NOHZ mode When NOHZ=y and high res timers are disabled (via cmdline or Kconfig) tick_nohz_switch_to_nohz() will notify the user about switching into NOHZ mode. Nothing is printed for the case where HIGH_RES_TIMERS=y. Fix this for the HIGH_RES_TIMERS=y case by duplicating the printk from the low res NOHZ path in the high res NOHZ path. This confused me since I was thinking 'dmesg | grep -i NOHZ' would tell me if NOHZ was enabled, but if I have hrtimers there is nothing. Signed-off-by: Stephen Boyd Acked-by: Thomas Gleixner Cc: Peter Zijlstra LKML-Reference: <1295419594-13085-1-git-send-email-sboyd@codeaurora.org> Signed-off-by: Ingo Molnar --- kernel/time/tick-sched.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 3e216e01bbd1..c55ea2433471 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -642,8 +642,7 @@ static void tick_nohz_switch_to_nohz(void) } local_irq_enable(); - printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", - smp_processor_id()); + printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id()); } /* @@ -795,8 +794,10 @@ void tick_setup_sched_timer(void) } #ifdef CONFIG_NO_HZ - if (tick_nohz_enabled) + if (tick_nohz_enabled) { ts->nohz_mode = NOHZ_MODE_HIGHRES; + printk(KERN_INFO "Switched to NOHz mode on CPU #%d\n", smp_processor_id()); + } #endif } #endif /* HIGH_RES_TIMERS */ -- cgit v1.2.3 From 2fbc2f1729e785a7b2faf9d8d60926bb1ff62af0 Mon Sep 17 00:00:00 2001 From: Shirish Pargaonkar Date: Mon, 6 Dec 2010 14:56:46 -0600 Subject: cifs: Use mask of ACEs for SID Everyone to calculate all three permissions user, group, and other If a DACL has entries for ACEs for SID Everyone and Authenticated Users, factor in mask in respective entries during calculation of permissions for all three, user, group, and other. http://technet.microsoft.com/en-us/library/bb463216.aspx Signed-off-by: Shirish Pargaonkar Reviewed-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsacl.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index a437ec391a01..1e7636b145a8 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -41,9 +41,12 @@ static struct cifs_wksid wksidarr[NUM_WK_SIDS] = { ; -/* security id for everyone */ +/* security id for everyone/world system group */ static const struct cifs_sid sid_everyone = { 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; +/* security id for Authenticated Users system group */ +static const struct cifs_sid sid_authusers = { + 1, 1, {0, 0, 0, 0, 0, 5}, {11} }; /* group users */ static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; @@ -365,7 +368,7 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, if (num_aces > 0) { umode_t user_mask = S_IRWXU; umode_t group_mask = S_IRWXG; - umode_t other_mask = S_IRWXO; + umode_t other_mask = S_IRWXU | S_IRWXG | S_IRWXO; ppace = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL); @@ -390,6 +393,12 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, ppace[i]->type, &fattr->cf_mode, &other_mask); + if (compare_sids(&(ppace[i]->sid), &sid_authusers)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &fattr->cf_mode, + &other_mask); + /* memcpy((void *)(&(cifscred->aces[i])), (void *)ppace[i], -- cgit v1.2.3 From b8f3ab4290f1e720166e888ea2a1d1d44c4d15dd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 18 Jan 2011 12:40:38 -0800 Subject: Revert "netlink: test for all flags of the NLM_F_DUMP composite" This reverts commit 0ab03c2b1478f2438d2c80204f7fef65b1bca9cf. It breaks several things including the avahi daemon. Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 2 +- net/ipv4/inet_diag.c | 2 +- net/netfilter/nf_conntrack_netlink.c | 4 ++-- net/netlink/genetlink.c | 2 +- net/xfrm/xfrm_user.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a5f7535aab5b..750db57f3bb3 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1820,7 +1820,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN)) return -EPERM; - if (kind == 2 && (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { + if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { struct sock *rtnl; rtnl_dumpit_func dumpit; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 2746c1fa6417..2ada17129fce 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -858,7 +858,7 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) nlmsg_len(nlh) < hdrlen) return -EINVAL; - if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { + if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlmsg_attrlen(nlh, hdrlen)) { struct nlattr *attr; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 2b7eef37875c..93297aaceb2b 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -924,7 +924,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, u16 zone; int err; - if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) + if (nlh->nlmsg_flags & NLM_F_DUMP) return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, ctnetlink_done); @@ -1787,7 +1787,7 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, u16 zone; int err; - if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { + if (nlh->nlmsg_flags & NLM_F_DUMP) { return netlink_dump_start(ctnl, skb, nlh, ctnetlink_exp_dump_table, ctnetlink_exp_done); diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index f83cb370292b..1781d99145e2 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -519,7 +519,7 @@ static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) security_netlink_recv(skb, CAP_NET_ADMIN)) return -EPERM; - if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { + if (nlh->nlmsg_flags & NLM_F_DUMP) { if (ops->dumpit == NULL) return -EOPNOTSUPP; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index d5e1e0b08890..61291965c5f6 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -2189,7 +2189,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) || type == (XFRM_MSG_GETPOLICY - XFRM_MSG_BASE)) && - (nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) { + (nlh->nlmsg_flags & NLM_F_DUMP)) { if (link->dump == NULL) return -EINVAL; -- cgit v1.2.3 From fec5568cbcf3b5d701d2adf9a22f0110bcc6eb33 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Thu, 13 Jan 2011 16:24:49 +0000 Subject: arm: omap3: cm-t3517: rtc fix Fix rtc gpios and mux Signed-off-by: Igor Grinberg Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-cm-t3517.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-omap2/board-cm-t3517.c b/arch/arm/mach-omap2/board-cm-t3517.c index 5b0c77732dfc..1706df0c51d2 100644 --- a/arch/arm/mach-omap2/board-cm-t3517.c +++ b/arch/arm/mach-omap2/board-cm-t3517.c @@ -124,8 +124,9 @@ static inline void cm_t3517_init_hecc(void) {} #if defined(CONFIG_RTC_DRV_V3020) || defined(CONFIG_RTC_DRV_V3020_MODULE) #define RTC_IO_GPIO (153) #define RTC_WR_GPIO (154) -#define RTC_RD_GPIO (160) +#define RTC_RD_GPIO (53) #define RTC_CS_GPIO (163) +#define RTC_CS_EN_GPIO (160) struct v3020_platform_data cm_t3517_v3020_pdata = { .use_gpio = 1, @@ -145,6 +146,16 @@ static struct platform_device cm_t3517_rtc_device = { static void __init cm_t3517_init_rtc(void) { + int err; + + err = gpio_request(RTC_CS_EN_GPIO, "rtc cs en"); + if (err) { + pr_err("CM-T3517: rtc cs en gpio request failed: %d\n", err); + return; + } + + gpio_direction_output(RTC_CS_EN_GPIO, 1); + platform_device_register(&cm_t3517_rtc_device); } #else @@ -256,11 +267,19 @@ static void __init cm_t3517_init_irq(void) static struct omap_board_mux board_mux[] __initdata = { /* GPIO186 - Green LED */ OMAP3_MUX(SYS_CLKOUT2, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT), - /* RTC GPIOs: IO, WR#, RD#, CS# */ + + /* RTC GPIOs: */ + /* IO - GPIO153 */ OMAP3_MUX(MCBSP4_DR, OMAP_MUX_MODE4 | OMAP_PIN_INPUT), + /* WR# - GPIO154 */ OMAP3_MUX(MCBSP4_DX, OMAP_MUX_MODE4 | OMAP_PIN_INPUT), - OMAP3_MUX(MCBSP_CLKS, OMAP_MUX_MODE4 | OMAP_PIN_INPUT), + /* RD# - GPIO53 */ + OMAP3_MUX(GPMC_NCS2, OMAP_MUX_MODE4 | OMAP_PIN_INPUT), + /* CS# - GPIO163 */ OMAP3_MUX(UART3_CTS_RCTX, OMAP_MUX_MODE4 | OMAP_PIN_INPUT), + /* CS EN - GPIO160 */ + OMAP3_MUX(MCBSP_CLKS, OMAP_MUX_MODE4 | OMAP_PIN_INPUT), + /* HSUSB1 RESET */ OMAP3_MUX(UART2_TX, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT), /* HSUSB2 RESET */ -- cgit v1.2.3 From 2216b436ed8788f1a6d51b136ed1421956aa18e0 Mon Sep 17 00:00:00 2001 From: Igor Grinberg Date: Thu, 13 Jan 2011 16:24:50 +0000 Subject: arm: omap3: cm-t3517: minor comment fix offsets in the comment were wrong - fix this. Signed-off-by: Igor Grinberg Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-cm-t3517.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/board-cm-t3517.c b/arch/arm/mach-omap2/board-cm-t3517.c index 1706df0c51d2..8f9a64d650ee 100644 --- a/arch/arm/mach-omap2/board-cm-t3517.c +++ b/arch/arm/mach-omap2/board-cm-t3517.c @@ -225,12 +225,12 @@ static struct mtd_partition cm_t3517_nand_partitions[] = { }, { .name = "linux", - .offset = MTDPART_OFS_APPEND, /* Offset = 0x280000 */ + .offset = MTDPART_OFS_APPEND, /* Offset = 0x2A0000 */ .size = 32 * NAND_BLOCK_SIZE, }, { .name = "rootfs", - .offset = MTDPART_OFS_APPEND, /* Offset = 0x680000 */ + .offset = MTDPART_OFS_APPEND, /* Offset = 0x6A0000 */ .size = MTDPART_SIZ_FULL, }, }; -- cgit v1.2.3 From d402786ea4f8433774a812d6b8635e737425cddd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 19 Jan 2011 00:51:36 +0000 Subject: net: fix can_checksum_protocol() arguments swap commit 0363466866d901fbc (net offloading: Convert checksums to use centrally computed features.) mistakenly swapped can_checksum_protocol() arguments. This broke IPv6 on bnx2 for instance, on NIC without TCPv6 checksum offloads. Reported-by: Hans de Bruin Signed-off-by: Eric Dumazet Acked-by: Jesse Gross Signed-off-by: David S. Miller --- net/core/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/dev.c b/net/core/dev.c index 4c58d11d3b68..8393ec408cd4 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2001,7 +2001,7 @@ static bool can_checksum_protocol(unsigned long features, __be16 protocol) static int harmonize_features(struct sk_buff *skb, __be16 protocol, int features) { - if (!can_checksum_protocol(protocol, features)) { + if (!can_checksum_protocol(features, protocol)) { features &= ~NETIF_F_ALL_CSUM; features &= ~NETIF_F_SG; } else if (illegal_highdma(skb->dev, skb)) { -- cgit v1.2.3 From 35a78fa48678926464138aab3a4dede4503befb5 Mon Sep 17 00:00:00 2001 From: Daniel Morsing Date: Wed, 19 Jan 2011 08:19:24 +0000 Subject: OMAP3: Devkit8000: Fix tps65930 pullup/pulldown configuration gpio7 on the tps65930 is used as an output on the devkit8000 and gpio1 is not connected. Remove gpio7 and change gpio1 to pulldown Signed-off-by: Daniel Morsing Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/board-devkit8000.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/board-devkit8000.c b/arch/arm/mach-omap2/board-devkit8000.c index 00bb1fc5e017..e906e05bb41b 100644 --- a/arch/arm/mach-omap2/board-devkit8000.c +++ b/arch/arm/mach-omap2/board-devkit8000.c @@ -275,8 +275,7 @@ static struct twl4030_gpio_platform_data devkit8000_gpio_data = { .irq_base = TWL4030_GPIO_IRQ_BASE, .irq_end = TWL4030_GPIO_IRQ_END, .use_leds = true, - .pullups = BIT(1), - .pulldowns = BIT(2) | BIT(6) | BIT(7) | BIT(8) | BIT(13) + .pulldowns = BIT(1) | BIT(2) | BIT(6) | BIT(8) | BIT(13) | BIT(15) | BIT(16) | BIT(17), .setup = devkit8000_twl_gpio_setup, }; -- cgit v1.2.3 From 4fb699b408b559107f1e1b47d176d33361446b69 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 24 Nov 2010 13:23:21 +0200 Subject: omap: DMA: clear interrupt status correctly When clearing the DMA channel, clear all status bits. When handling a DMA interrupt, clear only the interrupt status bits that have been read and are passed to the channel's interrupt handler, not every status bit. Signed-off-by: Adrian Hunter Acked-by: Santosh Shilimkar Acked-by: G, Manjunath Kondaiah Signed-off-by: Tony Lindgren --- arch/arm/plat-omap/dma.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index c4b2b478b1a5..85363084cc1a 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -53,7 +53,7 @@ enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED }; #endif #define OMAP_DMA_ACTIVE 0x01 -#define OMAP2_DMA_CSR_CLEAR_MASK 0xffe +#define OMAP2_DMA_CSR_CLEAR_MASK 0xffffffff #define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec) @@ -1873,7 +1873,7 @@ static int omap2_dma_handle_ch(int ch) printk(KERN_INFO "DMA misaligned error with device %d\n", dma_chan[ch].dev_id); - p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, ch); + p->dma_write(status, CSR, ch); p->dma_write(1 << ch, IRQSTATUS_L0, ch); /* read back the register to flush the write */ p->dma_read(IRQSTATUS_L0, ch); @@ -1893,10 +1893,9 @@ static int omap2_dma_handle_ch(int ch) OMAP_DMA_CHAIN_INCQHEAD(chain_id); status = p->dma_read(CSR, ch); + p->dma_write(status, CSR, ch); } - p->dma_write(status, CSR, ch); - if (likely(dma_chan[ch].callback != NULL)) dma_chan[ch].callback(ch, status, dma_chan[ch].data); -- cgit v1.2.3 From 4580ccc04ddd8c17a470573a7fdb8def2e036dfa Mon Sep 17 00:00:00 2001 From: Shan Wei Date: Tue, 18 Jan 2011 22:39:00 +0000 Subject: sctp: user perfect name for Delayed SACK Timer option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The option name of Delayed SACK Timer should be SCTP_DELAYED_SACK, not SCTP_DELAYED_ACK. Left SCTP_DELAYED_ACK be concomitant with SCTP_DELAYED_SACK, for making compatibility with existing applications. Reference: 8.1.19. Get or Set Delayed SACK Timer (SCTP_DELAYED_SACK) (http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-25) Signed-off-by: Shan Wei Acked-by: Wei Yongjun Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/user.h | 1 + net/sctp/socket.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index 2b2769c5ca9f..92eedc091517 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h @@ -78,6 +78,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_GET_PEER_ADDR_INFO 15 #define SCTP_DELAYED_ACK_TIME 16 #define SCTP_DELAYED_ACK SCTP_DELAYED_ACK_TIME +#define SCTP_DELAYED_SACK SCTP_DELAYED_ACK_TIME #define SCTP_CONTEXT 17 #define SCTP_FRAGMENT_INTERLEAVE 18 #define SCTP_PARTIAL_DELIVERY_POINT 19 /* Set/Get partial delivery point */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a09b0dd25f50..8e02550ff3e8 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3428,7 +3428,7 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen); break; - case SCTP_DELAYED_ACK: + case SCTP_DELAYED_SACK: retval = sctp_setsockopt_delayed_ack(sk, optval, optlen); break; case SCTP_PARTIAL_DELIVERY_POINT: @@ -5333,7 +5333,7 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_peer_addr_params(sk, len, optval, optlen); break; - case SCTP_DELAYED_ACK: + case SCTP_DELAYED_SACK: retval = sctp_getsockopt_delayed_ack(sk, len, optval, optlen); break; -- cgit v1.2.3 From fb228af7060d02a81a7bcc2ce329ba3ab1af0c7f Mon Sep 17 00:00:00 2001 From: David Henningsson Date: Wed, 19 Jan 2011 11:59:01 +0100 Subject: ALSA: HDA: Add SKU ignore for another Thinkpad Edge 14 BugLink: http://bugs.launchpad.net/bugs/705323 Thinkpad Edge 14 has one more SSID that suffers from disabled auto-mute. Signed-off-by: David Henningsson Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2b055e2780bb..5ea60c6d24aa 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -14955,6 +14955,7 @@ static struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), + SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), -- cgit v1.2.3 From 8aeb36e8f6d7eaa9cafc970b700414205743b258 Mon Sep 17 00:00:00 2001 From: Philip Sanderson Date: Thu, 20 Jan 2011 21:37:28 -0600 Subject: lguest: --username and --chroot options I've attached a patch which implements dropping to privileges and chrooting to a directory. Signed-off-by: Rusty Russell --- Documentation/lguest/lguest.c | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index dc73bc54cc4e..f64b85bcd6d4 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -39,6 +39,9 @@ #include #include #include +#include +#include + #include #include #include @@ -1872,6 +1875,8 @@ static struct option opts[] = { { "block", 1, NULL, 'b' }, { "rng", 0, NULL, 'r' }, { "initrd", 1, NULL, 'i' }, + { "username", 1, NULL, 'u' }, + { "chroot", 1, NULL, 'c' }, { NULL }, }; static void usage(void) @@ -1894,6 +1899,12 @@ int main(int argc, char *argv[]) /* If they specify an initrd file to load. */ const char *initrd_name = NULL; + /* Password structure for initgroups/setres[gu]id */ + struct passwd *user_details = NULL; + + /* Directory to chroot to */ + char *chroot_path = NULL; + /* Save the args: we "reboot" by execing ourselves again. */ main_args = argv; @@ -1950,6 +1961,14 @@ int main(int argc, char *argv[]) case 'i': initrd_name = optarg; break; + case 'u': + user_details = getpwnam(optarg); + if (!user_details) + err(1, "getpwnam failed, incorrect username?"); + break; + case 'c': + chroot_path = optarg; + break; default: warnx("Unknown argument %s", argv[optind]); usage(); @@ -2021,6 +2040,37 @@ int main(int argc, char *argv[]) /* If we exit via err(), this kills all the threads, restores tty. */ atexit(cleanup_devices); + /* If requested, chroot to a directory */ + if (chroot_path) { + if (chroot(chroot_path) != 0) + err(1, "chroot(\"%s\") failed", chroot_path); + + if (chdir("/") != 0) + err(1, "chdir(\"/\") failed"); + + verbose("chroot done\n"); + } + + /* If requested, drop privileges */ + if (user_details) { + uid_t u; + gid_t g; + + u = user_details->pw_uid; + g = user_details->pw_gid; + + if (initgroups(user_details->pw_name, g) != 0) + err(1, "initgroups failed"); + + if (setresgid(g, g, g) != 0) + err(1, "setresgid failed"); + + if (setresuid(u, u, u) != 0) + err(1, "setresuid failed"); + + verbose("Dropping privileges completed\n"); + } + /* Finally, run the Guest. This doesn't return. */ run_guest(); } -- cgit v1.2.3 From 5230ff0cccb0611830bb02b097535868df02752a Mon Sep 17 00:00:00 2001 From: Philip Sanderson Date: Thu, 20 Jan 2011 21:37:28 -0600 Subject: lguest: example launcher to use guard pages, drop PROT_EXEC, fix limit logic PROT_EXEC seems to be completely unnecessary (as the lguest binary never executes there), and will allow it to work with SELinux (and more importantly, PaX :-) as they can/do forbid writable and executable mappings. Also, map PROT_NONE guard pages at start and end of guest memory for extra paranoia. I changed the length check to addr + size > guest_limit because >= is wrong (addr of 0, size of getpagesize() with a guest_limit of getpagesize() would false positive). Signed-off-by: Rusty Russell --- Documentation/lguest/lguest.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index f64b85bcd6d4..d9da7e148538 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -301,20 +301,27 @@ static void *map_zeroed_pages(unsigned int num) /* * We use a private mapping (ie. if we write to the page, it will be - * copied). + * copied). We allocate an extra two pages PROT_NONE to act as guard + * pages against read/write attempts that exceed allocated space. */ - addr = mmap(NULL, getpagesize() * num, - PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, 0); + addr = mmap(NULL, getpagesize() * (num+2), + PROT_NONE, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) err(1, "Mmapping %u pages of /dev/zero", num); + if (mprotect(addr + getpagesize(), getpagesize() * num, + PROT_READ|PROT_WRITE) == -1) + err(1, "mprotect rw %u pages failed", num); + /* * One neat mmap feature is that you can close the fd, and it * stays mapped. */ close(fd); - return addr; + /* Return address after PROT_NONE page */ + return addr + getpagesize(); } /* Get some more pages for a device. */ @@ -346,7 +353,7 @@ static void map_at(int fd, void *addr, unsigned long offset, unsigned long len) * done to it. This allows us to share untouched memory between * Guests. */ - if (mmap(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC, + if (mmap(addr, len, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED) return; @@ -576,10 +583,10 @@ static void *_check_pointer(unsigned long addr, unsigned int size, unsigned int line) { /* - * We have to separately check addr and addr+size, because size could - * be huge and addr + size might wrap around. + * Check if the requested address and size exceeds the allocated memory, + * or addr + size wraps around. */ - if (addr >= guest_limit || addr + size >= guest_limit) + if ((addr + size) > guest_limit || (addr + size) < addr) errx(1, "%s:%i: Invalid address %#lx", __FILE__, line, addr); /* * We return a pointer for the caller's convenience, now we know it's -- cgit v1.2.3 From 85c0647275b60380e19542d43420184e86418d86 Mon Sep 17 00:00:00 2001 From: Philip Sanderson Date: Thu, 20 Jan 2011 21:37:29 -0600 Subject: lguest: document --rng in example Launcher Rusty Russell wrote: > Ah, it will appear as /dev/hwrng. It's a weirdness of Linux that our actual > hardware number generators are not wired up to /dev/random... Reflected this in the documentation, thanks :-) Signed-off-by: Rusty Russell --- Documentation/lguest/lguest.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/lguest/lguest.txt b/Documentation/lguest/lguest.txt index 6ccaf8e1a00e..dad99978a6a8 100644 --- a/Documentation/lguest/lguest.txt +++ b/Documentation/lguest/lguest.txt @@ -117,6 +117,11 @@ Running Lguest: for general information on how to get bridging to work. +- Random number generation. Using the --rng option will provide a + /dev/hwrng in the guest that will read from the host's /dev/random. + Use this option in conjunction with rng-tools (see ../hw_random.txt) + to provide entropy to the guest kernel's /dev/random. + There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest Good luck! -- cgit v1.2.3 From c9f2954964df1490373065558f3156379c7a2454 Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Tue, 30 Nov 2010 13:07:21 -0600 Subject: lguest: Use this_cpu_ops Use this_cpu_ops in a couple of places in lguest. Signed-off-by: Christoph Lameter Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 2 +- drivers/lguest/page_tables.c | 2 +- drivers/lguest/x86/core.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 4996cf5f73a0..2902ee234614 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -824,7 +824,7 @@ static void __init lguest_init_IRQ(void) for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) { /* Some systems map "vectors" to interrupts weirdly. Not us! */ - __get_cpu_var(vector_irq)[i] = i - FIRST_EXTERNAL_VECTOR; + __this_cpu_write(vector_irq[i]) = i - FIRST_EXTERNAL_VECTOR; if (i != SYSCALL_VECTOR) set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]); } diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index 04b22128a474..d21578ee95de 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -1137,7 +1137,7 @@ void free_guest_pagetable(struct lguest *lg) */ void map_switcher_in_guest(struct lg_cpu *cpu, struct lguest_pages *pages) { - pte_t *switcher_pte_page = __get_cpu_var(switcher_pte_pages); + pte_t *switcher_pte_page = __this_cpu_read(switcher_pte_pages); pte_t regs_pte; #ifdef CONFIG_X86_PAE diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index b4eb675a807e..bd4b5910473b 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -90,8 +90,8 @@ static void copy_in_guest_info(struct lg_cpu *cpu, struct lguest_pages *pages) * meanwhile). If that's not the case, we pretend everything in the * Guest has changed. */ - if (__get_cpu_var(lg_last_cpu) != cpu || cpu->last_pages != pages) { - __get_cpu_var(lg_last_cpu) = cpu; + if (__this_cpu_read(lg_last_cpu) != cpu || cpu->last_pages != pages) { + __this_cpu_read(lg_last_cpu) = cpu; cpu->last_pages = pages; cpu->changed = CHANGED_ALL; } -- cgit v1.2.3 From ced05dd741779986861fe7369fe002f542d6fa34 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2011 21:37:29 -0600 Subject: lguest: compile fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arch/x86/lguest/boot.c: In function ‘lguest_init_IRQ’: arch/x86/lguest/boot.c:824: error: macro "__this_cpu_write" requires 2 arguments, but only 1 given arch/x86/lguest/boot.c:824: error: ‘__this_cpu_write’ undeclared (first use in this function) arch/x86/lguest/boot.c:824: error: (Each undeclared identifier is reported only once arch/x86/lguest/boot.c:824: error: for each function it appears in.) drivers/lguest/x86/core.c: In function ‘copy_in_guest_info’: drivers/lguest/x86/core.c:94: error: lvalue required as left operand of assignment Signed-off-by: Rusty Russell --- arch/x86/lguest/boot.c | 2 +- drivers/lguest/x86/core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 2902ee234614..eba687f0cc0c 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c @@ -824,7 +824,7 @@ static void __init lguest_init_IRQ(void) for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) { /* Some systems map "vectors" to interrupts weirdly. Not us! */ - __this_cpu_write(vector_irq[i]) = i - FIRST_EXTERNAL_VECTOR; + __this_cpu_write(vector_irq[i], i - FIRST_EXTERNAL_VECTOR); if (i != SYSCALL_VECTOR) set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]); } diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c index bd4b5910473b..9f1659c3d1f3 100644 --- a/drivers/lguest/x86/core.c +++ b/drivers/lguest/x86/core.c @@ -91,7 +91,7 @@ static void copy_in_guest_info(struct lg_cpu *cpu, struct lguest_pages *pages) * Guest has changed. */ if (__this_cpu_read(lg_last_cpu) != cpu || cpu->last_pages != pages) { - __this_cpu_read(lg_last_cpu) = cpu; + __this_cpu_write(lg_last_cpu, cpu); cpu->last_pages = pages; cpu->changed = CHANGED_ALL; } -- cgit v1.2.3 From 2b8216e6354e7666a2718d4b891c8e8d7fcded27 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 1 Jan 2011 11:08:46 -0800 Subject: LGUEST_GUEST: fix unmet direct dependencies (VIRTUALIZATION && VIRTIO) Honor the kconfig menu hierarchy to remove kconfig dependency warnings: VIRTIO and VIRTIO_RING are subordinate to VIRTUALIZATION. warning: (LGUEST_GUEST) selects VIRTIO which has unmet direct dependencies (VIRTUALIZATION) warning: (LGUEST_GUEST && VIRTIO_PCI && VIRTIO_BALLOON) selects VIRTIO_RING which has unmet direct dependencies (VIRTUALIZATION && VIRTIO) Reported-by: Toralf F_rster Signed-off-by: Randy Dunlap Signed-off-by: Rusty Russell --- arch/x86/lguest/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/lguest/Kconfig b/arch/x86/lguest/Kconfig index 38718041efc3..6e121a2a49e1 100644 --- a/arch/x86/lguest/Kconfig +++ b/arch/x86/lguest/Kconfig @@ -2,6 +2,7 @@ config LGUEST_GUEST bool "Lguest guest support" select PARAVIRT depends on X86_32 + select VIRTUALIZATION select VIRTIO select VIRTIO_RING select VIRTIO_CONSOLE -- cgit v1.2.3 From 8b3bb3ecf1934ac4a7005ad9017de1127e2fbd2f Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 7 Jan 2011 02:55:06 -0600 Subject: virtio: remove virtio-pci root device We sometimes need to map between the virtio device and the given pci device. One such use is OS installer that gets the boot pci device from BIOS and needs to find the relevant block device. Since it can't, installation fails. Instead of creating a top-level devices/virtio-pci directory, create each device under the corresponding pci device node. Symlinks to all virtio-pci devices can be found under the pci driver link in bus/pci/drivers/virtio-pci/devices, and all virtio devices under drivers/bus/virtio/devices. Signed-off-by: Milton Miller Signed-off-by: Rusty Russell Acked-by: Michael S. Tsirkin Tested-by: Michael S. Tsirkin Acked-by: Gleb Natapov Tested-by: "Daniel P. Berrange" Cc: stable@kernel.org --- drivers/virtio/virtio_pci.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index ef8d9d558fc7..4fb5b2bf2348 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -96,11 +96,6 @@ static struct pci_device_id virtio_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, virtio_pci_id_table); -/* A PCI device has it's own struct device and so does a virtio device so - * we create a place for the virtio devices to show up in sysfs. I think it - * would make more sense for virtio to not insist on having it's own device. */ -static struct device *virtio_pci_root; - /* Convert a generic virtio device to our structure */ static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev) { @@ -629,7 +624,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev, if (vp_dev == NULL) return -ENOMEM; - vp_dev->vdev.dev.parent = virtio_pci_root; + vp_dev->vdev.dev.parent = &pci_dev->dev; vp_dev->vdev.dev.release = virtio_pci_release_dev; vp_dev->vdev.config = &virtio_pci_config_ops; vp_dev->pci_dev = pci_dev; @@ -717,17 +712,7 @@ static struct pci_driver virtio_pci_driver = { static int __init virtio_pci_init(void) { - int err; - - virtio_pci_root = root_device_register("virtio-pci"); - if (IS_ERR(virtio_pci_root)) - return PTR_ERR(virtio_pci_root); - - err = pci_register_driver(&virtio_pci_driver); - if (err) - root_device_unregister(virtio_pci_root); - - return err; + return pci_register_driver(&virtio_pci_driver); } module_init(virtio_pci_init); @@ -735,7 +720,6 @@ module_init(virtio_pci_init); static void __exit virtio_pci_exit(void) { pci_unregister_driver(&virtio_pci_driver); - root_device_unregister(virtio_pci_root); } module_exit(virtio_pci_exit); -- cgit v1.2.3 From fb87ec382f9d95469df494bdee9db922594f5cd4 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Wed, 19 Jan 2011 20:20:56 -0500 Subject: x86: Update CPU cache attributes table descriptors Update to latest definitions in: http://www.intel.com/Assets/PDF/appnote/241618.pdf [ Note, this update of the doc has removed some old values which we have listed. I think until we have clarification that they were never used in production, they should be left there. ] Signed-off-by: Dave Jones Cc: Arjan van de Ven Cc: "H. Peter Anvin" LKML-Reference: <20110120012055.GA15985@redhat.com> Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/intel_cacheinfo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index 7283e98deaae..ec2c19a7b8ef 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -45,6 +45,7 @@ static const struct _cache_table __cpuinitconst cache_table[] = { 0x0a, LVL_1_DATA, 8 }, /* 2 way set assoc, 32 byte line size */ { 0x0c, LVL_1_DATA, 16 }, /* 4-way set assoc, 32 byte line size */ { 0x0d, LVL_1_DATA, 16 }, /* 4-way set assoc, 64 byte line size */ + { 0x0e, LVL_1_DATA, 24 }, /* 6-way set assoc, 64 byte line size */ { 0x21, LVL_2, 256 }, /* 8-way set assoc, 64 byte line size */ { 0x22, LVL_3, 512 }, /* 4-way set assoc, sectored cache, 64 byte line size */ { 0x23, LVL_3, MB(1) }, /* 8-way set assoc, sectored cache, 64 byte line size */ @@ -66,6 +67,7 @@ static const struct _cache_table __cpuinitconst cache_table[] = { 0x45, LVL_2, MB(2) }, /* 4-way set assoc, 32 byte line size */ { 0x46, LVL_3, MB(4) }, /* 4-way set assoc, 64 byte line size */ { 0x47, LVL_3, MB(8) }, /* 8-way set assoc, 64 byte line size */ + { 0x48, LVL_2, MB(3) }, /* 12-way set assoc, 64 byte line size */ { 0x49, LVL_3, MB(4) }, /* 16-way set assoc, 64 byte line size */ { 0x4a, LVL_3, MB(6) }, /* 12-way set assoc, 64 byte line size */ { 0x4b, LVL_3, MB(8) }, /* 16-way set assoc, 64 byte line size */ @@ -87,6 +89,7 @@ static const struct _cache_table __cpuinitconst cache_table[] = { 0x7c, LVL_2, MB(1) }, /* 8-way set assoc, sectored cache, 64 byte line size */ { 0x7d, LVL_2, MB(2) }, /* 8-way set assoc, 64 byte line size */ { 0x7f, LVL_2, 512 }, /* 2-way set assoc, 64 byte line size */ + { 0x80, LVL_2, 512 }, /* 8-way set assoc, 64 byte line size */ { 0x82, LVL_2, 256 }, /* 8-way set assoc, 32 byte line size */ { 0x83, LVL_2, 512 }, /* 8-way set assoc, 32 byte line size */ { 0x84, LVL_2, MB(1) }, /* 8-way set assoc, 32 byte line size */ -- cgit v1.2.3 From d2ebd4798744c401faf3fdc6493383912ccd0b80 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 20 Jan 2011 12:36:21 +0100 Subject: ALSA: hda - Fix EAPD to low on CZC P10T tablet computer with ALC662 Signed-off-by: Anisse Astier Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5ea60c6d24aa..be4df4c6fd56 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -19460,6 +19460,7 @@ enum { ALC662_FIXUP_ASPIRE, ALC662_FIXUP_IDEAPAD, ALC272_FIXUP_MARIO, + ALC662_FIXUP_CZC_P10T, }; static const struct alc_fixup alc662_fixups[] = { @@ -19480,7 +19481,14 @@ static const struct alc_fixup alc662_fixups[] = { [ALC272_FIXUP_MARIO] = { .type = ALC_FIXUP_FUNC, .v.func = alc272_fixup_mario, - } + }, + [ALC662_FIXUP_CZC_P10T] = { + .type = ALC_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, + {} + } + }, }; static struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -19488,6 +19496,7 @@ static struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), + SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T), {} }; -- cgit v1.2.3 From 2ce802f62ba32a7d95748ac92bf351f76affb6ff Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 Jan 2011 12:06:35 +0100 Subject: lockdep: Move early boot local IRQ enable/disable status to init/main.c During early boot, local IRQ is disabled until IRQ subsystem is properly initialized. During this time, no one should enable local IRQ and some operations which usually are not allowed with IRQ disabled, e.g. operations which might sleep or require communications with other processors, are allowed. lockdep tracked this with early_boot_irqs_off/on() callbacks. As other subsystems need this information too, move it to init/main.c and make it generally available. While at it, toggle the boolean to early_boot_irqs_disabled instead of enabled so that it can be initialized with %false and %true indicates the exceptional condition. Signed-off-by: Tejun Heo Acked-by: Peter Zijlstra Acked-by: Pekka Enberg Cc: Linus Torvalds LKML-Reference: <20110120110635.GB6036@htj.dyndns.org> Signed-off-by: Ingo Molnar --- arch/x86/xen/enlighten.c | 2 +- include/linux/kernel.h | 2 ++ include/linux/lockdep.h | 8 -------- init/main.c | 13 +++++++++++-- kernel/lockdep.c | 18 +----------------- kernel/trace/trace_irqsoff.c | 8 -------- 6 files changed, 15 insertions(+), 36 deletions(-) diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 7e8d3bc80af6..50542efe45fb 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -1194,7 +1194,7 @@ asmlinkage void __init xen_start_kernel(void) per_cpu(xen_vcpu, 0) = &HYPERVISOR_shared_info->vcpu_info[0]; local_irq_disable(); - early_boot_irqs_off(); + early_boot_irqs_disabled = true; memblock_init(); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 5a9d9059520b..d07d8057e440 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -243,6 +243,8 @@ extern int test_taint(unsigned flag); extern unsigned long get_taint(void); extern int root_mountflags; +extern bool early_boot_irqs_disabled; + /* Values used for system_state */ extern enum system_states { SYSTEM_BOOTING, diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 71c09b26c759..f638fd78d106 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -436,16 +436,8 @@ do { \ #endif /* CONFIG_LOCKDEP */ #ifdef CONFIG_TRACE_IRQFLAGS -extern void early_boot_irqs_off(void); -extern void early_boot_irqs_on(void); extern void print_irqtrace_events(struct task_struct *curr); #else -static inline void early_boot_irqs_off(void) -{ -} -static inline void early_boot_irqs_on(void) -{ -} static inline void print_irqtrace_events(struct task_struct *curr) { } diff --git a/init/main.c b/init/main.c index 00799c1d4628..33c37c379e96 100644 --- a/init/main.c +++ b/init/main.c @@ -96,6 +96,15 @@ static inline void mark_rodata_ro(void) { } extern void tc_init(void); #endif +/* + * Debug helper: via this flag we know that we are in 'early bootup code' + * where only the boot processor is running with IRQ disabled. This means + * two things - IRQ must not be enabled before the flag is cleared and some + * operations which are not allowed with IRQ disabled are allowed while the + * flag is set. + */ +bool early_boot_irqs_disabled __read_mostly; + enum system_states system_state __read_mostly; EXPORT_SYMBOL(system_state); @@ -554,7 +563,7 @@ asmlinkage void __init start_kernel(void) cgroup_init_early(); local_irq_disable(); - early_boot_irqs_off(); + early_boot_irqs_disabled = true; /* * Interrupts are still disabled. Do necessary setups, then @@ -621,7 +630,7 @@ asmlinkage void __init start_kernel(void) if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were " "enabled early\n"); - early_boot_irqs_on(); + early_boot_irqs_disabled = false; local_irq_enable(); /* Interrupts are enabled now so all GFP allocations are safe. */ diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 42ba65dff7d9..0d2058da80f5 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -2291,22 +2291,6 @@ mark_held_locks(struct task_struct *curr, enum mark_type mark) return 1; } -/* - * Debugging helper: via this flag we know that we are in - * 'early bootup code', and will warn about any invalid irqs-on event: - */ -static int early_boot_irqs_enabled; - -void early_boot_irqs_off(void) -{ - early_boot_irqs_enabled = 0; -} - -void early_boot_irqs_on(void) -{ - early_boot_irqs_enabled = 1; -} - /* * Hardirqs will be enabled: */ @@ -2319,7 +2303,7 @@ void trace_hardirqs_on_caller(unsigned long ip) if (unlikely(!debug_locks || current->lockdep_recursion)) return; - if (DEBUG_LOCKS_WARN_ON(unlikely(!early_boot_irqs_enabled))) + if (DEBUG_LOCKS_WARN_ON(unlikely(early_boot_irqs_disabled))) return; if (unlikely(curr->hardirqs_enabled)) { diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 5cf8c602b880..92b6e1e12d98 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -453,14 +453,6 @@ void time_hardirqs_off(unsigned long a0, unsigned long a1) * Stubs: */ -void early_boot_irqs_off(void) -{ -} - -void early_boot_irqs_on(void) -{ -} - void trace_softirqs_on(unsigned long ip) { } -- cgit v1.2.3 From bd924e8cbd4b73ffb7d707a774c04f7e2cae88ed Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 Jan 2011 12:07:13 +0100 Subject: smp: Allow on_each_cpu() to be called while early_boot_irqs_disabled status to init/main.c percpu may end up calling vfree() during early boot which in turn may call on_each_cpu() for TLB flushes. The function of on_each_cpu() can be done safely while IRQ is disabled during early boot but it assumed that the function is always called with local IRQ enabled which ended up enabling local IRQ prematurely during boot and triggering a couple of warnings. This patch updates on_each_cpu() and smp_call_function_many() such on_each_cpu() can be used safely while early_boot_irqs_disabled is set. Signed-off-by: Tejun Heo Acked-by: Peter Zijlstra Acked-by: Pekka Enberg Cc: Linus Torvalds LKML-Reference: <20110120110713.GC6036@htj.dyndns.org> Signed-off-by: Ingo Molnar Reported-by: Ingo Molnar --- kernel/smp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index 4ec30e069987..4b83cd6815e2 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -430,7 +430,7 @@ void smp_call_function_many(const struct cpumask *mask, * can't happen. */ WARN_ON_ONCE(cpu_online(this_cpu) && irqs_disabled() - && !oops_in_progress); + && !oops_in_progress && !early_boot_irqs_disabled); /* So, what's a CPU they want? Ignoring this one. */ cpu = cpumask_first_and(mask, cpu_online_mask); @@ -533,17 +533,20 @@ void ipi_call_unlock_irq(void) #endif /* USE_GENERIC_SMP_HELPERS */ /* - * Call a function on all processors + * Call a function on all processors. May be used during early boot while + * early_boot_irqs_disabled is set. Use local_irq_save/restore() instead + * of local_irq_disable/enable(). */ int on_each_cpu(void (*func) (void *info), void *info, int wait) { + unsigned long flags; int ret = 0; preempt_disable(); ret = smp_call_function(func, info, wait); - local_irq_disable(); + local_irq_save(flags); func(info); - local_irq_enable(); + local_irq_restore(flags); preempt_enable(); return ret; } -- cgit v1.2.3 From 0ade640e9cda805692dbf688f4bb69e94719275a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:02 -0500 Subject: cifs: wait indefinitely for responses The client should not be timing out on individual SMB requests. Too much of the state between client and server is tied to the state of the socket. If we time out requests and issue spurious disconnects then that comprimises data integrity. Instead of doing this complicated dance where we try to decide how long to wait for a response for particular requests, have the client instead wait indefinitely for a response. Also, use a TASK_KILLABLE sleep here so that fatal signals will break out of this waiting. Later patches will add support for detecting dead peers and forcing reconnects based on that. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 110 ++++++++-------------------------------------------- 1 file changed, 17 insertions(+), 93 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 15059c7ef2ae..c41c9c4f0a79 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -318,48 +318,17 @@ static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, return 0; } -static int wait_for_response(struct cifsSesInfo *ses, - struct mid_q_entry *midQ, - unsigned long timeout, - unsigned long time_to_wait) +static int +wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) { - unsigned long curr_timeout; - - for (;;) { - curr_timeout = timeout + jiffies; - wait_event_timeout(ses->server->response_q, - midQ->midState != MID_REQUEST_SUBMITTED, timeout); - - if (time_after(jiffies, curr_timeout) && - (midQ->midState == MID_REQUEST_SUBMITTED) && - ((ses->server->tcpStatus == CifsGood) || - (ses->server->tcpStatus == CifsNew))) { - - unsigned long lrt; + int error; - /* We timed out. Is the server still - sending replies ? */ - spin_lock(&GlobalMid_Lock); - lrt = ses->server->lstrp; - spin_unlock(&GlobalMid_Lock); + error = wait_event_killable(server->response_q, + midQ->midState != MID_REQUEST_SUBMITTED); + if (error < 0) + return -ERESTARTSYS; - /* Calculate time_to_wait past last receive time. - Although we prefer not to time out if the - server is still responding - we will time - out if the server takes more than 15 (or 45 - or 180) seconds to respond to this request - and has not responded to any request from - other threads on the client within 10 seconds */ - lrt += time_to_wait; - if (time_after(jiffies, lrt)) { - /* No replies for time_to_wait. */ - cERROR(1, "server not responding"); - return -1; - } - } else { - return 0; - } - } + return 0; } @@ -433,7 +402,6 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, int rc = 0; int long_op; unsigned int receive_len; - unsigned long timeout; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; @@ -500,33 +468,12 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if (rc < 0) goto out; - if (long_op == CIFS_STD_OP) - timeout = 15 * HZ; - else if (long_op == CIFS_VLONG_OP) /* e.g. slow writes past EOF */ - timeout = 180 * HZ; - else if (long_op == CIFS_LONG_OP) - timeout = 45 * HZ; /* should be greater than - servers oplock break timeout (about 43 seconds) */ - else if (long_op == CIFS_ASYNC_OP) - goto out; - else if (long_op == CIFS_BLOCKING_OP) - timeout = 0x7FFFFFFF; /* large, but not so large as to wrap */ - else { - cERROR(1, "unknown timeout flag %d", long_op); - rc = -EIO; + if (long_op == CIFS_ASYNC_OP) goto out; - } - - /* wait for 15 seconds or until woken up due to response arriving or - due to last connection to this server being unmounted */ - if (signal_pending(current)) { - /* if signal pending do not hold up user for full smb timeout - but we still give response a chance to complete */ - timeout = 2 * HZ; - } - /* No user interrupts in wait - wreaks havoc with performance */ - wait_for_response(ses, midQ, timeout, 10 * HZ); + rc = wait_for_response(ses->server, midQ); + if (rc != 0) + goto out; rc = sync_mid_result(midQ, ses->server); if (rc != 0) { @@ -604,7 +551,6 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, { int rc = 0; unsigned int receive_len; - unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { @@ -668,33 +614,12 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if (rc < 0) goto out; - if (long_op == CIFS_STD_OP) - timeout = 15 * HZ; - /* wait for 15 seconds or until woken up due to response arriving or - due to last connection to this server being unmounted */ - else if (long_op == CIFS_ASYNC_OP) - goto out; - else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */ - timeout = 180 * HZ; - else if (long_op == CIFS_LONG_OP) - timeout = 45 * HZ; /* should be greater than - servers oplock break timeout (about 43 seconds) */ - else if (long_op == CIFS_BLOCKING_OP) - timeout = 0x7FFFFFFF; /* large but no so large as to wrap */ - else { - cERROR(1, "unknown timeout flag %d", long_op); - rc = -EIO; + if (long_op == CIFS_ASYNC_OP) goto out; - } - if (signal_pending(current)) { - /* if signal pending do not hold up user for full smb timeout - but we still give response a chance to complete */ - timeout = 2 * HZ; - } - - /* No user interrupts in wait - wreaks havoc with performance */ - wait_for_response(ses, midQ, timeout, 10 * HZ); + rc = wait_for_response(ses->server, midQ); + if (rc != 0) + goto out; rc = sync_mid_result(midQ, ses->server); if (rc != 0) { @@ -915,8 +840,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, } } - /* Wait 5 seconds for the response. */ - if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) { + if (wait_for_response(ses->server, midQ) == 0) { /* We got the response - restart system call. */ rstart = 1; } -- cgit v1.2.3 From dad255b182363db1d1124458cd3fb0a4deac0d0f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:02 -0500 Subject: cifs: don't reconnect server when we don't get a response We only want to force a reconnect to the server under very limited and specific circumstances. Now that we have processes waiting indefinitely for responses, we shouldn't reach this point unless a reconnect is already in process. Thus, there's no reason to re-mark the server for reconnect here. Reviewed-by: Suresh Jayaraman Reviewed-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index c41c9c4f0a79..f65cdec042e4 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -374,10 +374,8 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) if (mid->midState == MID_REQUEST_SUBMITTED) { if (server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; - else { - server->tcpStatus = CifsNeedReconnect; + else mid->midState = MID_RETRY_NEEDED; - } } if (rc != -EHOSTDOWN) { -- cgit v1.2.3 From 74dd92a881b62014ca3c754db6868e1f142f2fb9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:02 -0500 Subject: cifs: clean up sync_mid_result Make it use a switch statement based on the value of the midStatus. If the resp_buf is set, then MID_RESPONSE_RECEIVED is too. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index f65cdec042e4..6abd1445c983 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -363,28 +363,29 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) { int rc = 0; - spin_lock(&GlobalMid_Lock); + cFYI(1, "%s: cmd=%d mid=%d state=%d", __func__, mid->command, + mid->mid, mid->midState); - if (mid->resp_buf) { + spin_lock(&GlobalMid_Lock); + switch (mid->midState) { + case MID_RESPONSE_RECEIVED: spin_unlock(&GlobalMid_Lock); return rc; - } - - cERROR(1, "No response to cmd %d mid %d", mid->command, mid->mid); - if (mid->midState == MID_REQUEST_SUBMITTED) { - if (server->tcpStatus == CifsExiting) + case MID_REQUEST_SUBMITTED: + /* socket is going down, reject all calls */ + if (server->tcpStatus == CifsExiting) { + cERROR(1, "%s: canceling mid=%d cmd=0x%x state=%d", + __func__, mid->mid, mid->command, mid->midState); rc = -EHOSTDOWN; - else - mid->midState = MID_RETRY_NEEDED; - } - - if (rc != -EHOSTDOWN) { - if (mid->midState == MID_RETRY_NEEDED) { - rc = -EAGAIN; - cFYI(1, "marking request for retry"); - } else { - rc = -EIO; + break; } + case MID_RETRY_NEEDED: + rc = -EAGAIN; + break; + default: + cERROR(1, "%s: invalid mid state mid=%d state=%d", __func__, + mid->mid, mid->midState); + rc = -EIO; } spin_unlock(&GlobalMid_Lock); -- cgit v1.2.3 From 2b84a36c5529da136d28b268e75268892d09869c Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:21 -0500 Subject: cifs: allow for different handling of received response In order to incorporate async requests, we need to allow for a more general way to do things on receive, rather than just waking up a process. Turn the task pointer in the mid_q_entry into a callback function and a generic data pointer. When a response comes in, or the socket is reconnected, cifsd can call the callback function in order to wake up the process. The default is to just wake up the current process which should mean no change in behavior for existing code. Also, clean up the locking in cifs_reconnect. There doesn't seem to be any need to hold both the srv_mutex and GlobalMid_Lock when walking the list of mids. Reviewed-by: Suresh Jayaraman Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 8 ++++---- fs/cifs/cifsglob.h | 15 ++++++++++++++- fs/cifs/connect.c | 53 +++++++++++++++++++++++++--------------------------- fs/cifs/transport.c | 19 +++++++++++++++++-- 4 files changed, 60 insertions(+), 35 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index e2d0d5d455fa..65829d32128c 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -79,11 +79,11 @@ void cifs_dump_mids(struct TCP_Server_Info *server) spin_lock(&GlobalMid_Lock); list_for_each(tmp, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - cERROR(1, "State: %d Cmd: %d Pid: %d Tsk: %p Mid %d", + cERROR(1, "State: %d Cmd: %d Pid: %d Cbdata: %p Mid %d", mid_entry->midState, (int)mid_entry->command, mid_entry->pid, - mid_entry->tsk, + mid_entry->callback_data, mid_entry->mid); #ifdef CONFIG_CIFS_STATS2 cERROR(1, "IsLarge: %d buf: %p time rcv: %ld now: %ld", @@ -218,11 +218,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) mid_entry = list_entry(tmp3, struct mid_q_entry, qhead); seq_printf(m, "\tState: %d com: %d pid:" - " %d tsk: %p mid %d\n", + " %d cbdata: %p mid %d\n", mid_entry->midState, (int)mid_entry->command, mid_entry->pid, - mid_entry->tsk, + mid_entry->callback_data, mid_entry->mid); } spin_unlock(&GlobalMid_Lock); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 606ca8bb7102..4de737575959 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -508,6 +508,18 @@ static inline void cifs_stats_bytes_read(struct cifsTconInfo *tcon, #endif +struct mid_q_entry; + +/* + * This is the prototype for the mid callback function. When creating one, + * take special care to avoid deadlocks. Things to bear in mind: + * + * - it will be called by cifsd + * - the GlobalMid_Lock will be held + * - the mid will be removed from the pending_mid_q list + */ +typedef void (mid_callback_t)(struct mid_q_entry *mid); + /* one of these for every pending CIFS request to the server */ struct mid_q_entry { struct list_head qhead; /* mids waiting on reply from this server */ @@ -519,7 +531,8 @@ struct mid_q_entry { unsigned long when_sent; /* time when smb send finished */ unsigned long when_received; /* when demux complete (taken off wire) */ #endif - struct task_struct *tsk; /* task waiting for response */ + mid_callback_t *callback; /* call completion callback */ + void *callback_data; /* general purpose pointer for callback */ struct smb_hdr *resp_buf; /* response buffer */ int midState; /* wish this were enum but can not pass to wait_event */ __u8 command; /* smb command code */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 5c7f8450dbe0..aa66de1db5f5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -152,6 +152,7 @@ cifs_reconnect(struct TCP_Server_Info *server) /* before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they are not used until reconnected */ + cFYI(1, "%s: marking sessions and tcons for reconnect", __func__); spin_lock(&cifs_tcp_ses_lock); list_for_each(tmp, &server->smb_ses_list) { ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); @@ -163,7 +164,9 @@ cifs_reconnect(struct TCP_Server_Info *server) } } spin_unlock(&cifs_tcp_ses_lock); + /* do not want to be sending data on a socket we are freeing */ + cFYI(1, "%s: tearing down socket", __func__); mutex_lock(&server->srv_mutex); if (server->ssocket) { cFYI(1, "State: 0x%x Flags: 0x%lx", server->ssocket->state, @@ -180,22 +183,19 @@ cifs_reconnect(struct TCP_Server_Info *server) kfree(server->session_key.response); server->session_key.response = NULL; server->session_key.len = 0; + mutex_unlock(&server->srv_mutex); + /* mark submitted MIDs for retry and issue callback */ + cFYI(1, "%s: issuing mid callbacks", __func__); spin_lock(&GlobalMid_Lock); - list_for_each(tmp, &server->pending_mid_q) { - mid_entry = list_entry(tmp, struct - mid_q_entry, - qhead); - if (mid_entry->midState == MID_REQUEST_SUBMITTED) { - /* Mark other intransit requests as needing - retry so we do not immediately mark the - session bad again (ie after we reconnect - below) as they timeout too */ + list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { + mid_entry = list_entry(tmp, struct mid_q_entry, qhead); + if (mid_entry->midState == MID_REQUEST_SUBMITTED) mid_entry->midState = MID_RETRY_NEEDED; - } + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); } spin_unlock(&GlobalMid_Lock); - mutex_unlock(&server->srv_mutex); while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood)) { @@ -212,10 +212,9 @@ cifs_reconnect(struct TCP_Server_Info *server) if (server->tcpStatus != CifsExiting) server->tcpStatus = CifsGood; spin_unlock(&GlobalMid_Lock); - /* atomic_set(&server->inFlight,0);*/ - wake_up(&server->response_q); } } + return rc; } @@ -345,7 +344,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) struct msghdr smb_msg; struct kvec iov; struct socket *csocket = server->ssocket; - struct list_head *tmp; + struct list_head *tmp, *tmp2; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; char temp; @@ -558,10 +557,9 @@ incomplete_rcv: continue; } - - task_to_wake = NULL; + mid_entry = NULL; spin_lock(&GlobalMid_Lock); - list_for_each(tmp, &server->pending_mid_q) { + list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); if ((mid_entry->mid == smb_buffer->Mid) && @@ -602,8 +600,9 @@ incomplete_rcv: mid_entry->resp_buf = smb_buffer; mid_entry->largeBuf = isLargeBuf; multi_t2_fnd: - task_to_wake = mid_entry->tsk; mid_entry->midState = MID_RESPONSE_RECEIVED; + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); #ifdef CONFIG_CIFS_STATS2 mid_entry->when_received = jiffies; #endif @@ -613,9 +612,11 @@ multi_t2_fnd: server->lstrp = jiffies; break; } + mid_entry = NULL; } spin_unlock(&GlobalMid_Lock); - if (task_to_wake) { + + if (mid_entry != NULL) { /* Was previous buf put in mpx struct for multi-rsp? */ if (!isMultiRsp) { /* smb buffer will be freed by user thread */ @@ -624,7 +625,6 @@ multi_t2_fnd: else smallbuf = NULL; } - wake_up_process(task_to_wake); } else if (!is_valid_oplock_break(smb_buffer, server) && !isMultiRsp) { cERROR(1, "No task to wake, unknown frame received! " @@ -678,15 +678,12 @@ multi_t2_fnd: if (!list_empty(&server->pending_mid_q)) { spin_lock(&GlobalMid_Lock); - list_for_each(tmp, &server->pending_mid_q) { + list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); - if (mid_entry->midState == MID_REQUEST_SUBMITTED) { - cFYI(1, "Clearing Mid 0x%x - waking up ", - mid_entry->mid); - task_to_wake = mid_entry->tsk; - if (task_to_wake) - wake_up_process(task_to_wake); - } + cFYI(1, "Clearing Mid 0x%x - issuing callback", + mid_entry->mid); + list_del_init(&mid_entry->qhead); + mid_entry->callback(mid_entry); } spin_unlock(&GlobalMid_Lock); /* 1/8th of sec is more than enough time for them to exit */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 6abd1445c983..d77b6154cf22 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -36,6 +36,12 @@ extern mempool_t *cifs_mid_poolp; +static void +wake_up_task(struct mid_q_entry *mid) +{ + wake_up_process(mid->callback_data); +} + static struct mid_q_entry * AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) { @@ -58,7 +64,13 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* when mid allocated can be before when sent */ temp->when_alloc = jiffies; - temp->tsk = current; + + /* + * The default is for the mid to be synchronous, so the + * default callback just wakes up the current task. + */ + temp->callback = wake_up_task; + temp->callback_data = current; } atomic_inc(&midCount); @@ -367,6 +379,9 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) mid->mid, mid->midState); spin_lock(&GlobalMid_Lock); + /* ensure that it's no longer on the pending_mid_q */ + list_del_init(&mid->qhead); + switch (mid->midState) { case MID_RESPONSE_RECEIVED: spin_unlock(&GlobalMid_Lock); @@ -389,7 +404,7 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) } spin_unlock(&GlobalMid_Lock); - delete_mid(mid); + DeleteMidQEntry(mid); return rc; } -- cgit v1.2.3 From a6827c184ea9f5452e4aaa7c799dd3c7cc9ba05e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:21 -0500 Subject: cifs: add cifs_call_async Add a function that will send a request, and set up the mid for an async reply. Reviewed-by: Suresh Jayaraman Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 5 +++++ fs/cifs/transport.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 95d5dbbb4c7a..7f2988bb8929 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -61,6 +61,11 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata, const char *fullpath, const struct dfs_info3_param *ref, char **devname); /* extern void renew_parental_timestamps(struct dentry *direntry);*/ +extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer, + struct TCP_Server_Info *server); +extern int cifs_call_async(struct TCP_Server_Info *server, + struct smb_hdr *in_buf, mid_callback_t *callback, + void *cbdata); extern int SendReceive(const unsigned int /* xid */ , struct cifsSesInfo *, struct smb_hdr * /* input */ , struct smb_hdr * /* out */ , diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index d77b6154cf22..166b65a3763e 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -42,7 +42,7 @@ wake_up_task(struct mid_q_entry *mid) wake_up_process(mid->callback_data); } -static struct mid_q_entry * +struct mid_q_entry * AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) { struct mid_q_entry *temp; @@ -344,6 +344,62 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) } +/* + * Send a SMB request and set the callback function in the mid to handle + * the result. Caller is responsible for dealing with timeouts. + */ +int +cifs_call_async(struct TCP_Server_Info *server, struct smb_hdr *in_buf, + mid_callback_t *callback, void *cbdata) +{ + int rc; + struct mid_q_entry *mid; + + rc = wait_for_free_request(server, CIFS_ASYNC_OP); + if (rc) + return rc; + + mutex_lock(&server->srv_mutex); + mid = AllocMidQEntry(in_buf, server); + if (mid == NULL) { + mutex_unlock(&server->srv_mutex); + return -ENOMEM; + } + + /* put it on the pending_mid_q */ + spin_lock(&GlobalMid_Lock); + list_add_tail(&mid->qhead, &server->pending_mid_q); + spin_unlock(&GlobalMid_Lock); + + rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); + if (rc) { + mutex_unlock(&server->srv_mutex); + goto out_err; + } + + mid->callback = callback; + mid->callback_data = cbdata; + mid->midState = MID_REQUEST_SUBMITTED; +#ifdef CONFIG_CIFS_STATS2 + atomic_inc(&server->inSend); +#endif + rc = smb_send(server, in_buf, in_buf->smb_buf_length); +#ifdef CONFIG_CIFS_STATS2 + atomic_dec(&server->inSend); + mid->when_sent = jiffies; +#endif + mutex_unlock(&server->srv_mutex); + if (rc) + goto out_err; + + return rc; +out_err: + delete_mid(mid); + atomic_dec(&server->inFlight); + wake_up(&server->request_q); + return rc; +} + /* * * Send an SMB Request. No response info (other than return code) -- cgit v1.2.3 From 766fdbb57fdb1e53bc34c431103e95383d7f13ba Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:21 -0500 Subject: cifs: add ability to send an echo request Reviewed-by: Suresh Jayaraman Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 15 +++++++++++++++ fs/cifs/cifsproto.h | 2 ++ fs/cifs/cifssmb.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/transport.c | 2 +- 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index de36b09763a8..ea205b4fcad2 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -50,6 +50,7 @@ #define SMB_COM_SETATTR 0x09 /* trivial response */ #define SMB_COM_LOCKING_ANDX 0x24 /* trivial response */ #define SMB_COM_COPY 0x29 /* trivial rsp, fail filename ignrd*/ +#define SMB_COM_ECHO 0x2B /* echo request */ #define SMB_COM_OPEN_ANDX 0x2D /* Legacy open for old servers */ #define SMB_COM_READ_ANDX 0x2E #define SMB_COM_WRITE_ANDX 0x2F @@ -760,6 +761,20 @@ typedef struct smb_com_tconx_rsp_ext { * */ +typedef struct smb_com_echo_req { + struct smb_hdr hdr; + __le16 EchoCount; + __le16 ByteCount; + char Data[1]; +} __attribute__((packed)) ECHO_REQ; + +typedef struct smb_com_echo_rsp { + struct smb_hdr hdr; + __le16 SequenceNumber; + __le16 ByteCount; + char Data[1]; +} __attribute__((packed)) ECHO_RSP; + typedef struct smb_com_logoff_andx_req { struct smb_hdr hdr; /* wct = 2 */ __u8 AndXCommand; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7f2988bb8929..982895fa7615 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -63,6 +63,7 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata, /* extern void renew_parental_timestamps(struct dentry *direntry);*/ extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server); +extern void DeleteMidQEntry(struct mid_q_entry *midEntry); extern int cifs_call_async(struct TCP_Server_Info *server, struct smb_hdr *in_buf, mid_callback_t *callback, void *cbdata); @@ -358,6 +359,7 @@ extern int CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, const __u64 len, struct file_lock *, const __u16 lock_type, const bool waitFlag); extern int CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon); +extern int CIFSSMBEcho(struct TCP_Server_Info *server); extern int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses); extern struct cifsSesInfo *sesInfoAlloc(void); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3652cc60314c..54b9f5d8d1db 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -706,6 +706,53 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon) return rc; } +/* + * This is a no-op for now. We're not really interested in the reply, but + * rather in the fact that the server sent one and that server->lstrp + * gets updated. + * + * FIXME: maybe we should consider checking that the reply matches request? + */ +static void +cifs_echo_callback(struct mid_q_entry *mid) +{ + struct TCP_Server_Info *server = mid->callback_data; + + DeleteMidQEntry(mid); + atomic_dec(&server->inFlight); + wake_up(&server->request_q); +} + +int +CIFSSMBEcho(struct TCP_Server_Info *server) +{ + ECHO_REQ *smb; + int rc = 0; + + cFYI(1, "In echo request"); + + rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb); + if (rc) + return rc; + + /* set up echo request */ + smb->hdr.Tid = cpu_to_le16(0xffff); + smb->hdr.WordCount = cpu_to_le16(1); + smb->EchoCount = cpu_to_le16(1); + smb->ByteCount = cpu_to_le16(1); + smb->Data[0] = 'a'; + smb->hdr.smb_buf_length += 3; + + rc = cifs_call_async(server, (struct smb_hdr *)smb, + cifs_echo_callback, server); + if (rc) + cFYI(1, "Echo request failed: %d", rc); + + cifs_small_buf_release(smb); + + return rc; +} + int CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 166b65a3763e..a0cef4960516 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -78,7 +78,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server) return temp; } -static void +void DeleteMidQEntry(struct mid_q_entry *midEntry) { #ifdef CONFIG_CIFS_STATS2 -- cgit v1.2.3 From c74093b694998d30105d9904686da5e3576497c4 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:23 -0500 Subject: cifs: set up recurring workqueue job to do SMB echo requests Reviewed-by: Suresh Jayaraman Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 + fs/cifs/connect.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 4de737575959..9c728dd5b146 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -218,6 +218,7 @@ struct TCP_Server_Info { bool sec_kerberosu2u; /* supports U2U Kerberos */ bool sec_ntlmssp; /* supports NTLMSSP */ bool session_estab; /* mark when very first sess is established */ + struct delayed_work echo; /* echo ping workqueue job */ #ifdef CONFIG_CIFS_FSCACHE struct fscache_cookie *fscache; /* client index cache cookie */ #endif diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index aa66de1db5f5..f38ca084c9d2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -52,6 +52,9 @@ #define CIFS_PORT 445 #define RFC1001_PORT 139 +/* SMB echo "timeout" -- FIXME: tunable? */ +#define SMB_ECHO_INTERVAL (60 * HZ) + extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); @@ -333,6 +336,26 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) } +static void +cifs_echo_request(struct work_struct *work) +{ + int rc; + struct TCP_Server_Info *server = container_of(work, + struct TCP_Server_Info, echo.work); + + /* no need to ping if we got a response recently */ + if (time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ)) + goto requeue_echo; + + rc = CIFSSMBEcho(server); + if (rc) + cFYI(1, "Unable to send echo request to server: %s", + server->hostname); + +requeue_echo: + queue_delayed_work(system_nrt_wq, &server->echo, SMB_ECHO_INTERVAL); +} + static int cifs_demultiplex_thread(struct TCP_Server_Info *server) { @@ -1571,6 +1594,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) list_del_init(&server->tcp_ses_list); spin_unlock(&cifs_tcp_ses_lock); + cancel_delayed_work_sync(&server->echo); + spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); @@ -1662,6 +1687,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) tcp_ses->sequence_number = 0; INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); + INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); /* * at this point we are the only ones with the pointer @@ -1710,6 +1736,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) cifs_fscache_get_client_cookie(tcp_ses); + /* queue echo request delayed work */ + queue_delayed_work(system_nrt_wq, &tcp_ses->echo, SMB_ECHO_INTERVAL); + return tcp_ses; out_err_crypto_release: -- cgit v1.2.3 From fda3594362184383e73f0a2a5fa5b38ac0e04fd8 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 20 Jan 2011 18:06:34 +0000 Subject: [CIFS] cifs: reconnect unresponsive servers If the server isn't responding to echoes, we don't want to leave tasks hung waiting for it to reply. At that point, we'll want to reconnect so that soft mounts can return an error to userspace quickly. If the client hasn't received a reply after a specified number of echo intervals, assume that the transport is down and attempt to reconnect the socket. The number of echo_intervals to wait before attempting to reconnect is tunable via a module parameter. Setting it to 0, means that the client will never attempt to reconnect. The default is 5. Signed-off-by: Jeff Layton --- fs/cifs/cifsfs.c | 6 +++++- fs/cifs/cifsglob.h | 3 +++ fs/cifs/connect.c | 21 +++++++++++++++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d9f652a522a6..99d777a03dd0 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -77,7 +77,11 @@ unsigned int cifs_max_pending = CIFS_MAX_REQ; module_param(cifs_max_pending, int, 0); MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server. " "Default: 50 Range: 2 to 256"); - +unsigned short echo_retries = 5; +module_param(echo_retries, ushort, 0644); +MODULE_PARM_DESC(echo_retries, "Number of echo attempts before giving up and " + "reconnecting server. Default: 5. 0 means " + "never reconnect."); extern mempool_t *cifs_sm_req_poolp; extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9c728dd5b146..7040abc638fa 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -804,6 +804,9 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ +/* reconnect after this many failed echo attempts */ +GLOBAL_EXTERN unsigned short echo_retries; + void cifs_oplock_break(struct work_struct *work); void cifs_oplock_break_get(struct cifsFileInfo *cfile); void cifs_oplock_break_put(struct cifsFileInfo *cfile); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f38ca084c9d2..f5d7b59a3553 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -186,6 +186,7 @@ cifs_reconnect(struct TCP_Server_Info *server) kfree(server->session_key.response); server->session_key.response = NULL; server->session_key.len = 0; + server->lstrp = jiffies; mutex_unlock(&server->srv_mutex); /* mark submitted MIDs for retry and issue callback */ @@ -420,7 +421,20 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; pdu_length = 4; /* enough to get RFC1001 header */ + incomplete_rcv: + if (echo_retries > 0 && + time_after(jiffies, server->lstrp + + (echo_retries * SMB_ECHO_INTERVAL))) { + cERROR(1, "Server %s has not responded in %d seconds. " + "Reconnecting...", server->hostname, + (echo_retries * SMB_ECHO_INTERVAL / HZ)); + cifs_reconnect(server); + csocket = server->ssocket; + wake_up(&server->response_q); + continue; + } + length = kernel_recvmsg(csocket, &smb_msg, &iov, 1, pdu_length, 0 /* BB other flags? */); @@ -581,6 +595,8 @@ incomplete_rcv: } mid_entry = NULL; + server->lstrp = jiffies; + spin_lock(&GlobalMid_Lock); list_for_each_safe(tmp, tmp2, &server->pending_mid_q) { mid_entry = list_entry(tmp, struct mid_q_entry, qhead); @@ -629,10 +645,6 @@ multi_t2_fnd: #ifdef CONFIG_CIFS_STATS2 mid_entry->when_received = jiffies; #endif - /* so we do not time out requests to server - which is still responding (since server could - be busy but not dead) */ - server->lstrp = jiffies; break; } mid_entry = NULL; @@ -1685,6 +1697,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); tcp_ses->session_estab = false; tcp_ses->sequence_number = 0; + tcp_ses->lstrp = jiffies; INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list); INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request); -- cgit v1.2.3 From 7749981ec31aa40e28a1ef5687e46bc1aa278fae Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:23 -0500 Subject: cifs: remove code for setting timeouts on requests Since we don't time out individual requests anymore, remove the code that we used to use for setting timeouts on different requests. Reviewed-by: Pavel Shilovsky Reviewed-by: Suresh Jayaraman Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 9 +++------ fs/cifs/cifssmb.c | 8 ++++---- fs/cifs/connect.c | 2 +- fs/cifs/file.c | 44 +++++++------------------------------------- fs/cifs/sess.c | 2 +- fs/cifs/transport.c | 2 +- 6 files changed, 17 insertions(+), 50 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7040abc638fa..571132c95231 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -636,12 +636,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param, #define CIFS_IOVEC 4 /* array of response buffers */ /* Type of Request to SendReceive2 */ -#define CIFS_STD_OP 0 /* normal request timeout */ -#define CIFS_LONG_OP 1 /* long op (up to 45 sec, oplock time) */ -#define CIFS_VLONG_OP 2 /* sloow op - can take up to 180 seconds */ -#define CIFS_BLOCKING_OP 4 /* operation can block */ -#define CIFS_ASYNC_OP 8 /* do not wait for response */ -#define CIFS_TIMEOUT_MASK 0x00F /* only one of 5 above set in req */ +#define CIFS_BLOCKING_OP 1 /* operation can block */ +#define CIFS_ASYNC_OP 2 /* do not wait for response */ +#define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */ #define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ #define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ #define CIFS_NO_RESP 0x040 /* no response buffer required */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 54b9f5d8d1db..37113450757b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1240,7 +1240,7 @@ OldOpenRetry: pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP); + (struct smb_hdr *)pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, "Error in Open = %d", rc); @@ -1353,7 +1353,7 @@ openRetry: pSMB->ByteCount = cpu_to_le16(count); /* long_op set to 1 to allow for oplock break timeouts */ rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP); + (struct smb_hdr *)pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_opens); if (rc) { cFYI(1, "Error in Open = %d", rc); @@ -1435,7 +1435,7 @@ CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid, iov[0].iov_base = (char *)pSMB; iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */, - &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR); + &resp_buf_type, CIFS_LOG_ERROR); cifs_stats_inc(&tcon->num_reads); pSMBr = (READ_RSP *)iov[0].iov_base; if (rc) { @@ -3136,7 +3136,7 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, iov[0].iov_len = pSMB->hdr.smb_buf_length + 4; rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type, - CIFS_STD_OP); + 0); cifs_stats_inc(&tcon->num_acl_get); if (rc) { cFYI(1, "Send error in QuerySecDesc = %d", rc); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index f5d7b59a3553..8d4657596301 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -3022,7 +3022,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, pSMB->ByteCount = cpu_to_le16(count); rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, - CIFS_STD_OP); + 0); /* above now done in SendReceive */ if ((rc == 0) && (tcon != NULL)) { diff --git a/fs/cifs/file.c b/fs/cifs/file.c index cfa2e5ebcafe..bd2a028af833 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -839,29 +839,6 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) return rc; } -/* - * Set the timeout on write requests past EOF. For some servers (Windows) - * these calls can be very long. - * - * If we're writing >10M past the EOF we give a 180s timeout. Anything less - * than that gets a 45s timeout. Writes not past EOF get 15s timeouts. - * The 10M cutoff is totally arbitrary. A better scheme for this would be - * welcome if someone wants to suggest one. - * - * We may be able to do a better job with this if there were some way to - * declare that a file should be sparse. - */ -static int -cifs_write_timeout(struct cifsInodeInfo *cifsi, loff_t offset) -{ - if (offset <= cifsi->server_eof) - return CIFS_STD_OP; - else if (offset > (cifsi->server_eof + (10 * 1024 * 1024))) - return CIFS_VLONG_OP; - else - return CIFS_LONG_OP; -} - /* update the file size (if needed) after a write */ static void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, @@ -882,7 +859,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - int xid, long_op; + int xid; struct cifsFileInfo *open_file; struct cifsInodeInfo *cifsi = CIFS_I(inode); @@ -903,7 +880,6 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, xid = GetXid(); - long_op = cifs_write_timeout(cifsi, *poffset); for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; @@ -931,7 +907,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, min_t(const int, cifs_sb->wsize, write_size - total_written), *poffset, &bytes_written, - NULL, write_data + total_written, long_op); + NULL, write_data + total_written, 0); } if (rc || (bytes_written == 0)) { if (total_written) @@ -944,8 +920,6 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, cifs_update_eof(cifsi, *poffset, bytes_written); *poffset += bytes_written; } - long_op = CIFS_STD_OP; /* subsequent writes fast - - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); @@ -974,7 +948,7 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, unsigned int total_written; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - int xid, long_op; + int xid; struct dentry *dentry = open_file->dentry; struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode); @@ -987,7 +961,6 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, xid = GetXid(); - long_op = cifs_write_timeout(cifsi, *poffset); for (total_written = 0; write_size > total_written; total_written += bytes_written) { rc = -EAGAIN; @@ -1017,7 +990,7 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, rc = CIFSSMBWrite2(xid, pTcon, open_file->netfid, len, *poffset, &bytes_written, - iov, 1, long_op); + iov, 1, 0); } else rc = CIFSSMBWrite(xid, pTcon, open_file->netfid, @@ -1025,7 +998,7 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, write_size - total_written), *poffset, &bytes_written, write_data + total_written, - NULL, long_op); + NULL, 0); } if (rc || (bytes_written == 0)) { if (total_written) @@ -1038,8 +1011,6 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, cifs_update_eof(cifsi, *poffset, bytes_written); *poffset += bytes_written; } - long_op = CIFS_STD_OP; /* subsequent writes fast - - 15 seconds is plenty */ } cifs_stats_bytes_written(pTcon, total_written); @@ -1239,7 +1210,7 @@ static int cifs_writepages(struct address_space *mapping, struct pagevec pvec; int rc = 0; int scanned = 0; - int xid, long_op; + int xid; cifs_sb = CIFS_SB(mapping->host->i_sb); @@ -1384,11 +1355,10 @@ retry_write: cERROR(1, "No writable handles for inode"); rc = -EBADF; } else { - long_op = cifs_write_timeout(cifsi, offset); rc = CIFSSMBWrite2(xid, tcon, open_file->netfid, bytes_to_write, offset, &bytes_written, iov, n_iov, - long_op); + 0); cifsFileInfo_put(open_file); } diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index eb746486e49e..1cffd82c4f13 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -879,7 +879,7 @@ ssetup_ntlmssp_authenticate: BCC_LE(smb_buf) = cpu_to_le16(count); rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, - CIFS_STD_OP /* not long */ | CIFS_LOG_ERROR); + CIFS_LOG_ERROR); /* SMB request buf freed in SendReceive2 */ pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base; diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index a0cef4960516..fe92c4cb75f5 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -798,7 +798,7 @@ send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon, pSMB->hdr.Mid = GetNextMid(ses->server); return SendReceive(xid, ses, in_buf, out_buf, - &bytes_returned, CIFS_STD_OP); + &bytes_returned, 0); } int -- cgit v1.2.3 From 76dcc26f1d7f1c98c3f595379dcd9562f01bf38d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 11 Jan 2011 07:24:24 -0500 Subject: cifs: mangle existing header for SMB_COM_NT_CANCEL The NT_CANCEL command looks just like the original command, except for a few small differences. The send_nt_cancel function however currently takes a tcon, which we don't have in SendReceive and SendReceive2. Instead of "respinning" the entire header for an NT_CANCEL, just mangle the existing header by replacing just the fields we need. This means we don't need a tcon and allows us to call it from other places. Reviewed-by: Pavel Shilovsky Reviewed-by: Suresh Jayaraman Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/transport.c | 63 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index fe92c4cb75f5..c8e2808cd5e6 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -464,6 +464,43 @@ sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) return rc; } +/* + * An NT cancel request header looks just like the original request except: + * + * The Command is SMB_COM_NT_CANCEL + * The WordCount is zeroed out + * The ByteCount is zeroed out + * + * This function mangles an existing request buffer into a + * SMB_COM_NT_CANCEL request and then sends it. + */ +static int +send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf, + struct mid_q_entry *mid) +{ + int rc = 0; + + /* -4 for RFC1001 length and +2 for BCC field */ + in_buf->smb_buf_length = sizeof(struct smb_hdr) - 4 + 2; + in_buf->Command = SMB_COM_NT_CANCEL; + in_buf->WordCount = 0; + BCC_LE(in_buf) = 0; + + mutex_lock(&server->srv_mutex); + rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); + if (rc) { + mutex_unlock(&server->srv_mutex); + return rc; + } + rc = smb_send(server, in_buf, in_buf->smb_buf_length); + mutex_unlock(&server->srv_mutex); + + cFYI(1, "issued NT_CANCEL for mid %u, rc = %d", + in_buf->Mid, rc); + + return rc; +} + int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, @@ -753,29 +790,6 @@ out: return rc; } -/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */ - -static int -send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, - struct mid_q_entry *midQ) -{ - int rc = 0; - struct cifsSesInfo *ses = tcon->ses; - __u16 mid = in_buf->Mid; - - header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); - in_buf->Mid = mid; - mutex_lock(&ses->server->srv_mutex); - rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); - if (rc) { - mutex_unlock(&ses->server->srv_mutex); - return rc; - } - rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); - mutex_unlock(&ses->server->srv_mutex); - return rc; -} - /* We send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to return. */ @@ -890,8 +904,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ - - rc = send_nt_cancel(tcon, in_buf, midQ); + rc = send_nt_cancel(ses->server, in_buf, midQ); if (rc) { delete_mid(midQ); return rc; -- cgit v1.2.3 From 7d81c3b9e2e5057c52256e154205143ad4e6e919 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 8 Jan 2011 20:00:36 -0800 Subject: xen: fix non-ANSI function warning in irq.c Fix sparse warning for non-ANSI function declaration: arch/x86/xen/irq.c:129:30: warning: non-ANSI function declaration of function 'xen_init_irq_ops' Signed-off-by: Randy Dunlap Cc: Jeremy Fitzhardinge Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/xen/irq.c b/arch/x86/xen/irq.c index 9d30105a0c4a..6a6fe8939645 100644 --- a/arch/x86/xen/irq.c +++ b/arch/x86/xen/irq.c @@ -126,7 +126,7 @@ static const struct pv_irq_ops xen_irq_ops __initdata = { #endif }; -void __init xen_init_irq_ops() +void __init xen_init_irq_ops(void) { pv_irq_ops = xen_irq_ops; x86_init.irqs.intr_init = xen_init_IRQ; -- cgit v1.2.3 From b23fffd778c312b8fb258d342051fcbdf6712128 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Jan 2011 13:14:10 -0800 Subject: ACPI / Battery: remove battery refresh on resume This partially reverts commit da8aeb92d4853f37e281f11fddf61f9c7d84c3cd ("ACPI / Battery: Update information on info notification and resume"), which causes a hang on resume on at least some machines. This bug was bisected on an ASUS EeePC 901, which hangs at resume time if we do that "acpi_battery_refresh(battery)" in the battery resume function. Rafael suspects we'll still need to refresh the sysfs files upon resume, but that that can be done from a PM notifier (that will run after thawing user space). Bisected-and-tested-by: Linus Torvalds Cc: Matthew Garrett Cc: Len Brown Acked-by: Rafael J. Wysocki Signed-off-by: Linus Torvalds --- drivers/acpi/battery.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 68bc227e7c4c..ac1a599f5147 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -998,7 +998,6 @@ static int acpi_battery_resume(struct acpi_device *device) if (!device) return -EINVAL; battery = acpi_driver_data(device); - acpi_battery_refresh(battery); battery->update_time = 0; acpi_battery_update(battery); return 0; -- cgit v1.2.3 From 4f8ba8a0c095933dd54a2c281750c8a85b329b26 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Sun, 21 Nov 2010 22:36:12 +0300 Subject: CIFS: Make cifsFileInfo_put work with strict cache mode On strict cache mode when we close the last file handle of the inode we should set invalid_mapping flag on this inode to prevent data coherency problem when we open it again but it has been modified on the server. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/file.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 7852cd677051..ac51cd2d33ae 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -40,6 +40,7 @@ #define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ #define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */ #define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */ +#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */ struct cifs_sb_info { struct rb_root tlink_tree; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index bd2a028af833..1b26c2717599 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -287,6 +287,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) struct inode *inode = cifs_file->dentry->d_inode; struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink); struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsLockInfo *li, *tmp; spin_lock(&cifs_file_list_lock); @@ -302,6 +303,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) if (list_empty(&cifsi->openFileList)) { cFYI(1, "closing last open instance for inode %p", cifs_file->dentry->d_inode); + + /* in strict cache mode we need invalidate mapping on the last + close because it may cause a error when we open this file + again and get at least level II oplock */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) + CIFS_I(inode)->invalid_mapping = true; + cifs_set_oplock_level(cifsi, 0); } spin_unlock(&cifs_file_list_lock); -- cgit v1.2.3 From 8be7e6ba142423e6ad98fed293c96f196f685229 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Sun, 12 Dec 2010 13:11:13 +0300 Subject: CIFS: Implement cifs_strict_fsync Invalidate inode mapping if we don't have at least Level II oplock in cifs_strict_fsync. Also remove filemap_write_and_wait call from cifs_fsync because it is previously called from vfs_fsync_range. Add file operations' structures for strict cache mode. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 38 ++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsfs.h | 8 ++++++-- fs/cifs/file.c | 36 ++++++++++++++++++++++++++++-------- fs/cifs/inode.c | 8 ++++++-- 4 files changed, 78 insertions(+), 12 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 99d777a03dd0..f6093e401c5a 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -733,6 +733,25 @@ const struct file_operations cifs_file_ops = { .setlease = cifs_setlease, }; +const struct file_operations cifs_file_strict_ops = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = cifs_file_aio_write, + .open = cifs_open, + .release = cifs_close, + .lock = cifs_lock, + .fsync = cifs_strict_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .llseek = cifs_llseek, +#ifdef CONFIG_CIFS_POSIX + .unlocked_ioctl = cifs_ioctl, +#endif /* CONFIG_CIFS_POSIX */ + .setlease = cifs_setlease, +}; + const struct file_operations cifs_file_direct_ops = { /* no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ @@ -751,6 +770,7 @@ const struct file_operations cifs_file_direct_ops = { .llseek = cifs_llseek, .setlease = cifs_setlease, }; + const struct file_operations cifs_file_nobrl_ops = { .read = do_sync_read, .write = do_sync_write, @@ -769,6 +789,24 @@ const struct file_operations cifs_file_nobrl_ops = { .setlease = cifs_setlease, }; +const struct file_operations cifs_file_strict_nobrl_ops = { + .read = do_sync_read, + .write = do_sync_write, + .aio_read = generic_file_aio_read, + .aio_write = cifs_file_aio_write, + .open = cifs_open, + .release = cifs_close, + .fsync = cifs_strict_fsync, + .flush = cifs_flush, + .mmap = cifs_file_mmap, + .splice_read = generic_file_splice_read, + .llseek = cifs_llseek, +#ifdef CONFIG_CIFS_POSIX + .unlocked_ioctl = cifs_ioctl, +#endif /* CONFIG_CIFS_POSIX */ + .setlease = cifs_setlease, +}; + const struct file_operations cifs_file_direct_nobrl_ops = { /* no mmap, no aio, no readv - BB reevaluate whether they can be done with directio, no cache */ diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 4739a531cded..10c4303c282d 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -61,6 +61,7 @@ extern int cifs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); extern int cifs_revalidate_file(struct file *filp); extern int cifs_revalidate_dentry(struct dentry *); +extern void cifs_invalidate_mapping(struct inode *inode); extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int cifs_setattr(struct dentry *, struct iattr *); @@ -72,8 +73,10 @@ extern const struct inode_operations cifs_dfs_referral_inode_operations; /* Functions related to files and directories */ extern const struct file_operations cifs_file_ops; extern const struct file_operations cifs_file_direct_ops; /* if directio mnt */ -extern const struct file_operations cifs_file_nobrl_ops; -extern const struct file_operations cifs_file_direct_nobrl_ops; /* no brlocks */ +extern const struct file_operations cifs_file_strict_ops; /* if strictio mnt */ +extern const struct file_operations cifs_file_nobrl_ops; /* no brlocks */ +extern const struct file_operations cifs_file_direct_nobrl_ops; +extern const struct file_operations cifs_file_strict_nobrl_ops; extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file); @@ -83,6 +86,7 @@ extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset); extern int cifs_lock(struct file *, int, struct file_lock *); extern int cifs_fsync(struct file *, int); +extern int cifs_strict_fsync(struct file *, int); extern int cifs_flush(struct file *, fl_owner_t id); extern int cifs_file_mmap(struct file * , struct vm_area_struct *); extern const struct file_operations cifs_dir_ops; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1b26c2717599..5790fab7349b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1528,27 +1528,47 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, return rc; } -int cifs_fsync(struct file *file, int datasync) +int cifs_strict_fsync(struct file *file, int datasync) { int xid; int rc = 0; struct cifsTconInfo *tcon; struct cifsFileInfo *smbfile = file->private_data; struct inode *inode = file->f_path.dentry->d_inode; + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); xid = GetXid(); cFYI(1, "Sync file - name: %s datasync: 0x%x", file->f_path.dentry->d_name.name, datasync); - rc = filemap_write_and_wait(inode->i_mapping); - if (rc == 0) { - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + if (!CIFS_I(inode)->clientCanCacheRead) + cifs_invalidate_mapping(inode); - tcon = tlink_tcon(smbfile->tlink); - if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) - rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); - } + tcon = tlink_tcon(smbfile->tlink); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) + rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); + + FreeXid(xid); + return rc; +} + +int cifs_fsync(struct file *file, int datasync) +{ + int xid; + int rc = 0; + struct cifsTconInfo *tcon; + struct cifsFileInfo *smbfile = file->private_data; + struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); + + xid = GetXid(); + + cFYI(1, "Sync file - name: %s datasync: 0x%x", + file->f_path.dentry->d_name.name, datasync); + + tcon = tlink_tcon(smbfile->tlink); + if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) + rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); FreeXid(xid); return rc; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 6c9ee8014ff0..8852470b4fbb 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -44,13 +44,17 @@ static void cifs_set_ops(struct inode *inode) inode->i_fop = &cifs_file_direct_nobrl_ops; else inode->i_fop = &cifs_file_direct_ops; + } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) + inode->i_fop = &cifs_file_strict_nobrl_ops; + else + inode->i_fop = &cifs_file_strict_ops; } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) inode->i_fop = &cifs_file_nobrl_ops; else { /* not direct, send byte range locks */ inode->i_fop = &cifs_file_ops; } - /* check if server can support readpages */ if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf < PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) @@ -1679,7 +1683,7 @@ cifs_inode_needs_reval(struct inode *inode) /* * Zap the cache. Called when invalid_mapping flag is set. */ -static void +void cifs_invalidate_mapping(struct inode *inode) { int rc; -- cgit v1.2.3 From 7a6a19b17ab9103ec708c18befd28f2a3908d4c1 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 14 Dec 2010 11:29:51 +0300 Subject: CIFS: Implement cifs_file_strict_mmap (try #2) Invalidate inode mapping if we don't have at least Level II oplock. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 4 ++-- fs/cifs/cifsfs.h | 1 + fs/cifs/file.c | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index f6093e401c5a..e24d966fb214 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -743,7 +743,7 @@ const struct file_operations cifs_file_strict_ops = { .lock = cifs_lock, .fsync = cifs_strict_fsync, .flush = cifs_flush, - .mmap = cifs_file_mmap, + .mmap = cifs_file_strict_mmap, .splice_read = generic_file_splice_read, .llseek = cifs_llseek, #ifdef CONFIG_CIFS_POSIX @@ -798,7 +798,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = { .release = cifs_close, .fsync = cifs_strict_fsync, .flush = cifs_flush, - .mmap = cifs_file_mmap, + .mmap = cifs_file_strict_mmap, .splice_read = generic_file_splice_read, .llseek = cifs_llseek, #ifdef CONFIG_CIFS_POSIX diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 10c4303c282d..710072e36912 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -89,6 +89,7 @@ extern int cifs_fsync(struct file *, int); extern int cifs_strict_fsync(struct file *, int); extern int cifs_flush(struct file *, fl_owner_t id); extern int cifs_file_mmap(struct file * , struct vm_area_struct *); +extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *); extern const struct file_operations cifs_dir_ops; extern int cifs_dir_open(struct inode *inode, struct file *file); extern int cifs_readdir(struct file *file, void *direntry, filldir_t filldir); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5790fab7349b..0b32377ef8b7 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1769,6 +1769,21 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, return total_read; } +int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma) +{ + int rc, xid; + struct inode *inode = file->f_path.dentry->d_inode; + + xid = GetXid(); + + if (!CIFS_I(inode)->clientCanCacheRead) + cifs_invalidate_mapping(inode); + + rc = generic_file_mmap(file, vma); + FreeXid(xid); + return rc; +} + int cifs_file_mmap(struct file *file, struct vm_area_struct *vma) { int rc, xid; -- cgit v1.2.3 From a70307eeeb25b89f6b2baf3cf3f0cef83c96ba12 Mon Sep 17 00:00:00 2001 From: Pavel Shilovsky Date: Tue, 14 Dec 2010 11:50:41 +0300 Subject: CIFS: Implement cifs_strict_readv (try #4) Read from the cache if we have at least Level II oplock - otherwise read from the server. Add cifs_user_readv to let the client read into iovec buffers. Reviewed-by: Jeff Layton Signed-off-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 4 +- fs/cifs/cifsfs.h | 4 +- fs/cifs/file.c | 116 +++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 84 insertions(+), 40 deletions(-) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index e24d966fb214..a8323f1dc1c4 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -736,7 +736,7 @@ const struct file_operations cifs_file_ops = { const struct file_operations cifs_file_strict_ops = { .read = do_sync_read, .write = do_sync_write, - .aio_read = generic_file_aio_read, + .aio_read = cifs_strict_readv, .aio_write = cifs_file_aio_write, .open = cifs_open, .release = cifs_close, @@ -792,7 +792,7 @@ const struct file_operations cifs_file_nobrl_ops = { const struct file_operations cifs_file_strict_nobrl_ops = { .read = do_sync_read, .write = do_sync_write, - .aio_read = generic_file_aio_read, + .aio_read = cifs_strict_readv, .aio_write = cifs_file_aio_write, .open = cifs_open, .release = cifs_close, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 710072e36912..f23206d46531 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -81,7 +81,9 @@ extern int cifs_open(struct inode *inode, struct file *file); extern int cifs_close(struct inode *inode, struct file *file); extern int cifs_closedir(struct inode *inode, struct file *file); extern ssize_t cifs_user_read(struct file *file, char __user *read_data, - size_t read_size, loff_t *poffset); + size_t read_size, loff_t *poffset); +extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos); extern ssize_t cifs_user_write(struct file *file, const char __user *write_data, size_t write_size, loff_t *poffset); extern int cifs_lock(struct file *, int, struct file_lock *); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 0b32377ef8b7..d7d65a70678e 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1619,42 +1619,42 @@ int cifs_flush(struct file *file, fl_owner_t id) return rc; } -ssize_t cifs_user_read(struct file *file, char __user *read_data, - size_t read_size, loff_t *poffset) +static ssize_t +cifs_iovec_read(struct file *file, const struct iovec *iov, + unsigned long nr_segs, loff_t *poffset) { - int rc = -EACCES; - unsigned int bytes_read = 0; - unsigned int total_read = 0; - unsigned int current_read_size; + int rc; + int xid; + unsigned int total_read, bytes_read = 0; + size_t len, cur_len; + int iov_offset = 0; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - int xid; struct cifsFileInfo *open_file; - char *smb_read_data; - char __user *current_offset; struct smb_com_read_rsp *pSMBr; + char *read_data; + + if (!nr_segs) + return 0; + + len = iov_length(iov, nr_segs); + if (!len) + return 0; xid = GetXid(); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); - if (file->private_data == NULL) { - rc = -EBADF; - FreeXid(xid); - return rc; - } open_file = file->private_data; pTcon = tlink_tcon(open_file->tlink); if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); - for (total_read = 0, current_offset = read_data; - read_size > total_read; - total_read += bytes_read, current_offset += bytes_read) { - current_read_size = min_t(const int, read_size - total_read, - cifs_sb->rsize); + for (total_read = 0; total_read < len; total_read += bytes_read) { + cur_len = min_t(const size_t, len - total_read, cifs_sb->rsize); rc = -EAGAIN; - smb_read_data = NULL; + read_data = NULL; + while (rc == -EAGAIN) { int buf_type = CIFS_NO_BUFFER; if (open_file->invalidHandle) { @@ -1662,27 +1662,25 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, if (rc != 0) break; } - rc = CIFSSMBRead(xid, pTcon, - open_file->netfid, - current_read_size, *poffset, - &bytes_read, &smb_read_data, - &buf_type); - pSMBr = (struct smb_com_read_rsp *)smb_read_data; - if (smb_read_data) { - if (copy_to_user(current_offset, - smb_read_data + - 4 /* RFC1001 length field */ + - le16_to_cpu(pSMBr->DataOffset), - bytes_read)) + rc = CIFSSMBRead(xid, pTcon, open_file->netfid, + cur_len, *poffset, &bytes_read, + &read_data, &buf_type); + pSMBr = (struct smb_com_read_rsp *)read_data; + if (read_data) { + char *data_offset = read_data + 4 + + le16_to_cpu(pSMBr->DataOffset); + if (memcpy_toiovecend(iov, data_offset, + iov_offset, bytes_read)) rc = -EFAULT; - if (buf_type == CIFS_SMALL_BUFFER) - cifs_small_buf_release(smb_read_data); + cifs_small_buf_release(read_data); else if (buf_type == CIFS_LARGE_BUFFER) - cifs_buf_release(smb_read_data); - smb_read_data = NULL; + cifs_buf_release(read_data); + read_data = NULL; + iov_offset += bytes_read; } } + if (rc || (bytes_read == 0)) { if (total_read) { break; @@ -1695,13 +1693,57 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, *poffset += bytes_read; } } + FreeXid(xid); return total_read; } +ssize_t cifs_user_read(struct file *file, char __user *read_data, + size_t read_size, loff_t *poffset) +{ + struct iovec iov; + iov.iov_base = read_data; + iov.iov_len = read_size; + + return cifs_iovec_read(file, &iov, 1, poffset); +} + +static ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + ssize_t read; + + read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos); + if (read > 0) + iocb->ki_pos = pos; + + return read; +} + +ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct inode *inode; + + inode = iocb->ki_filp->f_path.dentry->d_inode; + + if (CIFS_I(inode)->clientCanCacheRead) + return generic_file_aio_read(iocb, iov, nr_segs, pos); + + /* + * In strict cache mode we need to read from the server all the time + * if we don't have level II oplock because the server can delay mtime + * change - so we can't make a decision about inode invalidating. + * And we can also fail with pagereading if there are mandatory locks + * on pages affected by this read but not on the region from pos to + * pos+len-1. + */ + + return cifs_user_readv(iocb, iov, nr_segs, pos); +} static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, - loff_t *poffset) + loff_t *poffset) { int rc = -EACCES; unsigned int bytes_read = 0; -- cgit v1.2.3 From c3dccf48174e50668b7c63544ac8c60c07a45978 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:50 -0500 Subject: cifs: TCP_Server_Info diet Remove fields that are completely unused, and rearrange struct according to recommendations by "pahole". Before: /* size: 1112, cachelines: 18, members: 49 */ /* sum members: 1086, holes: 8, sum holes: 26 */ /* bit holes: 1, sum bit holes: 7 bits */ /* last cacheline: 24 bytes */ After: /* size: 1072, cachelines: 17, members: 42 */ /* sum members: 1065, holes: 3, sum holes: 7 */ /* last cacheline: 48 bytes */ ...savings of 40 bytes per struct on x86_64. 21 bytes by field removal, and 19 by reorganizing to eliminate holes. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 26 +++++++++----------------- fs/cifs/cifssmb.c | 2 -- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 571132c95231..36f097e1ee74 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -161,6 +161,7 @@ struct TCP_Server_Info { int srv_count; /* reference counter */ /* 15 character server name + 0x20 16th byte indicating type = srv */ char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; + enum statusEnum tcpStatus; /* what we think the status is */ char *hostname; /* hostname portion of UNC string */ struct socket *ssocket; struct sockaddr_storage dstaddr; @@ -168,25 +169,16 @@ struct TCP_Server_Info { wait_queue_head_t response_q; wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ struct list_head pending_mid_q; - void *Server_NlsInfo; /* BB - placeholder for future NLS info */ - unsigned short server_codepage; /* codepage for the server */ - enum protocolEnum protocolType; - char versionMajor; - char versionMinor; - bool svlocal:1; /* local server or remote */ bool noblocksnd; /* use blocking sendmsg */ bool noautotune; /* do not autotune send buf sizes */ bool tcp_nodelay; atomic_t inFlight; /* number of requests on the wire to server */ -#ifdef CONFIG_CIFS_STATS2 - atomic_t inSend; /* requests trying to send */ - atomic_t num_waiters; /* blocked waiting to get in sendrecv */ -#endif - enum statusEnum tcpStatus; /* what we think the status is */ struct mutex srv_mutex; struct task_struct *tsk; char server_GUID[16]; char secMode; + bool session_estab; /* mark when very first sess is established */ + u16 dialect; /* dialect index that server chose */ enum securityEnum secType; unsigned int maxReq; /* Clients should submit no more */ /* than maxReq distinct unanswered SMBs to the server when using */ @@ -199,8 +191,6 @@ struct TCP_Server_Info { unsigned int max_vcs; /* maximum number of smb sessions, at least those that can be specified uniquely with vcnumbers */ - char sessid[4]; /* unique token id for this session */ - /* (returned on Negotiate */ int capabilities; /* allow selective disabling of caps by smb sess */ int timeAdj; /* Adjust for difference in server time zone in sec */ __u16 CurrentMid; /* multiplex id - rotating counter */ @@ -210,18 +200,20 @@ struct TCP_Server_Info { __u32 sequence_number; /* for signing, protected by srv_mutex */ struct session_key session_key; unsigned long lstrp; /* when we got last response from this server */ - u16 dialect; /* dialect index that server chose */ struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */ /* extended security flavors that server supports */ + bool sec_ntlmssp; /* supports NTLMSSP */ + bool sec_kerberosu2u; /* supports U2U Kerberos */ bool sec_kerberos; /* supports plain Kerberos */ bool sec_mskerberos; /* supports legacy MS Kerberos */ - bool sec_kerberosu2u; /* supports U2U Kerberos */ - bool sec_ntlmssp; /* supports NTLMSSP */ - bool session_estab; /* mark when very first sess is established */ struct delayed_work echo; /* echo ping workqueue job */ #ifdef CONFIG_CIFS_FSCACHE struct fscache_cookie *fscache; /* client index cache cookie */ #endif +#ifdef CONFIG_CIFS_STATS2 + atomic_t inSend; /* requests trying to send */ + atomic_t num_waiters; /* blocked waiting to get in sendrecv */ +#endif }; /* diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 37113450757b..5b1f6637f161 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -452,7 +452,6 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize), (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs); - GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey); /* even though we do not use raw we might as well set this accurately, in case we ever find a need for it */ if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { @@ -566,7 +565,6 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE); server->max_rw = le32_to_cpu(pSMBr->MaxRawSize); cFYI(DBG2, "Max buf = %d", ses->server->maxBuf); - GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey); server->capabilities = le32_to_cpu(pSMBr->Capabilities); server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj *= 60; -- cgit v1.2.3 From aae62fdb6b9a6605abdea7370c4a0e005e6c1cd7 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:50 -0500 Subject: cifs: move time field in cifsInodeInfo ...and remove length qualifiers from bools. Before: /* size: 1176, cachelines: 19, members: 13 */ /* sum members: 1165, holes: 2, sum holes: 11 */ /* bit holes: 1, sum bit holes: 4 bits */ /* last cacheline: 24 bytes */ After: /* size: 1168, cachelines: 19, members: 13 */ /* last cacheline: 16 bytes */ ...savings of 8 bytes per inode. Signed-off-by: Jeff Layton Reviewed-by: Pavel Shilovsky Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 36f097e1ee74..5bfb75346cb0 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -439,11 +439,11 @@ struct cifsInodeInfo { /* BB add in lists for dirty pages i.e. write caching info for oplock */ struct list_head openFileList; __u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */ - unsigned long time; /* jiffies of last update/check of inode */ - bool clientCanCacheRead:1; /* read oplock */ - bool clientCanCacheAll:1; /* read and writebehind oplock */ - bool delete_pending:1; /* DELETE_ON_CLOSE is set */ - bool invalid_mapping:1; /* pagecache is invalid */ + bool clientCanCacheRead; /* read oplock */ + bool clientCanCacheAll; /* read and writebehind oplock */ + bool delete_pending; /* DELETE_ON_CLOSE is set */ + bool invalid_mapping; /* pagecache is invalid */ + unsigned long time; /* jiffies of last update of inode */ u64 server_eof; /* current file size on server */ u64 uniqueid; /* server inode number */ u64 createtime; /* creation time on server */ -- cgit v1.2.3 From 690c522fa5a62825af880775e3ef1e55755667b2 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:51 -0500 Subject: cifs: use get/put_unaligned functions to access ByteCount It's possible that when we access the ByteCount that the alignment will be off. Most CPUs deal with that transparently, but there's usually some performance impact. Some CPUs raise an exception on unaligned accesses. Fix this by accessing the byte count using the get_unaligned and put_unaligned inlined functions. While we're at it, fix the types of some of the variables that end up getting returns from these functions. Acked-by: Pavel Shilovsky Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifspdu.h | 47 +++++++++++++++++++++++++++++++++++++++++++---- fs/cifs/cifssmb.c | 14 +++++--------- fs/cifs/connect.c | 10 +++++----- fs/cifs/netmisc.c | 4 ++-- fs/cifs/sess.c | 13 ++++++------- fs/cifs/transport.c | 9 ++++----- 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index ea205b4fcad2..b5c8cc5d7a7f 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -23,6 +23,7 @@ #define _CIFSPDU_H #include +#include #include "smbfsctl.h" #ifdef CONFIG_CIFS_WEAK_PW_HASH @@ -426,11 +427,49 @@ struct smb_hdr { __u16 Mid; __u8 WordCount; } __attribute__((packed)); -/* given a pointer to an smb_hdr retrieve the value of byte count */ -#define BCC(smb_var) (*(__u16 *)((char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount))) -#define BCC_LE(smb_var) (*(__le16 *)((char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount))) + +/* given a pointer to an smb_hdr retrieve a char pointer to the byte count */ +#define BCC(smb_var) ((unsigned char *)(smb_var) + sizeof(struct smb_hdr) + \ + (2 * (smb_var)->WordCount)) + /* given a pointer to an smb_hdr retrieve the pointer to the byte area */ -#define pByteArea(smb_var) ((unsigned char *)(smb_var) + sizeof(struct smb_hdr) + (2 * (smb_var)->WordCount) + 2) +#define pByteArea(smb_var) (BCC(smb_var) + 2) + +/* get the converted ByteCount for a SMB packet and return it */ +static inline __u16 +get_bcc(struct smb_hdr *hdr) +{ + __u16 *bc_ptr = (__u16 *)BCC(hdr); + + return get_unaligned(bc_ptr); +} + +/* get the unconverted ByteCount for a SMB packet and return it */ +static inline __u16 +get_bcc_le(struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + return get_unaligned_le16(bc_ptr); +} + +/* set the ByteCount for a SMB packet in host-byte order */ +static inline void +put_bcc(__u16 count, struct smb_hdr *hdr) +{ + __u16 *bc_ptr = (__u16 *)BCC(hdr); + + put_unaligned(count, bc_ptr); +} + +/* set the ByteCount for a SMB packet in little-endian */ +static inline void +put_bcc_le(__u16 count, struct smb_hdr *hdr) +{ + __le16 *bc_ptr = (__le16 *)BCC(hdr); + + put_unaligned_le16(count, bc_ptr); +} /* * Computer Name Length (since Netbios name was length 16 with last byte 0x20) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 5b1f6637f161..39cec0d9cd1b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -333,7 +333,6 @@ static int validate_t2(struct smb_t2_rsp *pSMB) { int rc = -EINVAL; int total_size; - char *pBCC; /* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ @@ -346,13 +345,9 @@ static int validate_t2(struct smb_t2_rsp *pSMB) if (total_size < 512) { total_size += le16_to_cpu(pSMB->t2_rsp.DataCount); - /* BCC le converted in SendReceive */ - pBCC = (pSMB->hdr.WordCount * 2) + - sizeof(struct smb_hdr) + - (char *)pSMB; - if ((total_size <= (*(u16 *)pBCC)) && - (total_size < - CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) { + if (total_size <= get_bcc(&pSMB->hdr) && + total_size < + CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { return 0; } } @@ -362,6 +357,7 @@ static int validate_t2(struct smb_t2_rsp *pSMB) sizeof(struct smb_t2_rsp) + 16); return rc; } + int CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) { @@ -5609,7 +5605,7 @@ QAllEAsRetry: } /* make sure list_len doesn't go past end of SMB */ - end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr); + end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr); if ((char *)ea_response_data + list_len > end_of_smb) { cFYI(1, "EA list appears to go beyond SMB"); rc = -EIO; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 8d4657596301..ca20e813275d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -318,9 +318,9 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); total_in_buf += total_in_buf2; pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf); - byte_count = le16_to_cpu(BCC_LE(pTargetSMB)); + byte_count = get_bcc_le(pTargetSMB); byte_count += total_in_buf2; - BCC_LE(pTargetSMB) = cpu_to_le16(byte_count); + put_bcc_le(byte_count, pTargetSMB); byte_count = pTargetSMB->smb_buf_length; byte_count += total_in_buf2; @@ -2937,8 +2937,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, TCONX_RSP *pSMBr; unsigned char *bcc_ptr; int rc = 0; - int length, bytes_left; - __u16 count; + int length; + __u16 bytes_left, count; if (ses == NULL) return -EIO; @@ -3032,7 +3032,7 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, tcon->need_reconnect = false; tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); - bytes_left = BCC(smb_buffer_response); + bytes_left = get_bcc(smb_buffer_response); length = strnlen(bcc_ptr, bytes_left - 2); if (smb_buffer->Flags2 & SMBFLG2_UNICODE) is_unicode = true; diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 6783ce6cdc89..8d9189f64477 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -916,14 +916,14 @@ unsigned int smbCalcSize(struct smb_hdr *ptr) { return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + - 2 /* size of the bcc field */ + BCC(ptr)); + 2 /* size of the bcc field */ + get_bcc(ptr)); } unsigned int smbCalcSize_LE(struct smb_hdr *ptr) { return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + - 2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr))); + 2 /* size of the bcc field */ + get_bcc_le(ptr)); } /* The following are taken from fs/ntfs/util.c */ diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 1cffd82c4f13..1adc9625a344 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -277,7 +277,7 @@ static void ascii_ssetup_strings(char **pbcc_area, struct cifsSesInfo *ses, } static void -decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, +decode_unicode_ssetup(char **pbcc_area, __u16 bleft, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { int len; @@ -323,7 +323,7 @@ decode_unicode_ssetup(char **pbcc_area, int bleft, struct cifsSesInfo *ses, return; } -static int decode_ascii_ssetup(char **pbcc_area, int bleft, +static int decode_ascii_ssetup(char **pbcc_area, __u16 bleft, struct cifsSesInfo *ses, const struct nls_table *nls_cp) { @@ -575,12 +575,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, char *str_area; SESSION_SETUP_ANDX *pSMB; __u32 capabilities; - int count; + __u16 count; int resp_buf_type; struct kvec iov[3]; enum securityEnum type; - __u16 action; - int bytes_remaining; + __u16 action, bytes_remaining; struct key *spnego_key = NULL; __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ u16 blob_len; @@ -876,7 +875,7 @@ ssetup_ntlmssp_authenticate: count = iov[1].iov_len + iov[2].iov_len; smb_buf->smb_buf_length += count; - BCC_LE(smb_buf) = cpu_to_le16(count); + put_bcc_le(count, smb_buf); rc = SendReceive2(xid, ses, iov, 3 /* num_iovecs */, &resp_buf_type, CIFS_LOG_ERROR); @@ -910,7 +909,7 @@ ssetup_ntlmssp_authenticate: cFYI(1, "UID = %d ", ses->Suid); /* response can have either 3 or 4 word count - Samba sends 3 */ /* and lanman response is 3 */ - bytes_remaining = BCC(smb_buf); + bytes_remaining = get_bcc(smb_buf); bcc_ptr = pByteArea(smb_buf); if (smb_buf->WordCount == 4) { diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index c8e2808cd5e6..c1ccca1a933f 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -484,7 +484,7 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf, in_buf->smb_buf_length = sizeof(struct smb_hdr) - 4 + 2; in_buf->Command = SMB_COM_NT_CANCEL; in_buf->WordCount = 0; - BCC_LE(in_buf) = 0; + put_bcc_le(0, in_buf); mutex_lock(&server->srv_mutex); rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); @@ -632,8 +632,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) - BCC(midQ->resp_buf) = - le16_to_cpu(BCC_LE(midQ->resp_buf)); + put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf); if ((flags & CIFS_NO_RESP) == 0) midQ->resp_buf = NULL; /* mark it so buf will not be freed by @@ -776,7 +775,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) - BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); + put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf); } else { rc = -EIO; cERROR(1, "Bad MID state?"); @@ -977,7 +976,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) - BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); + put_bcc(get_bcc_le(out_buf), out_buf); out: delete_mid(midQ); -- cgit v1.2.3 From 12df83c9b901cfe8ca7a66fbe0effc6d873cbbb9 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:51 -0500 Subject: cifs: clean up unaligned accesses in validate_t2 ...and clean up function to reduce indentation. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Reviewed-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 39cec0d9cd1b..675041a6949c 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -331,31 +331,33 @@ smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon, static int validate_t2(struct smb_t2_rsp *pSMB) { - int rc = -EINVAL; - int total_size; + unsigned int total_size; + + /* check for plausible wct */ + if (pSMB->hdr.WordCount < 10) + goto vt2_err; - /* check for plausible wct, bcc and t2 data and parm sizes */ /* check for parm and data offset going beyond end of smb */ - if (pSMB->hdr.WordCount >= 10) { - if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) && - (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) { - /* check that bcc is at least as big as parms + data */ - /* check that bcc is less than negotiated smb buffer */ - total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount); - if (total_size < 512) { - total_size += - le16_to_cpu(pSMB->t2_rsp.DataCount); - if (total_size <= get_bcc(&pSMB->hdr) && - total_size < - CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { - return 0; - } - } - } - } + if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 || + get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024) + goto vt2_err; + + /* check that bcc is at least as big as parms + data */ + /* check that bcc is less than negotiated smb buffer */ + total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount); + if (total_size >= 512) + goto vt2_err; + + total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount); + if (total_size > get_bcc(&pSMB->hdr) || + total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) + goto vt2_err; + + return 0; +vt2_err: cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB, sizeof(struct smb_t2_rsp) + 16); - return rc; + return -EINVAL; } int -- cgit v1.2.3 From 26ec254869c0158ea8db6de83b7644e2d93cac2a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:51 -0500 Subject: cifs: fix unaligned access in check2ndT2 and coalesce_t2 Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Reviewed-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/connect.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index ca20e813275d..18d3c7724d6e 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -232,9 +232,8 @@ cifs_reconnect(struct TCP_Server_Info *server) static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize) { struct smb_t2_rsp *pSMBt; - int total_data_size; - int data_in_this_rsp; int remaining; + __u16 total_data_size, data_in_this_rsp; if (pSMB->Command != SMB_COM_TRANSACTION2) return 0; @@ -248,8 +247,8 @@ static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize) pSMBt = (struct smb_t2_rsp *)pSMB; - total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); - data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount); + total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); + data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); remaining = total_data_size - data_in_this_rsp; @@ -275,21 +274,18 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) { struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; - int total_data_size; - int total_in_buf; - int remaining; - int total_in_buf2; char *data_area_of_target; char *data_area_of_buf2; - __u16 byte_count; + int remaining; + __u16 byte_count, total_data_size, total_in_buf, total_in_buf2; - total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); + total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); - if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) { + if (total_data_size != + get_unaligned_le16(&pSMB2->t2_rsp.TotalDataCount)) cFYI(1, "total data size of primary and secondary t2 differ"); - } - total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount); + total_in_buf = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); remaining = total_data_size - total_in_buf; @@ -299,25 +295,25 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) if (remaining == 0) /* nothing to do, ignore */ return 0; - total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount); + total_in_buf2 = get_unaligned_le16(&pSMB2->t2_rsp.DataCount); if (remaining < total_in_buf2) { cFYI(1, "transact2 2nd response contains too much data"); } /* find end of first SMB data area */ data_area_of_target = (char *)&pSMBt->hdr.Protocol + - le16_to_cpu(pSMBt->t2_rsp.DataOffset); + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); /* validate target area */ - data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol + - le16_to_cpu(pSMB2->t2_rsp.DataOffset); + data_area_of_buf2 = (char *)&pSMB2->hdr.Protocol + + get_unaligned_le16(&pSMB2->t2_rsp.DataOffset); data_area_of_target += total_in_buf; /* copy second buffer into end of first buffer */ memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); total_in_buf += total_in_buf2; - pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf); + put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount); byte_count = get_bcc_le(pTargetSMB); byte_count += total_in_buf2; put_bcc_le(byte_count, pTargetSMB); @@ -334,7 +330,6 @@ static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) return 0; /* we are done */ } else /* more responses to go */ return 1; - } static void -- cgit v1.2.3 From ba2dbf30df210b519bdd8d34ac2ecaaeeef34c44 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:51 -0500 Subject: cifs: clean up unaligned accesses in cifs_unicode.c Make sure we use get/put_unaligned routines when accessing wide character strings. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Reviewed-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 51 +++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 430f510a1720..5f6e71857be6 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -44,10 +44,14 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes, int charlen, outlen = 0; int maxwords = maxbytes / 2; char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; - for (i = 0; i < maxwords && from[i]; i++) { - charlen = codepage->uni2char(le16_to_cpu(from[i]), tmp, - NLS_MAX_CHARSET_SIZE); + for (i = 0; i < maxwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + + charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE); if (charlen > 0) outlen += charlen; else @@ -58,9 +62,9 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes, } /* - * cifs_mapchar - convert a little-endian char to proper char in codepage + * cifs_mapchar - convert a host-endian char to proper char in codepage * @target - where converted character should be copied - * @src_char - 2 byte little-endian source character + * @src_char - 2 byte host-endian source character * @cp - codepage to which character should be converted * @mapchar - should character be mapped according to mapchars mount option? * @@ -69,7 +73,7 @@ cifs_ucs2_bytes(const __le16 *from, int maxbytes, * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE). */ static int -cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp, +cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp, bool mapchar) { int len = 1; @@ -82,7 +86,7 @@ cifs_mapchar(char *target, const __le16 src_char, const struct nls_table *cp, * build_path_from_dentry are modified, as they use slash as * separator. */ - switch (le16_to_cpu(src_char)) { + switch (src_char) { case UNI_COLON: *target = ':'; break; @@ -109,8 +113,7 @@ out: return len; cp_convert: - len = cp->uni2char(le16_to_cpu(src_char), target, - NLS_MAX_CHARSET_SIZE); + len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE); if (len <= 0) { *target = '?'; len = 1; @@ -149,6 +152,7 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, int nullsize = nls_nullsize(codepage); int fromwords = fromlen / 2; char tmp[NLS_MAX_CHARSET_SIZE]; + __u16 ftmp; /* * because the chars can be of varying widths, we need to take care @@ -158,19 +162,23 @@ cifs_from_ucs2(char *to, const __le16 *from, int tolen, int fromlen, */ safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize); - for (i = 0; i < fromwords && from[i]; i++) { + for (i = 0; i < fromwords; i++) { + ftmp = get_unaligned_le16(&from[i]); + if (ftmp == 0) + break; + /* * check to see if converting this character might make the * conversion bleed into the null terminator */ if (outlen >= safelen) { - charlen = cifs_mapchar(tmp, from[i], codepage, mapchar); + charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar); if ((outlen + charlen) > (tolen - nullsize)) break; } /* put converted char into 'to' buffer */ - charlen = cifs_mapchar(&to[outlen], from[i], codepage, mapchar); + charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar); outlen += charlen; } @@ -193,24 +201,21 @@ cifs_strtoUCS(__le16 *to, const char *from, int len, { int charlen; int i; - wchar_t *wchar_to = (wchar_t *)to; /* needed to quiet sparse */ + wchar_t wchar_to; /* needed to quiet sparse */ for (i = 0; len && *from; i++, from += charlen, len -= charlen) { - - /* works for 2.4.0 kernel or later */ - charlen = codepage->char2uni(from, len, &wchar_to[i]); + charlen = codepage->char2uni(from, len, &wchar_to); if (charlen < 1) { - cERROR(1, "strtoUCS: char2uni of %d returned %d", - (int)*from, charlen); + cERROR(1, "strtoUCS: char2uni of 0x%x returned %d", + *from, charlen); /* A question mark */ - to[i] = cpu_to_le16(0x003f); + wchar_to = 0x003f; charlen = 1; - } else - to[i] = cpu_to_le16(wchar_to[i]); - + } + put_unaligned_le16(wchar_to, &to[i]); } - to[i] = 0; + put_unaligned_le16(0, &to[i]); return i; } -- cgit v1.2.3 From 84cdf74e8096a10dd6acbb870dd404b92f07a756 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 13:36:51 -0500 Subject: cifs: fix unaligned accesses in cifsConvertToUCS Move cifsConvertToUCS to cifs_unicode.c where all of the other unicode related functions live. Have it store mapped characters in 'temp' and then use put_unaligned_le16 to copy it to the target buffer. Also fix the comments to match kernel coding style. Signed-off-by: Jeff Layton Acked-by: Pavel Shilovsky Reviewed-by: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/cifs_unicode.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/misc.c | 71 ---------------------------------------------- 2 files changed, 76 insertions(+), 71 deletions(-) diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 5f6e71857be6..fc0fd4fde306 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -257,3 +257,79 @@ cifs_strndup_from_ucs(const char *src, const int maxlen, const bool is_unicode, return dst; } +/* + * Convert 16 bit Unicode pathname to wire format from string in current code + * page. Conversion may involve remapping up the six characters that are + * only legal in POSIX-like OS (if they are present in the string). Path + * names are little endian 16 bit Unicode on the wire + */ +int +cifsConvertToUCS(__le16 *target, const char *source, int maxlen, + const struct nls_table *cp, int mapChars) +{ + int i, j, charlen; + int len_remaining = maxlen; + char src_char; + __u16 temp; + + if (!mapChars) + return cifs_strtoUCS(target, source, PATH_MAX, cp); + + for (i = 0, j = 0; i < maxlen; j++) { + src_char = source[i]; + switch (src_char) { + case 0: + put_unaligned_le16(0, &target[j]); + goto ctoUCS_out; + case ':': + temp = UNI_COLON; + break; + case '*': + temp = UNI_ASTERIK; + break; + case '?': + temp = UNI_QUESTION; + break; + case '<': + temp = UNI_LESSTHAN; + break; + case '>': + temp = UNI_GRTRTHAN; + break; + case '|': + temp = UNI_PIPE; + break; + /* + * FIXME: We can not handle remapping backslash (UNI_SLASH) + * until all the calls to build_path_from_dentry are modified, + * as they use backslash as separator. + */ + default: + charlen = cp->char2uni(source+i, len_remaining, + &temp); + /* + * if no match, use question mark, which at least in + * some cases serves as wild card + */ + if (charlen < 1) { + temp = 0x003f; + charlen = 1; + } + len_remaining -= charlen; + /* + * character may take more than one byte in the source + * string, but will take exactly two bytes in the + * target string + */ + i += charlen; + continue; + } + put_unaligned_le16(temp, &target[j]); + i++; /* move to next char in source string */ + len_remaining--; + } + +ctoUCS_out: + return i; +} + diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 09bfcf08a90f..a09e077ba925 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -637,77 +637,6 @@ dump_smb(struct smb_hdr *smb_buf, int smb_buf_length) return; } -/* Convert 16 bit Unicode pathname to wire format from string in current code - page. Conversion may involve remapping up the seven characters that are - only legal in POSIX-like OS (if they are present in the string). Path - names are little endian 16 bit Unicode on the wire */ -int -cifsConvertToUCS(__le16 *target, const char *source, int maxlen, - const struct nls_table *cp, int mapChars) -{ - int i, j, charlen; - int len_remaining = maxlen; - char src_char; - __u16 temp; - - if (!mapChars) - return cifs_strtoUCS(target, source, PATH_MAX, cp); - - for (i = 0, j = 0; i < maxlen; j++) { - src_char = source[i]; - switch (src_char) { - case 0: - target[j] = 0; - goto ctoUCS_out; - case ':': - target[j] = cpu_to_le16(UNI_COLON); - break; - case '*': - target[j] = cpu_to_le16(UNI_ASTERIK); - break; - case '?': - target[j] = cpu_to_le16(UNI_QUESTION); - break; - case '<': - target[j] = cpu_to_le16(UNI_LESSTHAN); - break; - case '>': - target[j] = cpu_to_le16(UNI_GRTRTHAN); - break; - case '|': - target[j] = cpu_to_le16(UNI_PIPE); - break; - /* BB We can not handle remapping slash until - all the calls to build_path_from_dentry - are modified, as they use slash as separator BB */ - /* case '\\': - target[j] = cpu_to_le16(UNI_SLASH); - break;*/ - default: - charlen = cp->char2uni(source+i, - len_remaining, &temp); - /* if no match, use question mark, which - at least in some cases servers as wild card */ - if (charlen < 1) { - target[j] = cpu_to_le16(0x003f); - charlen = 1; - } else - target[j] = cpu_to_le16(temp); - len_remaining -= charlen; - /* character may take more than one byte in the - the source string, but will take exactly two - bytes in the target string */ - i += charlen; - continue; - } - i++; /* move to next char in source string */ - len_remaining--; - } - -ctoUCS_out: - return i; -} - void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) { -- cgit v1.2.3 From 6044565af458e7fa6e748bff437ecc49dea88d79 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 15 Jan 2011 18:19:48 +0100 Subject: firewire: core: fix unstable I/O with Canon camcorder Regression since commit 10389536742c, "firewire: core: check for 1394a compliant IRM, fix inaccessibility of Sony camcorder": The camcorder Canon MV5i generates lots of bus resets when asynchronous requests are sent to it (e.g. Config ROM read requests or FCP Command write requests) if the camcorder is not root node. This causes drop- outs in videos or makes the camcorder entirely inaccessible. https://bugzilla.redhat.com/show_bug.cgi?id=633260 Fix this by allowing any Canon device, even if it is a pre-1394a IRM like MV5i are, to remain root node (if it is at least Cycle Master capable). With the FireWire controller cards that I tested, MV5i always becomes root node when plugged in and left to its own devices. Reported-by: Ralf Lange Signed-off-by: Stefan Richter Cc: # 2.6.32.y and newer --- drivers/firewire/core-card.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index be0492398ef9..24ff35511e2b 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -75,6 +75,8 @@ static size_t config_rom_length = 1 + 4 + 1 + 1; #define BIB_IRMC ((1) << 31) #define NODE_CAPABILITIES 0x0c0083c0 /* per IEEE 1394 clause 8.3.2.6.5.2 */ +#define CANON_OUI 0x000085 + static void generate_config_rom(struct fw_card *card, __be32 *config_rom) { struct fw_descriptor *desc; @@ -284,6 +286,7 @@ static void bm_work(struct work_struct *work) bool root_device_is_running; bool root_device_is_cmc; bool irm_is_1394_1995_only; + bool keep_this_irm; spin_lock_irq(&card->lock); @@ -305,6 +308,10 @@ static void bm_work(struct work_struct *work) irm_is_1394_1995_only = irm_device && irm_device->config_rom && (irm_device->config_rom[2] & 0x000000f0) == 0; + /* Canon MV5i works unreliably if it is not root node. */ + keep_this_irm = irm_device && irm_device->config_rom && + irm_device->config_rom[3] >> 8 == CANON_OUI; + root_id = root_node->node_id; irm_id = card->irm_node->node_id; local_id = card->local_node->node_id; @@ -333,7 +340,7 @@ static void bm_work(struct work_struct *work) goto pick_me; } - if (irm_is_1394_1995_only) { + if (irm_is_1394_1995_only && !keep_this_irm) { new_root_id = local_id; fw_notify("%s, making local node (%02x) root.\n", "IRM is not 1394a compliant", new_root_id); @@ -382,7 +389,7 @@ static void bm_work(struct work_struct *work) spin_lock_irq(&card->lock); - if (rcode != RCODE_COMPLETE) { + if (rcode != RCODE_COMPLETE && !keep_this_irm) { /* * The lock request failed, maybe the IRM * isn't really IRM capable after all. Let's -- cgit v1.2.3 From 74a145049938b73b7e5421423f64a254d4192d3f Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 29 Nov 2010 04:09:52 +0200 Subject: firewire: net: invalidate ARP entries of removed nodes This makes it possible to resume communication with a node that dropped off the bus for a brief period. Otherwise communication will only be possible after ARP cache entry timeouts. Signed-off-by: Maxim Levitsky Signed-off-by: Stefan Richter (rebased) --- drivers/firewire/net.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c index c2e194c58667..7ed08fd1214e 100644 --- a/drivers/firewire/net.c +++ b/drivers/firewire/net.c @@ -191,6 +191,7 @@ struct fwnet_peer { struct fwnet_device *dev; u64 guid; u64 fifo; + __be32 ip; /* guarded by dev->lock */ struct list_head pd_list; /* received partial datagrams */ @@ -570,6 +571,8 @@ static int fwnet_finish_incoming_packet(struct net_device *net, peer->speed = sspd; if (peer->max_payload > max_payload) peer->max_payload = max_payload; + + peer->ip = arp1394->sip; } spin_unlock_irqrestore(&dev->lock, flags); @@ -1470,6 +1473,7 @@ static int fwnet_add_peer(struct fwnet_device *dev, peer->dev = dev; peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; peer->fifo = FWNET_NO_FIFO_ADDR; + peer->ip = 0; INIT_LIST_HEAD(&peer->pd_list); peer->pdg_size = 0; peer->datagram_label = 0; @@ -1589,10 +1593,13 @@ static int fwnet_remove(struct device *_dev) mutex_lock(&fwnet_device_mutex); + net = dev->netdev; + if (net && peer->ip) + arp_invalidate(net, peer->ip); + fwnet_remove_peer(peer, dev); if (list_empty(&dev->peer_list)) { - net = dev->netdev; unregister_netdev(net); if (dev->local_fifo != FWNET_NO_FIFO_ADDR) -- cgit v1.2.3 From 324719978dbb3ffad5a2e3d85af6c5dbbb766b99 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 20 Jan 2011 00:07:46 +0100 Subject: firewire: net: is not experimental anymore thanks to Clemens' and Maxim's fixes to firewire-ohci and -net in the last two kernel releases. Signed-off-by: Stefan Richter --- drivers/firewire/Kconfig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index 68f942cb30f2..0c56989cd907 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -49,15 +49,13 @@ config FIREWIRE_SBP2 configuration section. config FIREWIRE_NET - tristate "IP networking over 1394 (EXPERIMENTAL)" - depends on FIREWIRE && INET && EXPERIMENTAL + tristate "IP networking over 1394" + depends on FIREWIRE && INET help This enables IPv4 over IEEE 1394, providing IP connectivity with other implementations of RFC 2734 as found on several operating systems. Multicast support is currently limited. - NOTE, this driver is not stable yet! - To compile this driver as a module, say M here: The module will be called firewire-net. -- cgit v1.2.3 From d7b9935a347ae954be907ea3d5eb4564ff124c53 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Jan 2011 13:19:55 -0800 Subject: i915: Fix i915 suspend delay During system suspend, the "wait for ring buffer to empty" loop would always time out after three seconds, because the faster cached ring buffer head read would always return zero. Force the slow-and-careful PIO read on all but the first iterations of the loop to fix it. This also removes the unused (and useless) 'actual_head' variable that tried to approximate doing this, but did it incorrectly. Cc: Chris Wilson Cc: Rafael J. Wysocki Cc: Jesse Barnes Cc: Dave Airlie Cc: DRI mailing list Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/intel_ringbuffer.c | 5 +++-- drivers/gpu/drm/i915/intel_ringbuffer.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 03e337072517..f6b9baa6a63d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -928,6 +928,7 @@ static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring) int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n) { + int reread = 0; struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; unsigned long end; @@ -940,9 +941,8 @@ int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n) * fallback to the slow and accurate path. */ head = intel_read_status_page(ring, 4); - if (head < ring->actual_head) + if (reread) head = I915_READ_HEAD(ring); - ring->actual_head = head; ring->head = head & HEAD_ADDR; ring->space = ring->head - (ring->tail + 8); if (ring->space < 0) @@ -961,6 +961,7 @@ int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n) msleep(1); if (atomic_read(&dev_priv->mm.wedged)) return -EAGAIN; + reread = 1; } while (!time_after(jiffies, end)); trace_i915_ring_wait_end (dev); return -EBUSY; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index be9087e4c9be..5b0abfa881fc 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -47,7 +47,6 @@ struct intel_ring_buffer { struct drm_device *dev; struct drm_i915_gem_object *obj; - u32 actual_head; u32 head; u32 tail; int space; -- cgit v1.2.3 From 28e58ee8ce1f0e69c207f747b7b9054b071e328d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Jan 2011 16:21:59 -0800 Subject: Fix broken "pipe: use event aware wakeups" optimization Commit e462c448fdc8 ("pipe: use event aware wakeups") optimized the pipe event wakeup calls to avoid wakeups if the events do not match the requested set. However, the optimization was buggy, in that it didn't actually use the correct sets for the events: when we make room for more data to be written, the pipe poll() routine will return both the POLLOUT _and_ POLLWRNORM bits. Similarly for read. And most critically, when a pipe is released, that will potentially result in POLLHUP|POLLERR (depending on whether it was the last reader or writer), not just the regular POLLIN|POLLOUT. This bug showed itself as a hung gnome-screensaver-dialog process, stuck forever (or at least until it was poked by a signal or by being traced) in a poll() system call. Cc: Davide Libenzi Cc: David S. Miller Cc: Eric Dumazet Cc: Jens Axboe Cc: Andrew Morton Signed-off-by: Linus Torvalds --- fs/pipe.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/pipe.c b/fs/pipe.c index 89e9e19b1b2e..da42f7db50de 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -441,7 +441,7 @@ redo: break; } if (do_wakeup) { - wake_up_interruptible_sync_poll(&pipe->wait, POLLOUT); + wake_up_interruptible_sync_poll(&pipe->wait, POLLOUT | POLLWRNORM); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } pipe_wait(pipe); @@ -450,7 +450,7 @@ redo: /* Signal writers asynchronously that there is more room. */ if (do_wakeup) { - wake_up_interruptible_sync_poll(&pipe->wait, POLLOUT); + wake_up_interruptible_sync_poll(&pipe->wait, POLLOUT | POLLWRNORM); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } if (ret > 0) @@ -612,7 +612,7 @@ redo2: break; } if (do_wakeup) { - wake_up_interruptible_sync_poll(&pipe->wait, POLLIN); + wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLRDNORM); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); do_wakeup = 0; } @@ -623,7 +623,7 @@ redo2: out: mutex_unlock(&inode->i_mutex); if (do_wakeup) { - wake_up_interruptible_sync_poll(&pipe->wait, POLLIN); + wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLRDNORM); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); } if (ret > 0) @@ -715,7 +715,7 @@ pipe_release(struct inode *inode, int decr, int decw) if (!pipe->readers && !pipe->writers) { free_pipe_info(inode); } else { - wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLOUT); + wake_up_interruptible_sync_poll(&pipe->wait, POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM | POLLERR | POLLHUP); kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); } -- cgit v1.2.3 From 6a3c869a6021f4abcd69aa5fbb15c63f69eb36fe Mon Sep 17 00:00:00 2001 From: Dimitris Michailidis Date: Wed, 19 Jan 2011 15:29:05 +0000 Subject: cxgb4: fix reported state of interfaces without link Currently tools like ip and ifconfig report incorrect state for cxgb4 interfaces that are up but do not have link and do so until first link establishment. This is because the initial netif_carrier_off call is before register_netdev and it needs to be after to be fully effective. Fix this by moving netif_carrier_off into .ndo_open. Signed-off-by: Dimitris Michailidis Signed-off-by: David S. Miller --- drivers/net/cxgb4/cxgb4_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/cxgb4/cxgb4_main.c b/drivers/net/cxgb4/cxgb4_main.c index 059c1eec8c3f..ec35d458102c 100644 --- a/drivers/net/cxgb4/cxgb4_main.c +++ b/drivers/net/cxgb4/cxgb4_main.c @@ -2710,6 +2710,8 @@ static int cxgb_open(struct net_device *dev) struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter; + netif_carrier_off(dev); + if (!(adapter->flags & FULL_INIT_DONE)) { err = cxgb_up(adapter); if (err < 0) @@ -3661,7 +3663,6 @@ static int __devinit init_one(struct pci_dev *pdev, pi->xact_addr_filt = -1; pi->rx_offload = RX_CSO; pi->port_id = i; - netif_carrier_off(netdev); netdev->irq = pdev->irq; netdev->features |= NETIF_F_SG | TSO_FLAGS; -- cgit v1.2.3 From b30532515f0a62bfe17207ab00883dd262497006 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 20 Jan 2011 09:02:31 +0000 Subject: bonding: Ensure that we unshare skbs prior to calling pskb_may_pull Recently reported oops: kernel BUG at net/core/skbuff.c:813! invalid opcode: 0000 [#1] SMP last sysfs file: /sys/devices/virtual/net/bond0/broadcast CPU 8 Modules linked in: sit tunnel4 cpufreq_ondemand acpi_cpufreq freq_table bonding ipv6 dm_mirror dm_region_hash dm_log cdc_ether usbnet mii serio_raw i2c_i801 i2c_core iTCO_wdt iTCO_vendor_support shpchp ioatdma i7core_edac edac_core bnx2 ixgbe dca mdio sg ext4 mbcache jbd2 sd_mod crc_t10dif mptsas mptscsih mptbase scsi_transport_sas dm_mod [last unloaded: microcode] Modules linked in: sit tunnel4 cpufreq_ondemand acpi_cpufreq freq_table bonding ipv6 dm_mirror dm_region_hash dm_log cdc_ether usbnet mii serio_raw i2c_i801 i2c_core iTCO_wdt iTCO_vendor_support shpchp ioatdma i7core_edac edac_core bnx2 ixgbe dca mdio sg ext4 mbcache jbd2 sd_mod crc_t10dif mptsas mptscsih mptbase scsi_transport_sas dm_mod [last unloaded: microcode] Pid: 0, comm: swapper Not tainted 2.6.32-71.el6.x86_64 #1 BladeCenter HS22 -[7870AC1]- RIP: 0010:[] [] pskb_expand_head+0x36/0x1e0 RSP: 0018:ffff880028303b70 EFLAGS: 00010202 RAX: 0000000000000002 RBX: ffff880c6458ec80 RCX: 0000000000000020 RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff880c6458ec80 RBP: ffff880028303bc0 R08: ffffffff818a6180 R09: ffff880c6458ed64 R10: ffff880c622b36c0 R11: 0000000000000400 R12: 0000000000000000 R13: 0000000000000180 R14: ffff880c622b3000 R15: 0000000000000000 FS: 0000000000000000(0000) GS:ffff880028300000(0000) knlGS:0000000000000000 CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b CR2: 00000038653452a4 CR3: 0000000001001000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process swapper (pid: 0, threadinfo ffff8806649c2000, task ffff880c64f16ab0) Stack: ffff880028303bc0 ffffffff8104fff9 000000000000001c 0000000100000000 <0> ffff880000047d80 ffff880c6458ec80 000000000000001c ffff880c6223da00 <0> ffff880c622b3000 0000000000000000 ffff880028303c10 ffffffff81407f7a Call Trace: [] ? __wake_up_common+0x59/0x90 [] __pskb_pull_tail+0x2aa/0x360 [] bond_arp_rcv+0x2c0/0x2e0 [bonding] [] ? packet_rcv+0x377/0x440 [] netif_receive_skb+0x2db/0x670 [] napi_skb_finish+0x58/0x70 [] napi_gro_receive+0x39/0x50 [] ixgbe_clean_rx_irq+0x35b/0x900 [ixgbe] [] ixgbe_clean_rxtx_many+0x136/0x240 [ixgbe] [] net_rx_action+0x103/0x210 [] __do_softirq+0xb7/0x1e0 [] ? handle_IRQ_event+0x60/0x170 [] call_softirq+0x1c/0x30 [] do_softirq+0x65/0xa0 [] irq_exit+0x85/0x90 [] do_IRQ+0x75/0xf0 [] ret_from_intr+0x0/0x11 [] ? mwait_idle+0x71/0xd0 [] ? atomic_notifier_call_chain+0x1a/0x20 [] cpu_idle+0xb6/0x110 [] start_secondary+0x1fc/0x23f Resulted from bonding driver registering packet handlers via dev_add_pack and then trying to call pskb_may_pull. If another packet handler (like for AF_PACKET sockets) gets called first, the delivered skb will have a user count > 1, which causes pskb_may_pull to BUG halt when it does its skb_shared check. Fix this by calling skb_share_check prior to the may_pull call sites in the bonding driver to clone the skb when needed. Tested by myself and the reported successfully. Signed-off-by: Neil Horman CC: Andy Gospodarek CC: Jay Vosburgh CC: "David S. Miller" Signed-off-by: Jay Vosburgh Signed-off-by: Andy Gospodarek Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 4 ++++ drivers/net/bonding/bond_alb.c | 4 ++++ drivers/net/bonding/bond_main.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 48cf24ff4e6f..c91874d25e54 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2470,6 +2470,10 @@ int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct pac if (!(dev->flags & IFF_MASTER)) goto out; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out; + if (!pskb_may_pull(skb, sizeof(struct lacpdu))) goto out; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index f4e638c65129..5c6fba802f2b 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -326,6 +326,10 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct goto out; } + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out; + if (!pskb_may_pull(skb, arp_hdr_len(bond_dev))) goto out; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b1025b85acf1..163e0b06eaa5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2733,6 +2733,10 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack if (!slave || !slave_do_arp_validate(bond, slave)) goto out_unlock; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out_unlock; + if (!pskb_may_pull(skb, arp_hdr_len(dev))) goto out_unlock; -- cgit v1.2.3 From 6a108a14fa356ef607be308b68337939e56ea94e Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Thu, 20 Jan 2011 14:44:16 -0800 Subject: kconfig: rename CONFIG_EMBEDDED to CONFIG_EXPERT The meaning of CONFIG_EMBEDDED has long since been obsoleted; the option is used to configure any non-standard kernel with a much larger scope than only small devices. This patch renames the option to CONFIG_EXPERT in init/Kconfig and fixes references to the option throughout the kernel. A new CONFIG_EMBEDDED option is added that automatically selects CONFIG_EXPERT when enabled and can be used in the future to isolate options that should only be considered for embedded systems (RISC architectures, SLOB, etc). Calling the option "EXPERT" more accurately represents its intention: only expert users who understand the impact of the configuration changes they are making should enable it. Reviewed-by: Ingo Molnar Acked-by: David Woodhouse Signed-off-by: David Rientjes Cc: Greg KH Cc: "David S. Miller" Cc: Jens Axboe Cc: Arnd Bergmann Cc: Robin Holt Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/arm/configs/ag5evm_defconfig | 2 +- arch/arm/configs/am200epdkit_defconfig | 2 +- arch/arm/configs/at572d940hfek_defconfig | 2 +- arch/arm/configs/badge4_defconfig | 2 +- arch/arm/configs/bcmring_defconfig | 2 +- arch/arm/configs/cm_x2xx_defconfig | 2 +- arch/arm/configs/colibri_pxa270_defconfig | 2 +- arch/arm/configs/collie_defconfig | 2 +- arch/arm/configs/corgi_defconfig | 2 +- arch/arm/configs/da8xx_omapl_defconfig | 2 +- arch/arm/configs/davinci_all_defconfig | 2 +- arch/arm/configs/dove_defconfig | 2 +- arch/arm/configs/ebsa110_defconfig | 2 +- arch/arm/configs/edb7211_defconfig | 2 +- arch/arm/configs/em_x270_defconfig | 2 +- arch/arm/configs/ep93xx_defconfig | 2 +- arch/arm/configs/eseries_pxa_defconfig | 2 +- arch/arm/configs/ezx_defconfig | 2 +- arch/arm/configs/footbridge_defconfig | 2 +- arch/arm/configs/fortunet_defconfig | 2 +- arch/arm/configs/h5000_defconfig | 2 +- arch/arm/configs/imote2_defconfig | 2 +- arch/arm/configs/ixp2000_defconfig | 2 +- arch/arm/configs/ixp23xx_defconfig | 2 +- arch/arm/configs/ixp4xx_defconfig | 2 +- arch/arm/configs/loki_defconfig | 2 +- arch/arm/configs/lpd7a400_defconfig | 2 +- arch/arm/configs/lpd7a404_defconfig | 2 +- arch/arm/configs/magician_defconfig | 2 +- arch/arm/configs/mv78xx0_defconfig | 2 +- arch/arm/configs/mx1_defconfig | 2 +- arch/arm/configs/mx21_defconfig | 2 +- arch/arm/configs/mx27_defconfig | 2 +- arch/arm/configs/mx3_defconfig | 2 +- arch/arm/configs/mx51_defconfig | 2 +- arch/arm/configs/nhk8815_defconfig | 2 +- arch/arm/configs/omap1_defconfig | 2 +- arch/arm/configs/omap2plus_defconfig | 2 +- arch/arm/configs/orion5x_defconfig | 2 +- arch/arm/configs/pcm027_defconfig | 2 +- arch/arm/configs/pcontrol_g20_defconfig | 2 +- arch/arm/configs/pleb_defconfig | 2 +- arch/arm/configs/pnx4008_defconfig | 2 +- arch/arm/configs/simpad_defconfig | 2 +- arch/arm/configs/spitz_defconfig | 2 +- arch/arm/configs/stmp378x_defconfig | 2 +- arch/arm/configs/stmp37xx_defconfig | 2 +- arch/arm/configs/tct_hammer_defconfig | 2 +- arch/arm/configs/trizeps4_defconfig | 2 +- arch/arm/configs/u300_defconfig | 2 +- arch/arm/configs/viper_defconfig | 2 +- arch/arm/configs/xcep_defconfig | 2 +- arch/avr32/Kconfig | 4 +- arch/blackfin/configs/BF518F-EZBRD_defconfig | 2 +- arch/blackfin/configs/BF526-EZBRD_defconfig | 2 +- arch/blackfin/configs/BF527-AD7160-EVAL_defconfig | 2 +- arch/blackfin/configs/BF527-EZKIT-V2_defconfig | 2 +- arch/blackfin/configs/BF527-EZKIT_defconfig | 2 +- arch/blackfin/configs/BF527-TLL6527M_defconfig | 2 +- arch/blackfin/configs/BF533-EZKIT_defconfig | 2 +- arch/blackfin/configs/BF533-STAMP_defconfig | 2 +- arch/blackfin/configs/BF537-STAMP_defconfig | 2 +- arch/blackfin/configs/BF538-EZKIT_defconfig | 2 +- arch/blackfin/configs/BF548-EZKIT_defconfig | 2 +- arch/blackfin/configs/BF561-ACVILON_defconfig | 2 +- arch/blackfin/configs/BF561-EZKIT-SMP_defconfig | 2 +- arch/blackfin/configs/BF561-EZKIT_defconfig | 2 +- arch/blackfin/configs/BlackStamp_defconfig | 2 +- arch/blackfin/configs/CM-BF527_defconfig | 2 +- arch/blackfin/configs/CM-BF533_defconfig | 2 +- arch/blackfin/configs/CM-BF537E_defconfig | 2 +- arch/blackfin/configs/CM-BF537U_defconfig | 2 +- arch/blackfin/configs/CM-BF548_defconfig | 2 +- arch/blackfin/configs/CM-BF561_defconfig | 2 +- arch/blackfin/configs/DNP5370_defconfig | 2 +- arch/blackfin/configs/H8606_defconfig | 2 +- arch/blackfin/configs/IP0X_defconfig | 2 +- arch/blackfin/configs/PNAV-10_defconfig | 2 +- arch/blackfin/configs/SRV1_defconfig | 2 +- arch/blackfin/configs/TCM-BF518_defconfig | 2 +- arch/blackfin/configs/TCM-BF537_defconfig | 2 +- arch/cris/configs/artpec_3_defconfig | 2 +- arch/cris/configs/etrax-100lx_v2_defconfig | 2 +- arch/cris/configs/etraxfs_defconfig | 2 +- arch/frv/defconfig | 2 +- arch/h8300/defconfig | 2 +- arch/m32r/configs/m32700ut.smp_defconfig | 2 +- arch/m32r/configs/m32700ut.up_defconfig | 2 +- arch/m32r/configs/mappi.nommu_defconfig | 2 +- arch/m32r/configs/mappi.smp_defconfig | 2 +- arch/m32r/configs/mappi.up_defconfig | 2 +- arch/m32r/configs/mappi2.opsp_defconfig | 2 +- arch/m32r/configs/mappi2.vdec2_defconfig | 2 +- arch/m32r/configs/mappi3.smp_defconfig | 2 +- arch/m32r/configs/oaks32r_defconfig | 2 +- arch/m32r/configs/opsput_defconfig | 2 +- arch/m32r/configs/usrv_defconfig | 2 +- arch/m68knommu/configs/m5208evb_defconfig | 2 +- arch/m68knommu/configs/m5249evb_defconfig | 2 +- arch/m68knommu/configs/m5272c3_defconfig | 2 +- arch/m68knommu/configs/m5275evb_defconfig | 2 +- arch/m68knommu/configs/m5307c3_defconfig | 2 +- arch/m68knommu/configs/m5407c3_defconfig | 2 +- arch/m68knommu/defconfig | 2 +- arch/microblaze/configs/mmu_defconfig | 2 +- arch/microblaze/configs/nommu_defconfig | 2 +- arch/mips/Kconfig.debug | 2 +- arch/mips/configs/ar7_defconfig | 2 +- arch/mips/configs/bcm47xx_defconfig | 2 +- arch/mips/configs/bcm63xx_defconfig | 2 +- arch/mips/configs/bigsur_defconfig | 2 +- arch/mips/configs/capcella_defconfig | 2 +- arch/mips/configs/cavium-octeon_defconfig | 2 +- arch/mips/configs/cobalt_defconfig | 2 +- arch/mips/configs/db1000_defconfig | 2 +- arch/mips/configs/db1100_defconfig | 2 +- arch/mips/configs/db1200_defconfig | 2 +- arch/mips/configs/db1500_defconfig | 2 +- arch/mips/configs/db1550_defconfig | 2 +- arch/mips/configs/decstation_defconfig | 2 +- arch/mips/configs/e55_defconfig | 2 +- arch/mips/configs/fuloong2e_defconfig | 2 +- arch/mips/configs/gpr_defconfig | 2 +- arch/mips/configs/ip22_defconfig | 2 +- arch/mips/configs/ip27_defconfig | 2 +- arch/mips/configs/ip28_defconfig | 2 +- arch/mips/configs/ip32_defconfig | 2 +- arch/mips/configs/jazz_defconfig | 2 +- arch/mips/configs/jmr3927_defconfig | 2 +- arch/mips/configs/lasat_defconfig | 2 +- arch/mips/configs/lemote2f_defconfig | 2 +- arch/mips/configs/malta_defconfig | 2 +- arch/mips/configs/markeins_defconfig | 2 +- arch/mips/configs/mipssim_defconfig | 2 +- arch/mips/configs/mpc30x_defconfig | 2 +- arch/mips/configs/msp71xx_defconfig | 2 +- arch/mips/configs/mtx1_defconfig | 2 +- arch/mips/configs/pb1100_defconfig | 2 +- arch/mips/configs/pb1200_defconfig | 2 +- arch/mips/configs/pb1500_defconfig | 2 +- arch/mips/configs/pb1550_defconfig | 2 +- arch/mips/configs/pnx8335-stb225_defconfig | 2 +- arch/mips/configs/pnx8550-jbs_defconfig | 2 +- arch/mips/configs/pnx8550-stb810_defconfig | 2 +- arch/mips/configs/powertv_defconfig | 2 +- arch/mips/configs/rb532_defconfig | 2 +- arch/mips/configs/rbtx49xx_defconfig | 2 +- arch/mips/configs/rm200_defconfig | 2 +- arch/mips/configs/sb1250-swarm_defconfig | 2 +- arch/mips/configs/tb0219_defconfig | 2 +- arch/mips/configs/tb0226_defconfig | 2 +- arch/mips/configs/tb0287_defconfig | 2 +- arch/mips/configs/workpad_defconfig | 2 +- arch/mips/configs/wrppmc_defconfig | 2 +- arch/mips/configs/yosemite_defconfig | 2 +- arch/mn10300/configs/asb2303_defconfig | 2 +- arch/mn10300/configs/asb2364_defconfig | 2 +- arch/parisc/configs/a500_defconfig | 2 +- arch/parisc/configs/c3000_defconfig | 2 +- arch/powerpc/configs/40x/acadia_defconfig | 2 +- arch/powerpc/configs/40x/ep405_defconfig | 2 +- arch/powerpc/configs/40x/hcu4_defconfig | 2 +- arch/powerpc/configs/40x/kilauea_defconfig | 2 +- arch/powerpc/configs/40x/makalu_defconfig | 2 +- arch/powerpc/configs/40x/walnut_defconfig | 2 +- arch/powerpc/configs/44x/arches_defconfig | 2 +- arch/powerpc/configs/44x/bamboo_defconfig | 2 +- arch/powerpc/configs/44x/bluestone_defconfig | 2 +- arch/powerpc/configs/44x/canyonlands_defconfig | 2 +- arch/powerpc/configs/44x/ebony_defconfig | 2 +- arch/powerpc/configs/44x/eiger_defconfig | 2 +- arch/powerpc/configs/44x/icon_defconfig | 2 +- arch/powerpc/configs/44x/iss476-smp_defconfig | 2 +- arch/powerpc/configs/44x/katmai_defconfig | 2 +- arch/powerpc/configs/44x/rainier_defconfig | 2 +- arch/powerpc/configs/44x/redwood_defconfig | 2 +- arch/powerpc/configs/44x/sam440ep_defconfig | 2 +- arch/powerpc/configs/44x/sequoia_defconfig | 2 +- arch/powerpc/configs/44x/taishan_defconfig | 2 +- arch/powerpc/configs/44x/warp_defconfig | 2 +- arch/powerpc/configs/52xx/cm5200_defconfig | 2 +- arch/powerpc/configs/52xx/lite5200b_defconfig | 2 +- arch/powerpc/configs/52xx/motionpro_defconfig | 2 +- arch/powerpc/configs/52xx/pcm030_defconfig | 2 +- arch/powerpc/configs/52xx/tqm5200_defconfig | 2 +- arch/powerpc/configs/83xx/asp8347_defconfig | 2 +- arch/powerpc/configs/83xx/kmeter1_defconfig | 2 +- arch/powerpc/configs/83xx/mpc8313_rdb_defconfig | 2 +- arch/powerpc/configs/83xx/mpc8315_rdb_defconfig | 2 +- arch/powerpc/configs/83xx/mpc832x_mds_defconfig | 2 +- arch/powerpc/configs/83xx/mpc832x_rdb_defconfig | 2 +- arch/powerpc/configs/83xx/mpc834x_itx_defconfig | 2 +- arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig | 2 +- arch/powerpc/configs/83xx/mpc834x_mds_defconfig | 2 +- arch/powerpc/configs/83xx/mpc836x_mds_defconfig | 2 +- arch/powerpc/configs/83xx/mpc836x_rdk_defconfig | 2 +- arch/powerpc/configs/83xx/mpc837x_mds_defconfig | 2 +- arch/powerpc/configs/83xx/mpc837x_rdb_defconfig | 2 +- arch/powerpc/configs/83xx/sbc834x_defconfig | 2 +- arch/powerpc/configs/85xx/ksi8560_defconfig | 2 +- arch/powerpc/configs/85xx/mpc8540_ads_defconfig | 2 +- arch/powerpc/configs/85xx/mpc8560_ads_defconfig | 2 +- arch/powerpc/configs/85xx/mpc85xx_cds_defconfig | 2 +- arch/powerpc/configs/85xx/sbc8548_defconfig | 2 +- arch/powerpc/configs/85xx/sbc8560_defconfig | 2 +- arch/powerpc/configs/85xx/socrates_defconfig | 2 +- arch/powerpc/configs/85xx/stx_gp3_defconfig | 2 +- arch/powerpc/configs/85xx/tqm8540_defconfig | 2 +- arch/powerpc/configs/85xx/tqm8541_defconfig | 2 +- arch/powerpc/configs/85xx/tqm8548_defconfig | 2 +- arch/powerpc/configs/85xx/tqm8555_defconfig | 2 +- arch/powerpc/configs/85xx/tqm8560_defconfig | 2 +- arch/powerpc/configs/85xx/xes_mpc85xx_defconfig | 2 +- arch/powerpc/configs/86xx/gef_ppc9a_defconfig | 2 +- arch/powerpc/configs/86xx/gef_sbc310_defconfig | 2 +- arch/powerpc/configs/86xx/gef_sbc610_defconfig | 2 +- arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig | 2 +- arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig | 2 +- arch/powerpc/configs/86xx/sbc8641d_defconfig | 2 +- arch/powerpc/configs/adder875_defconfig | 2 +- arch/powerpc/configs/e55xx_smp_defconfig | 2 +- arch/powerpc/configs/ep8248e_defconfig | 2 +- arch/powerpc/configs/ep88xc_defconfig | 2 +- arch/powerpc/configs/gamecube_defconfig | 2 +- arch/powerpc/configs/holly_defconfig | 2 +- arch/powerpc/configs/mgcoge_defconfig | 2 +- arch/powerpc/configs/mgsuvd_defconfig | 2 +- arch/powerpc/configs/mpc7448_hpc2_defconfig | 2 +- arch/powerpc/configs/mpc8272_ads_defconfig | 2 +- arch/powerpc/configs/mpc83xx_defconfig | 2 +- arch/powerpc/configs/mpc85xx_defconfig | 2 +- arch/powerpc/configs/mpc85xx_smp_defconfig | 2 +- arch/powerpc/configs/mpc866_ads_defconfig | 2 +- arch/powerpc/configs/mpc86xx_defconfig | 2 +- arch/powerpc/configs/mpc885_ads_defconfig | 2 +- arch/powerpc/configs/ppc40x_defconfig | 2 +- arch/powerpc/configs/ppc44x_defconfig | 2 +- arch/powerpc/configs/pq2fads_defconfig | 2 +- arch/powerpc/configs/ps3_defconfig | 2 +- arch/powerpc/configs/storcenter_defconfig | 2 +- arch/powerpc/configs/tqm8xx_defconfig | 2 +- arch/powerpc/configs/wii_defconfig | 2 +- arch/powerpc/platforms/iseries/Kconfig | 2 +- arch/powerpc/platforms/pseries/Kconfig | 6 +-- arch/score/configs/spct6600_defconfig | 2 +- arch/sh/Kconfig | 2 +- arch/tile/Kconfig | 2 +- arch/tile/Kconfig.debug | 2 +- arch/tile/configs/tile_defconfig | 2 +- arch/um/defconfig | 2 +- arch/x86/Kconfig | 20 +++---- arch/x86/Kconfig.cpu | 2 +- arch/x86/Kconfig.debug | 4 +- arch/xtensa/configs/common_defconfig | 2 +- arch/xtensa/configs/iss_defconfig | 2 +- arch/xtensa/configs/s6105_defconfig | 2 +- block/Kconfig | 2 +- drivers/acpi/Kconfig | 2 +- drivers/ata/Kconfig | 2 +- drivers/base/Kconfig | 2 +- drivers/char/Kconfig | 10 ++-- drivers/cpufreq/Kconfig | 2 +- drivers/firmware/Kconfig | 2 +- drivers/gpu/drm/Kconfig | 2 +- drivers/gpu/drm/drm_fb_helper.c | 4 +- drivers/gpu/drm/nouveau/Kconfig | 2 +- drivers/gpu/vga/Kconfig | 2 +- drivers/hid/Kconfig | 64 +++++++++++------------ drivers/hid/usbhid/Kconfig | 2 +- drivers/ide/Kconfig | 2 +- drivers/infiniband/hw/mthca/Kconfig | 2 +- drivers/infiniband/ulp/ipoib/Kconfig | 2 +- drivers/input/Kconfig | 6 +-- drivers/input/keyboard/Kconfig | 4 +- drivers/input/mouse/Kconfig | 10 ++-- drivers/input/serio/Kconfig | 6 +-- drivers/input/touchscreen/Kconfig | 30 +++++------ drivers/media/common/tuners/Kconfig | 2 +- drivers/media/dvb/frontends/Kconfig | 2 +- drivers/media/video/Kconfig | 2 +- drivers/net/Kconfig | 2 +- drivers/pci/pcie/Kconfig | 2 +- drivers/pcmcia/Kconfig | 12 ++--- drivers/serial/Kconfig | 4 +- drivers/ssb/Kconfig | 2 +- drivers/usb/core/Kconfig | 6 +-- drivers/video/Kconfig | 2 +- drivers/video/console/Kconfig | 2 +- fs/Kconfig | 2 +- fs/proc/Kconfig | 6 +-- fs/sysfs/Kconfig | 2 +- init/Kconfig | 60 ++++++++++++--------- lib/Kconfig.debug | 6 +-- lib/xz/Kconfig | 12 ++--- net/mac80211/Kconfig | 6 +-- net/rfkill/Kconfig | 4 +- net/wireless/Kconfig | 2 +- usr/Kconfig | 18 +++---- 298 files changed, 431 insertions(+), 423 deletions(-) diff --git a/arch/arm/configs/ag5evm_defconfig b/arch/arm/configs/ag5evm_defconfig index 2b9cf56db363..212ead354a6b 100644 --- a/arch/arm/configs/ag5evm_defconfig +++ b/arch/arm/configs/ag5evm_defconfig @@ -10,7 +10,7 @@ CONFIG_NAMESPACES=y # CONFIG_PID_NS is not set CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_DEADLINE is not set diff --git a/arch/arm/configs/am200epdkit_defconfig b/arch/arm/configs/am200epdkit_defconfig index 5536c488dd01..f0dea52e49c4 100644 --- a/arch/arm/configs/am200epdkit_defconfig +++ b/arch/arm/configs/am200epdkit_defconfig @@ -3,7 +3,7 @@ CONFIG_LOCALVERSION="gum" # CONFIG_SWAP is not set CONFIG_SYSVIPC=y CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_EPOLL is not set # CONFIG_SHMEM is not set diff --git a/arch/arm/configs/at572d940hfek_defconfig b/arch/arm/configs/at572d940hfek_defconfig index 695e32d4fb58..1b1158ae8f82 100644 --- a/arch/arm/configs/at572d940hfek_defconfig +++ b/arch/arm/configs/at572d940hfek_defconfig @@ -17,7 +17,7 @@ CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_OPROFILE=m diff --git a/arch/arm/configs/badge4_defconfig b/arch/arm/configs/badge4_defconfig index 3a1ad15a779f..5b54abbeb0b3 100644 --- a/arch/arm/configs/badge4_defconfig +++ b/arch/arm/configs/badge4_defconfig @@ -1,6 +1,6 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y CONFIG_ARCH_SA1100=y diff --git a/arch/arm/configs/bcmring_defconfig b/arch/arm/configs/bcmring_defconfig index 75984cd1e233..795374d48f81 100644 --- a/arch/arm/configs/bcmring_defconfig +++ b/arch/arm/configs/bcmring_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_LOCALVERSION_AUTO is not set # CONFIG_SWAP is not set CONFIG_SYSVIPC=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_HOTPLUG is not set # CONFIG_ELF_CORE is not set diff --git a/arch/arm/configs/cm_x2xx_defconfig b/arch/arm/configs/cm_x2xx_defconfig index dcfbcf3b6c3e..a93ff8da5bab 100644 --- a/arch/arm/configs/cm_x2xx_defconfig +++ b/arch/arm/configs/cm_x2xx_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set diff --git a/arch/arm/configs/colibri_pxa270_defconfig b/arch/arm/configs/colibri_pxa270_defconfig index f52c64e36d8d..2ef2c5e8aaec 100644 --- a/arch/arm/configs/colibri_pxa270_defconfig +++ b/arch/arm/configs/colibri_pxa270_defconfig @@ -8,7 +8,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/collie_defconfig b/arch/arm/configs/collie_defconfig index 310f9a6270be..6c56ad086c7c 100644 --- a/arch/arm/configs/collie_defconfig +++ b/arch/arm/configs/collie_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_BASE_FULL is not set # CONFIG_EPOLL is not set CONFIG_SLOB=y diff --git a/arch/arm/configs/corgi_defconfig b/arch/arm/configs/corgi_defconfig index 4a1fa81ed37d..e53c47563845 100644 --- a/arch/arm/configs/corgi_defconfig +++ b/arch/arm/configs/corgi_defconfig @@ -4,7 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_PROFILING=y CONFIG_OPROFILE=m CONFIG_MODULES=y diff --git a/arch/arm/configs/da8xx_omapl_defconfig b/arch/arm/configs/da8xx_omapl_defconfig index cdc40c4b8c48..88ccde058ba4 100644 --- a/arch/arm/configs/da8xx_omapl_defconfig +++ b/arch/arm/configs/da8xx_omapl_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y diff --git a/arch/arm/configs/davinci_all_defconfig b/arch/arm/configs/davinci_all_defconfig index 2519cc5a5f8f..889922ad229c 100644 --- a/arch/arm/configs/davinci_all_defconfig +++ b/arch/arm/configs/davinci_all_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig index 9359e1bf32c1..54bf5eec8016 100644 --- a/arch/arm/configs/dove_defconfig +++ b/arch/arm/configs/dove_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/ebsa110_defconfig b/arch/arm/configs/ebsa110_defconfig index c3194186920c..14559dbb4c2c 100644 --- a/arch/arm/configs/ebsa110_defconfig +++ b/arch/arm/configs/ebsa110_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_ARCH_EBSA110=y CONFIG_PCCARD=m diff --git a/arch/arm/configs/edb7211_defconfig b/arch/arm/configs/edb7211_defconfig index 7b62be1561ea..d52ded350a12 100644 --- a/arch/arm/configs/edb7211_defconfig +++ b/arch/arm/configs/edb7211_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_ARCH_CLPS711X=y CONFIG_ARCH_EDB7211=y diff --git a/arch/arm/configs/em_x270_defconfig b/arch/arm/configs/em_x270_defconfig index d7db34f79702..60a21e01eb70 100644 --- a/arch/arm/configs/em_x270_defconfig +++ b/arch/arm/configs/em_x270_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set diff --git a/arch/arm/configs/ep93xx_defconfig b/arch/arm/configs/ep93xx_defconfig index 6d6689cdf398..8e97b2f7ceec 100644 --- a/arch/arm/configs/ep93xx_defconfig +++ b/arch/arm/configs/ep93xx_defconfig @@ -4,7 +4,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/eseries_pxa_defconfig b/arch/arm/configs/eseries_pxa_defconfig index 1691dea582fe..d68ac67c201c 100644 --- a/arch/arm/configs/eseries_pxa_defconfig +++ b/arch/arm/configs/eseries_pxa_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y diff --git a/arch/arm/configs/ezx_defconfig b/arch/arm/configs/ezx_defconfig index c4eeb6d1cbf0..227a477346ed 100644 --- a/arch/arm/configs/ezx_defconfig +++ b/arch/arm/configs/ezx_defconfig @@ -7,7 +7,7 @@ CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_RD_BZIP2=y CONFIG_RD_LZMA=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/footbridge_defconfig b/arch/arm/configs/footbridge_defconfig index 4f925ead2617..038518ab39a8 100644 --- a/arch/arm/configs/footbridge_defconfig +++ b/arch/arm/configs/footbridge_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_MODULES=y CONFIG_ARCH_FOOTBRIDGE=y diff --git a/arch/arm/configs/fortunet_defconfig b/arch/arm/configs/fortunet_defconfig index e11c7eab8ed0..840fced7529f 100644 --- a/arch/arm/configs/fortunet_defconfig +++ b/arch/arm/configs/fortunet_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_ARCH_CLPS711X=y CONFIG_ARCH_FORTUNET=y diff --git a/arch/arm/configs/h5000_defconfig b/arch/arm/configs/h5000_defconfig index ac336f10000c..37903e3f0efc 100644 --- a/arch/arm/configs/h5000_defconfig +++ b/arch/arm/configs/h5000_defconfig @@ -4,7 +4,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/imote2_defconfig b/arch/arm/configs/imote2_defconfig index ade55c8c408b..176ec22af034 100644 --- a/arch/arm/configs/imote2_defconfig +++ b/arch/arm/configs/imote2_defconfig @@ -6,7 +6,7 @@ CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_RD_BZIP2=y CONFIG_RD_LZMA=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/ixp2000_defconfig b/arch/arm/configs/ixp2000_defconfig index 908324684549..8405aded97a3 100644 --- a/arch/arm/configs/ixp2000_defconfig +++ b/arch/arm/configs/ixp2000_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/ixp23xx_defconfig b/arch/arm/configs/ixp23xx_defconfig index 7fc056a8569c..688717612e91 100644 --- a/arch/arm/configs/ixp23xx_defconfig +++ b/arch/arm/configs/ixp23xx_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/ixp4xx_defconfig b/arch/arm/configs/ixp4xx_defconfig index 5c5023934001..063e2ab2c8f1 100644 --- a/arch/arm/configs/ixp4xx_defconfig +++ b/arch/arm/configs/ixp4xx_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/arm/configs/loki_defconfig b/arch/arm/configs/loki_defconfig index e1eaff7f5536..1ba752b2dc6d 100644 --- a/arch/arm/configs/loki_defconfig +++ b/arch/arm/configs/loki_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/lpd7a400_defconfig b/arch/arm/configs/lpd7a400_defconfig index 20caaaba4a04..5a48f171204c 100644 --- a/arch/arm/configs/lpd7a400_defconfig +++ b/arch/arm/configs/lpd7a400_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set # CONFIG_IOSCHED_DEADLINE is not set diff --git a/arch/arm/configs/lpd7a404_defconfig b/arch/arm/configs/lpd7a404_defconfig index 1efcce97b4a7..22d0631de009 100644 --- a/arch/arm/configs/lpd7a404_defconfig +++ b/arch/arm/configs/lpd7a404_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=16 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set CONFIG_SLAB=y diff --git a/arch/arm/configs/magician_defconfig b/arch/arm/configs/magician_defconfig index af805e8fd03d..a88e64d4e9a5 100644 --- a/arch/arm/configs/magician_defconfig +++ b/arch/arm/configs/magician_defconfig @@ -4,7 +4,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/mv78xx0_defconfig b/arch/arm/configs/mv78xx0_defconfig index b0d082422d46..7305ebddb510 100644 --- a/arch/arm/configs/mv78xx0_defconfig +++ b/arch/arm/configs/mv78xx0_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y # CONFIG_SLUB_DEBUG is not set CONFIG_PROFILING=y diff --git a/arch/arm/configs/mx1_defconfig b/arch/arm/configs/mx1_defconfig index 2f38d9715437..b39b5ced8a10 100644 --- a/arch/arm/configs/mx1_defconfig +++ b/arch/arm/configs/mx1_defconfig @@ -4,7 +4,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/mx21_defconfig b/arch/arm/configs/mx21_defconfig index 6454e18e2abe..411f88dd4402 100644 --- a/arch/arm/configs/mx21_defconfig +++ b/arch/arm/configs/mx21_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/mx27_defconfig b/arch/arm/configs/mx27_defconfig index 813cfb366c18..9ad4c656c9bd 100644 --- a/arch/arm/configs/mx27_defconfig +++ b/arch/arm/configs/mx27_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y diff --git a/arch/arm/configs/mx3_defconfig b/arch/arm/configs/mx3_defconfig index e648ea3429be..7c4b30b34952 100644 --- a/arch/arm/configs/mx3_defconfig +++ b/arch/arm/configs/mx3_defconfig @@ -4,7 +4,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/mx51_defconfig b/arch/arm/configs/mx51_defconfig index 5c7a87260fab..9cba68cfa51a 100644 --- a/arch/arm/configs/mx51_defconfig +++ b/arch/arm/configs/mx51_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=18 CONFIG_RELAY=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set CONFIG_MODULES=y diff --git a/arch/arm/configs/nhk8815_defconfig b/arch/arm/configs/nhk8815_defconfig index 0e2dc26ebe66..37207d1bf44b 100644 --- a/arch/arm/configs/nhk8815_defconfig +++ b/arch/arm/configs/nhk8815_defconfig @@ -7,7 +7,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig index a350cc6bfe6a..7b63462b349d 100644 --- a/arch/arm/configs/omap1_defconfig +++ b/arch/arm/configs/omap1_defconfig @@ -6,7 +6,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig index ccedde1371c3..ae890caa17a7 100644 --- a/arch/arm/configs/omap2plus_defconfig +++ b/arch/arm/configs/omap2plus_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y diff --git a/arch/arm/configs/orion5x_defconfig b/arch/arm/configs/orion5x_defconfig index 439323b3b0ed..a288d7033950 100644 --- a/arch/arm/configs/orion5x_defconfig +++ b/arch/arm/configs/orion5x_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SLUB_DEBUG is not set CONFIG_PROFILING=y CONFIG_OPROFILE=y diff --git a/arch/arm/configs/pcm027_defconfig b/arch/arm/configs/pcm027_defconfig index 583a0610bd00..2f136c30a989 100644 --- a/arch/arm/configs/pcm027_defconfig +++ b/arch/arm/configs/pcm027_defconfig @@ -7,7 +7,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/pcontrol_g20_defconfig b/arch/arm/configs/pcontrol_g20_defconfig index b42ee62c4d77..c75c9fcede58 100644 --- a/arch/arm/configs/pcontrol_g20_defconfig +++ b/arch/arm/configs/pcontrol_g20_defconfig @@ -10,7 +10,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/arm/configs/pleb_defconfig b/arch/arm/configs/pleb_defconfig index d1efbdc1e6dc..cb08cc561da5 100644 --- a/arch/arm/configs/pleb_defconfig +++ b/arch/arm/configs/pleb_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set # CONFIG_SHMEM is not set CONFIG_MODULES=y diff --git a/arch/arm/configs/pnx4008_defconfig b/arch/arm/configs/pnx4008_defconfig index bd481f04276f..35a31ccacc32 100644 --- a/arch/arm/configs/pnx4008_defconfig +++ b/arch/arm/configs/pnx4008_defconfig @@ -5,7 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_AUDIT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/simpad_defconfig b/arch/arm/configs/simpad_defconfig index af3b12e3b464..d3358155bf8a 100644 --- a/arch/arm/configs/simpad_defconfig +++ b/arch/arm/configs/simpad_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOCALVERSION="oe1" CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig index aebd4bb0ad01..70158273c6dd 100644 --- a/arch/arm/configs/spitz_defconfig +++ b/arch/arm/configs/spitz_defconfig @@ -4,7 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_PROFILING=y CONFIG_OPROFILE=m CONFIG_MODULES=y diff --git a/arch/arm/configs/stmp378x_defconfig b/arch/arm/configs/stmp378x_defconfig index 94a2d904bf94..1079c2b6eb3a 100644 --- a/arch/arm/configs/stmp378x_defconfig +++ b/arch/arm/configs/stmp378x_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/stmp37xx_defconfig b/arch/arm/configs/stmp37xx_defconfig index d8ee58cfa872..564a5cc44085 100644 --- a/arch/arm/configs/stmp37xx_defconfig +++ b/arch/arm/configs/stmp37xx_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/arm/configs/tct_hammer_defconfig b/arch/arm/configs/tct_hammer_defconfig index e89ca19489c2..95c0f0d63db6 100644 --- a/arch/arm/configs/tct_hammer_defconfig +++ b/arch/arm/configs/tct_hammer_defconfig @@ -5,7 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_BUG is not set # CONFIG_ELF_CORE is not set diff --git a/arch/arm/configs/trizeps4_defconfig b/arch/arm/configs/trizeps4_defconfig index 37f48342827c..3162173fa75a 100644 --- a/arch/arm/configs/trizeps4_defconfig +++ b/arch/arm/configs/trizeps4_defconfig @@ -7,7 +7,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/arm/configs/u300_defconfig b/arch/arm/configs/u300_defconfig index c1c252cdca60..4a5a12681be2 100644 --- a/arch/arm/configs/u300_defconfig +++ b/arch/arm/configs/u300_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_AIO is not set # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_MODULES=y diff --git a/arch/arm/configs/viper_defconfig b/arch/arm/configs/viper_defconfig index 9d7bf5e0d0f5..8b0c717378fa 100644 --- a/arch/arm/configs/viper_defconfig +++ b/arch/arm/configs/viper_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=13 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_ELF_CORE is not set # CONFIG_SHMEM is not set CONFIG_SLAB=y diff --git a/arch/arm/configs/xcep_defconfig b/arch/arm/configs/xcep_defconfig index 70d47dbae6db..5b5504143647 100644 --- a/arch/arm/configs/xcep_defconfig +++ b/arch/arm/configs/xcep_defconfig @@ -8,7 +8,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SHMEM is not set # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/avr32/Kconfig b/arch/avr32/Kconfig index 313b13073c54..cd2062fe0f61 100644 --- a/arch/avr32/Kconfig +++ b/arch/avr32/Kconfig @@ -1,8 +1,8 @@ config AVR32 def_bool y - # With EMBEDDED=n, we get lots of stuff automatically selected + # With EXPERT=n, we get lots of stuff automatically selected # that we usually don't need on AVR32. - select EMBEDDED + select EXPERT select HAVE_CLK select HAVE_OPROFILE select HAVE_KPROBES diff --git a/arch/blackfin/configs/BF518F-EZBRD_defconfig b/arch/blackfin/configs/BF518F-EZBRD_defconfig index c0b988ee30df..db8d38a12a9a 100644 --- a/arch/blackfin/configs/BF518F-EZBRD_defconfig +++ b/arch/blackfin/configs/BF518F-EZBRD_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF526-EZBRD_defconfig b/arch/blackfin/configs/BF526-EZBRD_defconfig index 864af5b68874..3e50d7857c27 100644 --- a/arch/blackfin/configs/BF526-EZBRD_defconfig +++ b/arch/blackfin/configs/BF526-EZBRD_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF527-AD7160-EVAL_defconfig b/arch/blackfin/configs/BF527-AD7160-EVAL_defconfig index 7b6a3370dbe2..362f59dd5228 100644 --- a/arch/blackfin/configs/BF527-AD7160-EVAL_defconfig +++ b/arch/blackfin/configs/BF527-AD7160-EVAL_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_ELF_CORE is not set # CONFIG_AIO is not set CONFIG_SLAB=y diff --git a/arch/blackfin/configs/BF527-EZKIT-V2_defconfig b/arch/blackfin/configs/BF527-EZKIT-V2_defconfig index 4faa6b46a352..023ff0df2692 100644 --- a/arch/blackfin/configs/BF527-EZKIT-V2_defconfig +++ b/arch/blackfin/configs/BF527-EZKIT-V2_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF527-EZKIT_defconfig b/arch/blackfin/configs/BF527-EZKIT_defconfig index 9d893eb68243..4e5a121b3c56 100644 --- a/arch/blackfin/configs/BF527-EZKIT_defconfig +++ b/arch/blackfin/configs/BF527-EZKIT_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF527-TLL6527M_defconfig b/arch/blackfin/configs/BF527-TLL6527M_defconfig index 97a2767c80f8..cd0636bb24a0 100644 --- a/arch/blackfin/configs/BF527-TLL6527M_defconfig +++ b/arch/blackfin/configs/BF527-TLL6527M_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF533-EZKIT_defconfig b/arch/blackfin/configs/BF533-EZKIT_defconfig index f84774360c5b..9f8fc84e4ac9 100644 --- a/arch/blackfin/configs/BF533-EZKIT_defconfig +++ b/arch/blackfin/configs/BF533-EZKIT_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF533-STAMP_defconfig b/arch/blackfin/configs/BF533-STAMP_defconfig index 0e7262c04cc2..ccc432b722a0 100644 --- a/arch/blackfin/configs/BF533-STAMP_defconfig +++ b/arch/blackfin/configs/BF533-STAMP_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF537-STAMP_defconfig b/arch/blackfin/configs/BF537-STAMP_defconfig index 4d14a002e7bd..566695472a84 100644 --- a/arch/blackfin/configs/BF537-STAMP_defconfig +++ b/arch/blackfin/configs/BF537-STAMP_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF538-EZKIT_defconfig b/arch/blackfin/configs/BF538-EZKIT_defconfig index fbee9d776f56..ac22124ccb6c 100644 --- a/arch/blackfin/configs/BF538-EZKIT_defconfig +++ b/arch/blackfin/configs/BF538-EZKIT_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF548-EZKIT_defconfig b/arch/blackfin/configs/BF548-EZKIT_defconfig index 05dd11db2f7d..944404b6ff08 100644 --- a/arch/blackfin/configs/BF548-EZKIT_defconfig +++ b/arch/blackfin/configs/BF548-EZKIT_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF561-ACVILON_defconfig b/arch/blackfin/configs/BF561-ACVILON_defconfig index bcb14d1c5664..b7c8451f26ac 100644 --- a/arch/blackfin/configs/BF561-ACVILON_defconfig +++ b/arch/blackfin/configs/BF561-ACVILON_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF561-EZKIT-SMP_defconfig b/arch/blackfin/configs/BF561-EZKIT-SMP_defconfig index 4cf451024fd8..7e67ba31e991 100644 --- a/arch/blackfin/configs/BF561-EZKIT-SMP_defconfig +++ b/arch/blackfin/configs/BF561-EZKIT-SMP_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BF561-EZKIT_defconfig b/arch/blackfin/configs/BF561-EZKIT_defconfig index 843aaa54a9e3..141e5933e1aa 100644 --- a/arch/blackfin/configs/BF561-EZKIT_defconfig +++ b/arch/blackfin/configs/BF561-EZKIT_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/BlackStamp_defconfig b/arch/blackfin/configs/BlackStamp_defconfig index dae7adf3b2a2..97ebe09a7370 100644 --- a/arch/blackfin/configs/BlackStamp_defconfig +++ b/arch/blackfin/configs/BlackStamp_defconfig @@ -6,7 +6,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/CM-BF527_defconfig b/arch/blackfin/configs/CM-BF527_defconfig index f3414244bfed..c2457543e58c 100644 --- a/arch/blackfin/configs/CM-BF527_defconfig +++ b/arch/blackfin/configs/CM-BF527_defconfig @@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/CM-BF533_defconfig b/arch/blackfin/configs/CM-BF533_defconfig index 8c7e08f173d4..baf1c1573e5e 100644 --- a/arch/blackfin/configs/CM-BF533_defconfig +++ b/arch/blackfin/configs/CM-BF533_defconfig @@ -7,7 +7,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/CM-BF537E_defconfig b/arch/blackfin/configs/CM-BF537E_defconfig index bd3cb766d078..707cbf8a2590 100644 --- a/arch/blackfin/configs/CM-BF537E_defconfig +++ b/arch/blackfin/configs/CM-BF537E_defconfig @@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/CM-BF537U_defconfig b/arch/blackfin/configs/CM-BF537U_defconfig index 82224f37c04e..4596935eadac 100644 --- a/arch/blackfin/configs/CM-BF537U_defconfig +++ b/arch/blackfin/configs/CM-BF537U_defconfig @@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/CM-BF548_defconfig b/arch/blackfin/configs/CM-BF548_defconfig index 433598c6e773..df267588efec 100644 --- a/arch/blackfin/configs/CM-BF548_defconfig +++ b/arch/blackfin/configs/CM-BF548_defconfig @@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/CM-BF561_defconfig b/arch/blackfin/configs/CM-BF561_defconfig index ded7d845cb39..6c7b21585a43 100644 --- a/arch/blackfin/configs/CM-BF561_defconfig +++ b/arch/blackfin/configs/CM-BF561_defconfig @@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/DNP5370_defconfig b/arch/blackfin/configs/DNP5370_defconfig index 0ebc7d9aa426..f50313657f3e 100644 --- a/arch/blackfin/configs/DNP5370_defconfig +++ b/arch/blackfin/configs/DNP5370_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLOB=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_IOSCHED_CFQ is not set diff --git a/arch/blackfin/configs/H8606_defconfig b/arch/blackfin/configs/H8606_defconfig index 700fb701c121..7450127b6455 100644 --- a/arch/blackfin/configs/H8606_defconfig +++ b/arch/blackfin/configs/H8606_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/IP0X_defconfig b/arch/blackfin/configs/IP0X_defconfig index b40156d217e3..5e797cf72043 100644 --- a/arch/blackfin/configs/IP0X_defconfig +++ b/arch/blackfin/configs/IP0X_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/PNAV-10_defconfig b/arch/blackfin/configs/PNAV-10_defconfig index be866d95ed76..a566a2fe6b9b 100644 --- a/arch/blackfin/configs/PNAV-10_defconfig +++ b/arch/blackfin/configs/PNAV-10_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/SRV1_defconfig b/arch/blackfin/configs/SRV1_defconfig index b64bdf759b82..853809510ee9 100644 --- a/arch/blackfin/configs/SRV1_defconfig +++ b/arch/blackfin/configs/SRV1_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS_ALL=y # CONFIG_ELF_CORE is not set diff --git a/arch/blackfin/configs/TCM-BF518_defconfig b/arch/blackfin/configs/TCM-BF518_defconfig index 1bccd9a50986..d496ae9a39b0 100644 --- a/arch/blackfin/configs/TCM-BF518_defconfig +++ b/arch/blackfin/configs/TCM-BF518_defconfig @@ -7,7 +7,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_FUTEX is not set diff --git a/arch/blackfin/configs/TCM-BF537_defconfig b/arch/blackfin/configs/TCM-BF537_defconfig index 00ce899e9e5d..65f642167a50 100644 --- a/arch/blackfin/configs/TCM-BF537_defconfig +++ b/arch/blackfin/configs/TCM-BF537_defconfig @@ -8,7 +8,7 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set diff --git a/arch/cris/configs/artpec_3_defconfig b/arch/cris/configs/artpec_3_defconfig index 590f72c9455d..71854d41c5a0 100644 --- a/arch/cris/configs/artpec_3_defconfig +++ b/arch/cris/configs/artpec_3_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/cris/configs/etrax-100lx_v2_defconfig b/arch/cris/configs/etrax-100lx_v2_defconfig index 1b2853e39801..a85aabf92be5 100644 --- a/arch/cris/configs/etrax-100lx_v2_defconfig +++ b/arch/cris/configs/etrax-100lx_v2_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/cris/configs/etraxfs_defconfig b/arch/cris/configs/etraxfs_defconfig index f73d38cc9c66..87c7227fecb2 100644 --- a/arch/cris/configs/etraxfs_defconfig +++ b/arch/cris/configs/etraxfs_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/frv/defconfig b/arch/frv/defconfig index b8ebe9e8a493..b1b792610fdf 100644 --- a/arch/frv/defconfig +++ b/arch/frv/defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_MMU=y CONFIG_FRV_OUTOFLINE_ATOMIC_OPS=y diff --git a/arch/h8300/defconfig b/arch/h8300/defconfig index 342f77765f02..042425a02645 100644 --- a/arch/h8300/defconfig +++ b/arch/h8300/defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_LOCALVERSION_AUTO is not set CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set diff --git a/arch/m32r/configs/m32700ut.smp_defconfig b/arch/m32r/configs/m32700ut.smp_defconfig index 816c3ecaa2aa..a3d727ed6a16 100644 --- a/arch/m32r/configs/m32700ut.smp_defconfig +++ b/arch/m32r/configs/m32700ut.smp_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=15 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/m32700ut.up_defconfig b/arch/m32r/configs/m32700ut.up_defconfig index 84785686640a..b8334163099d 100644 --- a/arch/m32r/configs/m32700ut.up_defconfig +++ b/arch/m32r/configs/m32700ut.up_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/mappi.nommu_defconfig b/arch/m32r/configs/mappi.nommu_defconfig index 354a964d084d..7c90ce2fc42b 100644 --- a/arch/m32r/configs/mappi.nommu_defconfig +++ b/arch/m32r/configs/mappi.nommu_defconfig @@ -3,7 +3,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/mappi.smp_defconfig b/arch/m32r/configs/mappi.smp_defconfig index 9022307bd073..367d07cebcd3 100644 --- a/arch/m32r/configs/mappi.smp_defconfig +++ b/arch/m32r/configs/mappi.smp_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=15 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/mappi.up_defconfig b/arch/m32r/configs/mappi.up_defconfig index 3726068721a5..cb11384386ce 100644 --- a/arch/m32r/configs/mappi.up_defconfig +++ b/arch/m32r/configs/mappi.up_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/mappi2.opsp_defconfig b/arch/m32r/configs/mappi2.opsp_defconfig index 6136fad048e4..3bff779259b4 100644 --- a/arch/m32r/configs/mappi2.opsp_defconfig +++ b/arch/m32r/configs/mappi2.opsp_defconfig @@ -4,7 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/mappi2.vdec2_defconfig b/arch/m32r/configs/mappi2.vdec2_defconfig index dce1fc7d67ed..75246c9c1af8 100644 --- a/arch/m32r/configs/mappi2.vdec2_defconfig +++ b/arch/m32r/configs/mappi2.vdec2_defconfig @@ -4,7 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/mappi3.smp_defconfig b/arch/m32r/configs/mappi3.smp_defconfig index b204e2ecd0f1..27cefd41ac1f 100644 --- a/arch/m32r/configs/mappi3.smp_defconfig +++ b/arch/m32r/configs/mappi3.smp_defconfig @@ -5,7 +5,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=15 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/oaks32r_defconfig b/arch/m32r/configs/oaks32r_defconfig index 5aa4ea9ebb10..5087a510ca4f 100644 --- a/arch/m32r/configs/oaks32r_defconfig +++ b/arch/m32r/configs/oaks32r_defconfig @@ -2,7 +2,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/opsput_defconfig b/arch/m32r/configs/opsput_defconfig index 8494c6a276e8..50c6f525db20 100644 --- a/arch/m32r/configs/opsput_defconfig +++ b/arch/m32r/configs/opsput_defconfig @@ -4,7 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/m32r/configs/usrv_defconfig b/arch/m32r/configs/usrv_defconfig index 1df293bc2ab9..a3cfaaedab60 100644 --- a/arch/m32r/configs/usrv_defconfig +++ b/arch/m32r/configs/usrv_defconfig @@ -5,7 +5,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=15 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/m68knommu/configs/m5208evb_defconfig b/arch/m68knommu/configs/m5208evb_defconfig index 6ac2981a2cdf..2f5655c577af 100644 --- a/arch/m68knommu/configs/m5208evb_defconfig +++ b/arch/m68knommu/configs/m5208evb_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/m68knommu/configs/m5249evb_defconfig b/arch/m68knommu/configs/m5249evb_defconfig index 14934ff8d5c3..16df72bfbd45 100644 --- a/arch/m68knommu/configs/m5249evb_defconfig +++ b/arch/m68knommu/configs/m5249evb_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/m68knommu/configs/m5272c3_defconfig b/arch/m68knommu/configs/m5272c3_defconfig index 5985a3b593d8..4e6ea50c7f33 100644 --- a/arch/m68knommu/configs/m5272c3_defconfig +++ b/arch/m68knommu/configs/m5272c3_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/m68knommu/configs/m5275evb_defconfig b/arch/m68knommu/configs/m5275evb_defconfig index 5a7857efb45d..f3dd74115a34 100644 --- a/arch/m68knommu/configs/m5275evb_defconfig +++ b/arch/m68knommu/configs/m5275evb_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/m68knommu/configs/m5307c3_defconfig b/arch/m68knommu/configs/m5307c3_defconfig index e8102018c8d4..bce0a20c3737 100644 --- a/arch/m68knommu/configs/m5307c3_defconfig +++ b/arch/m68knommu/configs/m5307c3_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/m68knommu/configs/m5407c3_defconfig b/arch/m68knommu/configs/m5407c3_defconfig index 5c124a7ba2a7..618cc32691f2 100644 --- a/arch/m68knommu/configs/m5407c3_defconfig +++ b/arch/m68knommu/configs/m5407c3_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/m68knommu/defconfig b/arch/m68knommu/defconfig index 6ac2981a2cdf..2f5655c577af 100644 --- a/arch/m68knommu/defconfig +++ b/arch/m68knommu/defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_FUTEX is not set diff --git a/arch/microblaze/configs/mmu_defconfig b/arch/microblaze/configs/mmu_defconfig index ab8fbe7ad90b..b3f5eecff2a7 100644 --- a/arch/microblaze/configs/mmu_defconfig +++ b/arch/microblaze/configs/mmu_defconfig @@ -7,7 +7,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="rootfs.cpio" CONFIG_INITRAMFS_COMPRESSION_GZIP=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_HOTPLUG is not set diff --git a/arch/microblaze/configs/nommu_defconfig b/arch/microblaze/configs/nommu_defconfig index ebc143c5368e..0249e4b7e1d3 100644 --- a/arch/microblaze/configs/nommu_defconfig +++ b/arch/microblaze/configs/nommu_defconfig @@ -6,7 +6,7 @@ CONFIG_BSD_PROCESS_ACCT_V3=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_HOTPLUG is not set diff --git a/arch/mips/Kconfig.debug b/arch/mips/Kconfig.debug index f437cd1fafb8..5358f90b4dd2 100644 --- a/arch/mips/Kconfig.debug +++ b/arch/mips/Kconfig.debug @@ -7,7 +7,7 @@ config TRACE_IRQFLAGS_SUPPORT source "lib/Kconfig.debug" config EARLY_PRINTK - bool "Early printk" if EMBEDDED + bool "Early printk" if EXPERT depends on SYS_HAS_EARLY_PRINTK default y help diff --git a/arch/mips/configs/ar7_defconfig b/arch/mips/configs/ar7_defconfig index c78c7e7e41df..6cd5a519ce5c 100644 --- a/arch/mips/configs/ar7_defconfig +++ b/arch/mips/configs/ar7_defconfig @@ -14,7 +14,7 @@ CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_RD_LZMA=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_ELF_CORE is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/bcm47xx_defconfig b/arch/mips/configs/bcm47xx_defconfig index 927d58b2cd03..22fdf2f0cc23 100644 --- a/arch/mips/configs/bcm47xx_defconfig +++ b/arch/mips/configs/bcm47xx_defconfig @@ -21,7 +21,7 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_RD_LZMA=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/bcm63xx_defconfig b/arch/mips/configs/bcm63xx_defconfig index b806a4e32896..919005139f5a 100644 --- a/arch/mips/configs/bcm63xx_defconfig +++ b/arch/mips/configs/bcm63xx_defconfig @@ -10,7 +10,7 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_TINY_RCU=y CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_PCSPKR_PLATFORM is not set # CONFIG_FUTEX is not set # CONFIG_EPOLL is not set diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig index 9749bc8758db..1cdff6b6327d 100644 --- a/arch/mips/configs/bigsur_defconfig +++ b/arch/mips/configs/bigsur_defconfig @@ -26,7 +26,7 @@ CONFIG_PID_NS=y CONFIG_NET_NS=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/capcella_defconfig b/arch/mips/configs/capcella_defconfig index 502a8e9c084b..5135dc0b950a 100644 --- a/arch/mips/configs/capcella_defconfig +++ b/arch/mips/configs/capcella_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/cavium-octeon_defconfig b/arch/mips/configs/cavium-octeon_defconfig index 3567b6f07b37..75165dfa60c1 100644 --- a/arch/mips/configs/cavium-octeon_defconfig +++ b/arch/mips/configs/cavium-octeon_defconfig @@ -15,7 +15,7 @@ CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/cobalt_defconfig b/arch/mips/configs/cobalt_defconfig index 6c4f7e9d3383..5419adb219a8 100644 --- a/arch/mips/configs/cobalt_defconfig +++ b/arch/mips/configs/cobalt_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/mips/configs/db1000_defconfig b/arch/mips/configs/db1000_defconfig index dda158b2c8dc..4044c9e0fb73 100644 --- a/arch/mips/configs/db1000_defconfig +++ b/arch/mips/configs/db1000_defconfig @@ -11,7 +11,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/mips/configs/db1100_defconfig b/arch/mips/configs/db1100_defconfig index 7e4fc76df538..c6b49938ee84 100644 --- a/arch/mips/configs/db1100_defconfig +++ b/arch/mips/configs/db1100_defconfig @@ -11,7 +11,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/db1200_defconfig b/arch/mips/configs/db1200_defconfig index 6fe205fa7b61..1f69249b839a 100644 --- a/arch/mips/configs/db1200_defconfig +++ b/arch/mips/configs/db1200_defconfig @@ -12,7 +12,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/db1500_defconfig b/arch/mips/configs/db1500_defconfig index a741c55448d0..b6e21c7cb6bd 100644 --- a/arch/mips/configs/db1500_defconfig +++ b/arch/mips/configs/db1500_defconfig @@ -10,7 +10,7 @@ CONFIG_LOCALVERSION="-db1500" CONFIG_KERNEL_LZMA=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/mips/configs/db1550_defconfig b/arch/mips/configs/db1550_defconfig index cd32dd8c8008..798a553c9e80 100644 --- a/arch/mips/configs/db1550_defconfig +++ b/arch/mips/configs/db1550_defconfig @@ -11,7 +11,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/decstation_defconfig b/arch/mips/configs/decstation_defconfig index b15bfd1e69c8..87d0340837aa 100644 --- a/arch/mips/configs/decstation_defconfig +++ b/arch/mips/configs/decstation_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/e55_defconfig b/arch/mips/configs/e55_defconfig index 0b60c06a943d..0126e66d60cb 100644 --- a/arch/mips/configs/e55_defconfig +++ b/arch/mips/configs/e55_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/fuloong2e_defconfig b/arch/mips/configs/fuloong2e_defconfig index 63944a14b816..e5b73de08fc5 100644 --- a/arch/mips/configs/fuloong2e_defconfig +++ b/arch/mips/configs/fuloong2e_defconfig @@ -17,7 +17,7 @@ CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_PID_NS=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_PCSPKR_PLATFORM is not set # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/gpr_defconfig b/arch/mips/configs/gpr_defconfig index 53edc134f274..48a40aefaf58 100644 --- a/arch/mips/configs/gpr_defconfig +++ b/arch/mips/configs/gpr_defconfig @@ -11,7 +11,7 @@ CONFIG_BSD_PROCESS_ACCT_V3=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_MODULES=y diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index 36de199f4c27..d1606569b001 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -17,7 +17,7 @@ CONFIG_IPC_NS=y CONFIG_USER_NS=y CONFIG_PID_NS=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set # CONFIG_PCSPKR_PLATFORM is not set # CONFIG_COMPAT_BRK is not set diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig index 4b16c48b0c36..0e36abcd39cc 100644 --- a/arch/mips/configs/ip27_defconfig +++ b/arch/mips/configs/ip27_defconfig @@ -15,7 +15,7 @@ CONFIG_CGROUPS=y CONFIG_CPUSETS=y CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/ip28_defconfig b/arch/mips/configs/ip28_defconfig index 98f2c7736e87..4dbf6269b3f9 100644 --- a/arch/mips/configs/ip28_defconfig +++ b/arch/mips/configs/ip28_defconfig @@ -8,7 +8,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/ip32_defconfig b/arch/mips/configs/ip32_defconfig index 5bea99b26fa8..7bbd52194fc3 100644 --- a/arch/mips/configs/ip32_defconfig +++ b/arch/mips/configs/ip32_defconfig @@ -10,7 +10,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_RELAY=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_OPROFILE=m diff --git a/arch/mips/configs/jazz_defconfig b/arch/mips/configs/jazz_defconfig index 6ae46bcdb20b..92a60aecad5c 100644 --- a/arch/mips/configs/jazz_defconfig +++ b/arch/mips/configs/jazz_defconfig @@ -10,7 +10,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/jmr3927_defconfig b/arch/mips/configs/jmr3927_defconfig index bf24e9309b9c..db5705e18b36 100644 --- a/arch/mips/configs/jmr3927_defconfig +++ b/arch/mips/configs/jmr3927_defconfig @@ -4,7 +4,7 @@ CONFIG_TOSHIBA_JMR3927=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/lasat_defconfig b/arch/mips/configs/lasat_defconfig index 6447261c61d0..d9f3db29ab95 100644 --- a/arch/mips/configs/lasat_defconfig +++ b/arch/mips/configs/lasat_defconfig @@ -8,7 +8,7 @@ CONFIG_HZ_1000=y CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set diff --git a/arch/mips/configs/lemote2f_defconfig b/arch/mips/configs/lemote2f_defconfig index f7033f3a5822..167c1d07b809 100644 --- a/arch/mips/configs/lemote2f_defconfig +++ b/arch/mips/configs/lemote2f_defconfig @@ -21,7 +21,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_RD_BZIP2=y CONFIG_RD_LZMA=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_PROFILING=y CONFIG_OPROFILE=m CONFIG_MODULES=y diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig index 9d03b68aece8..7270f3183bda 100644 --- a/arch/mips/configs/malta_defconfig +++ b/arch/mips/configs/malta_defconfig @@ -15,7 +15,7 @@ CONFIG_UTS_NS=y CONFIG_IPC_NS=y CONFIG_PID_NS=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/markeins_defconfig b/arch/mips/configs/markeins_defconfig index 86bf001babe9..9c9a123016c0 100644 --- a/arch/mips/configs/markeins_defconfig +++ b/arch/mips/configs/markeins_defconfig @@ -9,7 +9,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/mipssim_defconfig b/arch/mips/configs/mipssim_defconfig index 4925f507dc21..b5ad7387bbb0 100644 --- a/arch/mips/configs/mipssim_defconfig +++ b/arch/mips/configs/mipssim_defconfig @@ -7,7 +7,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/mpc30x_defconfig b/arch/mips/configs/mpc30x_defconfig index efb779f8f6fe..c16de9812920 100644 --- a/arch/mips/configs/mpc30x_defconfig +++ b/arch/mips/configs/mpc30x_defconfig @@ -5,7 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/msp71xx_defconfig b/arch/mips/configs/msp71xx_defconfig index ab051458452b..d1142e9cd9a1 100644 --- a/arch/mips/configs/msp71xx_defconfig +++ b/arch/mips/configs/msp71xx_defconfig @@ -8,7 +8,7 @@ CONFIG_LOCALVERSION="-pmc" CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SHMEM is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/mtx1_defconfig b/arch/mips/configs/mtx1_defconfig index 814699754e0d..a97a42c6b2c8 100644 --- a/arch/mips/configs/mtx1_defconfig +++ b/arch/mips/configs/mtx1_defconfig @@ -11,7 +11,7 @@ CONFIG_AUDIT=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_PROFILING=y CONFIG_OPROFILE=m diff --git a/arch/mips/configs/pb1100_defconfig b/arch/mips/configs/pb1100_defconfig index 1597aa1842fa..75eb1b1f316c 100644 --- a/arch/mips/configs/pb1100_defconfig +++ b/arch/mips/configs/pb1100_defconfig @@ -11,7 +11,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/pb1200_defconfig b/arch/mips/configs/pb1200_defconfig index 96f0d43cf08b..dcbe2704e5ed 100644 --- a/arch/mips/configs/pb1200_defconfig +++ b/arch/mips/configs/pb1200_defconfig @@ -12,7 +12,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/pb1500_defconfig b/arch/mips/configs/pb1500_defconfig index b4bfd4823458..fa00487146f8 100644 --- a/arch/mips/configs/pb1500_defconfig +++ b/arch/mips/configs/pb1500_defconfig @@ -11,7 +11,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/pb1550_defconfig b/arch/mips/configs/pb1550_defconfig index 5a660024d22a..e83d6497e8b4 100644 --- a/arch/mips/configs/pb1550_defconfig +++ b/arch/mips/configs/pb1550_defconfig @@ -11,7 +11,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/pnx8335-stb225_defconfig b/arch/mips/configs/pnx8335-stb225_defconfig index 39926a1a96b6..f2925769dfa3 100644 --- a/arch/mips/configs/pnx8335-stb225_defconfig +++ b/arch/mips/configs/pnx8335-stb225_defconfig @@ -11,7 +11,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/pnx8550-jbs_defconfig b/arch/mips/configs/pnx8550-jbs_defconfig index 3376bc8616cc..1d1f2067f3e6 100644 --- a/arch/mips/configs/pnx8550-jbs_defconfig +++ b/arch/mips/configs/pnx8550-jbs_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/pnx8550-stb810_defconfig b/arch/mips/configs/pnx8550-stb810_defconfig index 6514f1bf0afb..15c66a571f99 100644 --- a/arch/mips/configs/pnx8550-stb810_defconfig +++ b/arch/mips/configs/pnx8550-stb810_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/powertv_defconfig b/arch/mips/configs/powertv_defconfig index f1f58e91dd80..3b0b6e8c8533 100644 --- a/arch/mips/configs/powertv_defconfig +++ b/arch/mips/configs/powertv_defconfig @@ -14,7 +14,7 @@ CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_RD_GZIP is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS_ALL=y # CONFIG_PCSPKR_PLATFORM is not set diff --git a/arch/mips/configs/rb532_defconfig b/arch/mips/configs/rb532_defconfig index d6457bc38c71..55902d9cd0f2 100644 --- a/arch/mips/configs/rb532_defconfig +++ b/arch/mips/configs/rb532_defconfig @@ -13,7 +13,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_ELF_CORE is not set # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/mips/configs/rbtx49xx_defconfig b/arch/mips/configs/rbtx49xx_defconfig index 29acfab31516..9cba856277ff 100644 --- a/arch/mips/configs/rbtx49xx_defconfig +++ b/arch/mips/configs/rbtx49xx_defconfig @@ -12,7 +12,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set # CONFIG_PCSPKR_PLATFORM is not set # CONFIG_EPOLL is not set diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index 2b3e47653f60..2c0230e76d20 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -12,7 +12,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/sb1250-swarm_defconfig b/arch/mips/configs/sb1250-swarm_defconfig index 64840d717750..5b0463ef9389 100644 --- a/arch/mips/configs/sb1250-swarm_defconfig +++ b/arch/mips/configs/sb1250-swarm_defconfig @@ -15,7 +15,7 @@ CONFIG_RELAY=y CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/tb0219_defconfig b/arch/mips/configs/tb0219_defconfig index d9be37fc9cb7..30036b4cbeb1 100644 --- a/arch/mips/configs/tb0219_defconfig +++ b/arch/mips/configs/tb0219_defconfig @@ -5,7 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/tb0226_defconfig b/arch/mips/configs/tb0226_defconfig index 3d25dd08907b..81bfa1d4d8e3 100644 --- a/arch/mips/configs/tb0226_defconfig +++ b/arch/mips/configs/tb0226_defconfig @@ -5,7 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/mips/configs/tb0287_defconfig b/arch/mips/configs/tb0287_defconfig index be697c9b23c6..c415c4f0e5c2 100644 --- a/arch/mips/configs/tb0287_defconfig +++ b/arch/mips/configs/tb0287_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_PCSPKR_PLATFORM is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/workpad_defconfig b/arch/mips/configs/workpad_defconfig index 7ec9287254d8..ee4b2be43c44 100644 --- a/arch/mips/configs/workpad_defconfig +++ b/arch/mips/configs/workpad_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mips/configs/wrppmc_defconfig b/arch/mips/configs/wrppmc_defconfig index a231b73b1a40..44a451be359e 100644 --- a/arch/mips/configs/wrppmc_defconfig +++ b/arch/mips/configs/wrppmc_defconfig @@ -7,7 +7,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_EPOLL is not set CONFIG_SLAB=y diff --git a/arch/mips/configs/yosemite_defconfig b/arch/mips/configs/yosemite_defconfig index ab3a3dcec04d..f72d305a3f08 100644 --- a/arch/mips/configs/yosemite_defconfig +++ b/arch/mips/configs/yosemite_defconfig @@ -8,7 +8,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/mn10300/configs/asb2303_defconfig b/arch/mn10300/configs/asb2303_defconfig index 3f749b69ca71..1fd41ec1dfb5 100644 --- a/arch/mn10300/configs/asb2303_defconfig +++ b/arch/mn10300/configs/asb2303_defconfig @@ -4,7 +4,7 @@ CONFIG_BSD_PROCESS_ACCT=y CONFIG_TINY_RCU=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/mn10300/configs/asb2364_defconfig b/arch/mn10300/configs/asb2364_defconfig index 83ce2f27b12a..31d76261a3d5 100644 --- a/arch/mn10300/configs/asb2364_defconfig +++ b/arch/mn10300/configs/asb2364_defconfig @@ -15,7 +15,7 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_RESOURCE_COUNTERS=y CONFIG_RELAY=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_SLAB=y diff --git a/arch/parisc/configs/a500_defconfig b/arch/parisc/configs/a500_defconfig index f9305f30603a..b647b182dacc 100644 --- a/arch/parisc/configs/a500_defconfig +++ b/arch/parisc/configs/a500_defconfig @@ -8,7 +8,7 @@ CONFIG_LOG_BUF_SHIFT=16 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_SLAB=y CONFIG_PROFILING=y diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig index 628d3e022535..311ca367b622 100644 --- a/arch/parisc/configs/c3000_defconfig +++ b/arch/parisc/configs/c3000_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_SLAB=y CONFIG_PROFILING=y diff --git a/arch/powerpc/configs/40x/acadia_defconfig b/arch/powerpc/configs/40x/acadia_defconfig index 97fedceaa30b..4182c772340b 100644 --- a/arch/powerpc/configs/40x/acadia_defconfig +++ b/arch/powerpc/configs/40x/acadia_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/40x/ep405_defconfig b/arch/powerpc/configs/40x/ep405_defconfig index 33b3c24f4edd..2dbb293163f5 100644 --- a/arch/powerpc/configs/40x/ep405_defconfig +++ b/arch/powerpc/configs/40x/ep405_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/40x/hcu4_defconfig b/arch/powerpc/configs/40x/hcu4_defconfig index 4613079a0ab1..ebeb4accad65 100644 --- a/arch/powerpc/configs/40x/hcu4_defconfig +++ b/arch/powerpc/configs/40x/hcu4_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/40x/kilauea_defconfig b/arch/powerpc/configs/40x/kilauea_defconfig index 34b8c1a1e752..532ea9d93a15 100644 --- a/arch/powerpc/configs/40x/kilauea_defconfig +++ b/arch/powerpc/configs/40x/kilauea_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/40x/makalu_defconfig b/arch/powerpc/configs/40x/makalu_defconfig index 651be09136fa..3c142ac1b344 100644 --- a/arch/powerpc/configs/40x/makalu_defconfig +++ b/arch/powerpc/configs/40x/makalu_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/40x/walnut_defconfig b/arch/powerpc/configs/40x/walnut_defconfig index ded455e18339..ff57d4828ffc 100644 --- a/arch/powerpc/configs/40x/walnut_defconfig +++ b/arch/powerpc/configs/40x/walnut_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/44x/arches_defconfig b/arch/powerpc/configs/44x/arches_defconfig index 63746a041d6b..3ed16d5c909d 100644 --- a/arch/powerpc/configs/44x/arches_defconfig +++ b/arch/powerpc/configs/44x/arches_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/bamboo_defconfig b/arch/powerpc/configs/44x/bamboo_defconfig index f5f2a4e3e21b..b1b7d2c5c059 100644 --- a/arch/powerpc/configs/44x/bamboo_defconfig +++ b/arch/powerpc/configs/44x/bamboo_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/bluestone_defconfig b/arch/powerpc/configs/44x/bluestone_defconfig index ac65b48b8ccd..30a0a8e08fdd 100644 --- a/arch/powerpc/configs/44x/bluestone_defconfig +++ b/arch/powerpc/configs/44x/bluestone_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_VM_EVENT_COUNTERS is not set # CONFIG_PCI_QUIRKS is not set # CONFIG_COMPAT_BRK is not set diff --git a/arch/powerpc/configs/44x/canyonlands_defconfig b/arch/powerpc/configs/44x/canyonlands_defconfig index 17e4dd98eed7..a46942aac695 100644 --- a/arch/powerpc/configs/44x/canyonlands_defconfig +++ b/arch/powerpc/configs/44x/canyonlands_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/ebony_defconfig b/arch/powerpc/configs/44x/ebony_defconfig index fedd03fdf5d5..07d77e51f1ba 100644 --- a/arch/powerpc/configs/44x/ebony_defconfig +++ b/arch/powerpc/configs/44x/ebony_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/44x/eiger_defconfig b/arch/powerpc/configs/44x/eiger_defconfig index ebff7011282e..2ce7e9aff09e 100644 --- a/arch/powerpc/configs/44x/eiger_defconfig +++ b/arch/powerpc/configs/44x/eiger_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/icon_defconfig b/arch/powerpc/configs/44x/icon_defconfig index 865e93fb41fd..18730ff9de7c 100644 --- a/arch/powerpc/configs/44x/icon_defconfig +++ b/arch/powerpc/configs/44x/icon_defconfig @@ -6,7 +6,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/iss476-smp_defconfig b/arch/powerpc/configs/44x/iss476-smp_defconfig index 8ece4c774415..92f863ac8443 100644 --- a/arch/powerpc/configs/44x/iss476-smp_defconfig +++ b/arch/powerpc/configs/44x/iss476-smp_defconfig @@ -7,7 +7,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_PROFILING=y diff --git a/arch/powerpc/configs/44x/katmai_defconfig b/arch/powerpc/configs/44x/katmai_defconfig index 4ca9b4873c51..34c09144a699 100644 --- a/arch/powerpc/configs/44x/katmai_defconfig +++ b/arch/powerpc/configs/44x/katmai_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/rainier_defconfig b/arch/powerpc/configs/44x/rainier_defconfig index e3b65d24207e..21c33faf61a2 100644 --- a/arch/powerpc/configs/44x/rainier_defconfig +++ b/arch/powerpc/configs/44x/rainier_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/redwood_defconfig b/arch/powerpc/configs/44x/redwood_defconfig index 64cd0f3421a9..01cc2b1a7f9a 100644 --- a/arch/powerpc/configs/44x/redwood_defconfig +++ b/arch/powerpc/configs/44x/redwood_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/sam440ep_defconfig b/arch/powerpc/configs/44x/sam440ep_defconfig index 01d03367917e..dfcffede16ad 100644 --- a/arch/powerpc/configs/44x/sam440ep_defconfig +++ b/arch/powerpc/configs/44x/sam440ep_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/sequoia_defconfig b/arch/powerpc/configs/44x/sequoia_defconfig index 89b2f9626137..47e399f2892f 100644 --- a/arch/powerpc/configs/44x/sequoia_defconfig +++ b/arch/powerpc/configs/44x/sequoia_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/taishan_defconfig b/arch/powerpc/configs/44x/taishan_defconfig index e3386cf6f5b7..a6a002ed5681 100644 --- a/arch/powerpc/configs/44x/taishan_defconfig +++ b/arch/powerpc/configs/44x/taishan_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/44x/warp_defconfig b/arch/powerpc/configs/44x/warp_defconfig index 9c13b9dffafa..6cf9d6614805 100644 --- a/arch/powerpc/configs/44x/warp_defconfig +++ b/arch/powerpc/configs/44x/warp_defconfig @@ -8,7 +8,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/52xx/cm5200_defconfig b/arch/powerpc/configs/52xx/cm5200_defconfig index f234c4d0b15c..69b57daf402e 100644 --- a/arch/powerpc/configs/52xx/cm5200_defconfig +++ b/arch/powerpc/configs/52xx/cm5200_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/52xx/lite5200b_defconfig b/arch/powerpc/configs/52xx/lite5200b_defconfig index a4a795c80740..f3638ae0a627 100644 --- a/arch/powerpc/configs/52xx/lite5200b_defconfig +++ b/arch/powerpc/configs/52xx/lite5200b_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/52xx/motionpro_defconfig b/arch/powerpc/configs/52xx/motionpro_defconfig index 20d53a1aa7e4..6828eda02bdc 100644 --- a/arch/powerpc/configs/52xx/motionpro_defconfig +++ b/arch/powerpc/configs/52xx/motionpro_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/52xx/pcm030_defconfig b/arch/powerpc/configs/52xx/pcm030_defconfig index 6bd58338bf1a..7f7e4a878602 100644 --- a/arch/powerpc/configs/52xx/pcm030_defconfig +++ b/arch/powerpc/configs/52xx/pcm030_defconfig @@ -8,7 +8,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_SLAB=y diff --git a/arch/powerpc/configs/52xx/tqm5200_defconfig b/arch/powerpc/configs/52xx/tqm5200_defconfig index 3a1f70292d9d..959cd2cfc275 100644 --- a/arch/powerpc/configs/52xx/tqm5200_defconfig +++ b/arch/powerpc/configs/52xx/tqm5200_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_KALLSYMS is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/83xx/asp8347_defconfig b/arch/powerpc/configs/83xx/asp8347_defconfig index eed42d8919e8..d2762d9dcb8e 100644 --- a/arch/powerpc/configs/83xx/asp8347_defconfig +++ b/arch/powerpc/configs/83xx/asp8347_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/kmeter1_defconfig b/arch/powerpc/configs/83xx/kmeter1_defconfig index e43ecb27dfd7..7a7b731c5735 100644 --- a/arch/powerpc/configs/83xx/kmeter1_defconfig +++ b/arch/powerpc/configs/83xx/kmeter1_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_HOTPLUG is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig b/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig index c2e6ab51d335..c683bce4c26e 100644 --- a/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc8313_rdb_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig b/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig index 1d3b20065913..a721cd3d793f 100644 --- a/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc8315_rdb_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc832x_mds_defconfig b/arch/powerpc/configs/83xx/mpc832x_mds_defconfig index 91fe73bd5ad2..a5699a1f7d0a 100644 --- a/arch/powerpc/configs/83xx/mpc832x_mds_defconfig +++ b/arch/powerpc/configs/83xx/mpc832x_mds_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig b/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig index 6d300f205604..b4da1a7e6449 100644 --- a/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc832x_rdb_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc834x_itx_defconfig b/arch/powerpc/configs/83xx/mpc834x_itx_defconfig index b236a67e01fe..291f8221d5a6 100644 --- a/arch/powerpc/configs/83xx/mpc834x_itx_defconfig +++ b/arch/powerpc/configs/83xx/mpc834x_itx_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig b/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig index 001dead3cde9..f8b228aaa03a 100644 --- a/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig +++ b/arch/powerpc/configs/83xx/mpc834x_itxgp_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc834x_mds_defconfig b/arch/powerpc/configs/83xx/mpc834x_mds_defconfig index 9dccefca00c3..99660c062191 100644 --- a/arch/powerpc/configs/83xx/mpc834x_mds_defconfig +++ b/arch/powerpc/configs/83xx/mpc834x_mds_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc836x_mds_defconfig b/arch/powerpc/configs/83xx/mpc836x_mds_defconfig index d4b165d7d294..10b5c4cd0e72 100644 --- a/arch/powerpc/configs/83xx/mpc836x_mds_defconfig +++ b/arch/powerpc/configs/83xx/mpc836x_mds_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc836x_rdk_defconfig b/arch/powerpc/configs/83xx/mpc836x_rdk_defconfig index 89ba67274bda..45925d701d2a 100644 --- a/arch/powerpc/configs/83xx/mpc836x_rdk_defconfig +++ b/arch/powerpc/configs/83xx/mpc836x_rdk_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc837x_mds_defconfig b/arch/powerpc/configs/83xx/mpc837x_mds_defconfig index 2ea6b405046a..f367985be6f7 100644 --- a/arch/powerpc/configs/83xx/mpc837x_mds_defconfig +++ b/arch/powerpc/configs/83xx/mpc837x_mds_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig b/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig index bffe3c775030..414eda381591 100644 --- a/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig +++ b/arch/powerpc/configs/83xx/mpc837x_rdb_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/83xx/sbc834x_defconfig b/arch/powerpc/configs/83xx/sbc834x_defconfig index fa5c9eefc9ad..6d6463fe06fc 100644 --- a/arch/powerpc/configs/83xx/sbc834x_defconfig +++ b/arch/powerpc/configs/83xx/sbc834x_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_SLAB=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/85xx/ksi8560_defconfig b/arch/powerpc/configs/85xx/ksi8560_defconfig index 385b1af37d75..8f7c1061891a 100644 --- a/arch/powerpc/configs/85xx/ksi8560_defconfig +++ b/arch/powerpc/configs/85xx/ksi8560_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set CONFIG_KSI8560=y CONFIG_CPM2=y diff --git a/arch/powerpc/configs/85xx/mpc8540_ads_defconfig b/arch/powerpc/configs/85xx/mpc8540_ads_defconfig index 222b704c1f4b..55e0725500dc 100644 --- a/arch/powerpc/configs/85xx/mpc8540_ads_defconfig +++ b/arch/powerpc/configs/85xx/mpc8540_ads_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set CONFIG_MPC8540_ADS=y CONFIG_NO_HZ=y diff --git a/arch/powerpc/configs/85xx/mpc8560_ads_defconfig b/arch/powerpc/configs/85xx/mpc8560_ads_defconfig index 619702de9477..d724095530a6 100644 --- a/arch/powerpc/configs/85xx/mpc8560_ads_defconfig +++ b/arch/powerpc/configs/85xx/mpc8560_ads_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set CONFIG_MPC8560_ADS=y CONFIG_BINFMT_MISC=y diff --git a/arch/powerpc/configs/85xx/mpc85xx_cds_defconfig b/arch/powerpc/configs/85xx/mpc85xx_cds_defconfig index 6bf56e83f957..4b44beaa21ae 100644 --- a/arch/powerpc/configs/85xx/mpc85xx_cds_defconfig +++ b/arch/powerpc/configs/85xx/mpc85xx_cds_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set CONFIG_MPC85xx_CDS=y CONFIG_NO_HZ=y diff --git a/arch/powerpc/configs/85xx/sbc8548_defconfig b/arch/powerpc/configs/85xx/sbc8548_defconfig index a9a17d055766..5b2b651dfb98 100644 --- a/arch/powerpc/configs/85xx/sbc8548_defconfig +++ b/arch/powerpc/configs/85xx/sbc8548_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y # CONFIG_BLK_DEV_BSG is not set CONFIG_SBC8548=y diff --git a/arch/powerpc/configs/85xx/sbc8560_defconfig b/arch/powerpc/configs/85xx/sbc8560_defconfig index 820e32d8c42b..f7fdb0318e4c 100644 --- a/arch/powerpc/configs/85xx/sbc8560_defconfig +++ b/arch/powerpc/configs/85xx/sbc8560_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y # CONFIG_BLK_DEV_BSG is not set CONFIG_SBC8560=y diff --git a/arch/powerpc/configs/85xx/socrates_defconfig b/arch/powerpc/configs/85xx/socrates_defconfig index b6db3f47af99..77506b5d5a41 100644 --- a/arch/powerpc/configs/85xx/socrates_defconfig +++ b/arch/powerpc/configs/85xx/socrates_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/85xx/stx_gp3_defconfig b/arch/powerpc/configs/85xx/stx_gp3_defconfig index 333a41bd2a68..5d4db154bf59 100644 --- a/arch/powerpc/configs/85xx/stx_gp3_defconfig +++ b/arch/powerpc/configs/85xx/stx_gp3_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/85xx/tqm8540_defconfig b/arch/powerpc/configs/85xx/tqm8540_defconfig index 33db352f847e..ddcb9f37fa1f 100644 --- a/arch/powerpc/configs/85xx/tqm8540_defconfig +++ b/arch/powerpc/configs/85xx/tqm8540_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/85xx/tqm8541_defconfig b/arch/powerpc/configs/85xx/tqm8541_defconfig index f0c20dfbd4d3..981abd6d4b57 100644 --- a/arch/powerpc/configs/85xx/tqm8541_defconfig +++ b/arch/powerpc/configs/85xx/tqm8541_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/85xx/tqm8548_defconfig b/arch/powerpc/configs/85xx/tqm8548_defconfig index a883450dcdfa..37b3d7227cdd 100644 --- a/arch/powerpc/configs/85xx/tqm8548_defconfig +++ b/arch/powerpc/configs/85xx/tqm8548_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_BLK_DEV_BSG is not set diff --git a/arch/powerpc/configs/85xx/tqm8555_defconfig b/arch/powerpc/configs/85xx/tqm8555_defconfig index ff95f90dc171..3593b320c97c 100644 --- a/arch/powerpc/configs/85xx/tqm8555_defconfig +++ b/arch/powerpc/configs/85xx/tqm8555_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/85xx/tqm8560_defconfig b/arch/powerpc/configs/85xx/tqm8560_defconfig index 8d6c90ea4783..de413acc34d6 100644 --- a/arch/powerpc/configs/85xx/tqm8560_defconfig +++ b/arch/powerpc/configs/85xx/tqm8560_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set # CONFIG_EPOLL is not set diff --git a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig index f53efe4a0e0c..5ea3124518fd 100644 --- a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig +++ b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig @@ -11,7 +11,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/86xx/gef_ppc9a_defconfig b/arch/powerpc/configs/86xx/gef_ppc9a_defconfig index 432ebc28d25c..4b2441244eab 100644 --- a/arch/powerpc/configs/86xx/gef_ppc9a_defconfig +++ b/arch/powerpc/configs/86xx/gef_ppc9a_defconfig @@ -11,7 +11,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/86xx/gef_sbc310_defconfig b/arch/powerpc/configs/86xx/gef_sbc310_defconfig index ce5e919d9b55..a360ba44b928 100644 --- a/arch/powerpc/configs/86xx/gef_sbc310_defconfig +++ b/arch/powerpc/configs/86xx/gef_sbc310_defconfig @@ -11,7 +11,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/86xx/gef_sbc610_defconfig b/arch/powerpc/configs/86xx/gef_sbc610_defconfig index 589e71e6dc1c..be2829dd129f 100644 --- a/arch/powerpc/configs/86xx/gef_sbc610_defconfig +++ b/arch/powerpc/configs/86xx/gef_sbc610_defconfig @@ -11,7 +11,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig index 321fb47096d9..036bfb2d18cd 100644 --- a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig +++ b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_ELF_CORE is not set CONFIG_MODULES=y diff --git a/arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig b/arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig index b5e46399374e..0c9c7ed7ec75 100644 --- a/arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig +++ b/arch/powerpc/configs/86xx/mpc8641_hpcn_defconfig @@ -10,7 +10,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/86xx/sbc8641d_defconfig b/arch/powerpc/configs/86xx/sbc8641d_defconfig index 71145c3a64db..0a92ca045641 100644 --- a/arch/powerpc/configs/86xx/sbc8641d_defconfig +++ b/arch/powerpc/configs/86xx/sbc8641d_defconfig @@ -11,7 +11,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/adder875_defconfig b/arch/powerpc/configs/adder875_defconfig index ca84c7fc24d5..69128740c14d 100644 --- a/arch/powerpc/configs/adder875_defconfig +++ b/arch/powerpc/configs/adder875_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set diff --git a/arch/powerpc/configs/e55xx_smp_defconfig b/arch/powerpc/configs/e55xx_smp_defconfig index 94d120ef99cf..06f95492afc7 100644 --- a/arch/powerpc/configs/e55xx_smp_defconfig +++ b/arch/powerpc/configs/e55xx_smp_defconfig @@ -12,7 +12,7 @@ CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/ep8248e_defconfig b/arch/powerpc/configs/ep8248e_defconfig index 2677b08199e7..fceffb3cffbe 100644 --- a/arch/powerpc/configs/ep8248e_defconfig +++ b/arch/powerpc/configs/ep8248e_defconfig @@ -2,7 +2,7 @@ CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_SLAB=y # CONFIG_IOSCHED_CFQ is not set diff --git a/arch/powerpc/configs/ep88xc_defconfig b/arch/powerpc/configs/ep88xc_defconfig index f9a3112e5442..219fd470ed22 100644 --- a/arch/powerpc/configs/ep88xc_defconfig +++ b/arch/powerpc/configs/ep88xc_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set diff --git a/arch/powerpc/configs/gamecube_defconfig b/arch/powerpc/configs/gamecube_defconfig index fcf0a398cd66..e74d3a483705 100644 --- a/arch/powerpc/configs/gamecube_defconfig +++ b/arch/powerpc/configs/gamecube_defconfig @@ -6,7 +6,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_ELF_CORE is not set CONFIG_PERF_COUNTERS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/powerpc/configs/holly_defconfig b/arch/powerpc/configs/holly_defconfig index b9b63a609525..94ebfee188db 100644 --- a/arch/powerpc/configs/holly_defconfig +++ b/arch/powerpc/configs/holly_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_MODULES=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_PPC_CHRP is not set diff --git a/arch/powerpc/configs/mgcoge_defconfig b/arch/powerpc/configs/mgcoge_defconfig index c4ed255af18b..39518e91822f 100644 --- a/arch/powerpc/configs/mgcoge_defconfig +++ b/arch/powerpc/configs/mgcoge_defconfig @@ -3,7 +3,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_SLAB=y # CONFIG_IOSCHED_CFQ is not set diff --git a/arch/powerpc/configs/mgsuvd_defconfig b/arch/powerpc/configs/mgsuvd_defconfig index f276c7cf555b..2a490626015c 100644 --- a/arch/powerpc/configs/mgsuvd_defconfig +++ b/arch/powerpc/configs/mgsuvd_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set # CONFIG_BUG is not set diff --git a/arch/powerpc/configs/mpc7448_hpc2_defconfig b/arch/powerpc/configs/mpc7448_hpc2_defconfig index 3b9470883de5..75f0bbf0f6e8 100644 --- a/arch/powerpc/configs/mpc7448_hpc2_defconfig +++ b/arch/powerpc/configs/mpc7448_hpc2_defconfig @@ -4,7 +4,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_PPC_CHRP is not set # CONFIG_PPC_PMAC is not set diff --git a/arch/powerpc/configs/mpc8272_ads_defconfig b/arch/powerpc/configs/mpc8272_ads_defconfig index c7d68ff1a736..6a22400f73c1 100644 --- a/arch/powerpc/configs/mpc8272_ads_defconfig +++ b/arch/powerpc/configs/mpc8272_ads_defconfig @@ -2,7 +2,7 @@ CONFIG_SYSVIPC=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y # CONFIG_PPC_CHRP is not set # CONFIG_PPC_PMAC is not set diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig index 5b1b10fd9740..5aac9a8bc53b 100644 --- a/arch/powerpc/configs/mpc83xx_defconfig +++ b/arch/powerpc/configs/mpc83xx_defconfig @@ -3,7 +3,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SLAB=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index 3aeb5949cfef..99a19d1e9bf8 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -10,7 +10,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index d62c8016f4bc..c636f23f8c92 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -12,7 +12,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/mpc866_ads_defconfig b/arch/powerpc/configs/mpc866_ads_defconfig index 668215cae890..5c258823e694 100644 --- a/arch/powerpc/configs/mpc866_ads_defconfig +++ b/arch/powerpc/configs/mpc866_ads_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_HOTPLUG is not set # CONFIG_BUG is not set diff --git a/arch/powerpc/configs/mpc86xx_defconfig b/arch/powerpc/configs/mpc86xx_defconfig index 63b90d477889..55b54318fef6 100644 --- a/arch/powerpc/configs/mpc86xx_defconfig +++ b/arch/powerpc/configs/mpc86xx_defconfig @@ -10,7 +10,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/mpc885_ads_defconfig b/arch/powerpc/configs/mpc885_ads_defconfig index f9b83481b00e..9e146cdf63de 100644 --- a/arch/powerpc/configs/mpc885_ads_defconfig +++ b/arch/powerpc/configs/mpc885_ads_defconfig @@ -4,7 +4,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set diff --git a/arch/powerpc/configs/ppc40x_defconfig b/arch/powerpc/configs/ppc40x_defconfig index 93d7425ce6cd..bfd634b5ada7 100644 --- a/arch/powerpc/configs/ppc40x_defconfig +++ b/arch/powerpc/configs/ppc40x_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/ppc44x_defconfig b/arch/powerpc/configs/ppc44x_defconfig index 2fa05f7be4cb..47133202a625 100644 --- a/arch/powerpc/configs/ppc44x_defconfig +++ b/arch/powerpc/configs/ppc44x_defconfig @@ -5,7 +5,7 @@ CONFIG_POSIX_MQUEUE=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_KALLSYMS_EXTRA_PASS=y CONFIG_MODULES=y diff --git a/arch/powerpc/configs/pq2fads_defconfig b/arch/powerpc/configs/pq2fads_defconfig index a4353bef31c5..baad8db21b61 100644 --- a/arch/powerpc/configs/pq2fads_defconfig +++ b/arch/powerpc/configs/pq2fads_defconfig @@ -3,7 +3,7 @@ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y # CONFIG_PPC_CHRP is not set # CONFIG_PPC_PMAC is not set diff --git a/arch/powerpc/configs/ps3_defconfig b/arch/powerpc/configs/ps3_defconfig index 49cffe003657..caba919f65d8 100644 --- a/arch/powerpc/configs/ps3_defconfig +++ b/arch/powerpc/configs/ps3_defconfig @@ -8,7 +8,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_NAMESPACES=y CONFIG_BLK_DEV_INITRD=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_EXTRA_PASS=y # CONFIG_PERF_EVENTS is not set # CONFIG_COMPAT_BRK is not set diff --git a/arch/powerpc/configs/storcenter_defconfig b/arch/powerpc/configs/storcenter_defconfig index 4f0c10a62b9d..ebb2a66c99d3 100644 --- a/arch/powerpc/configs/storcenter_defconfig +++ b/arch/powerpc/configs/storcenter_defconfig @@ -1,7 +1,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/powerpc/configs/tqm8xx_defconfig b/arch/powerpc/configs/tqm8xx_defconfig index d0a5b6763880..8616fde0896f 100644 --- a/arch/powerpc/configs/tqm8xx_defconfig +++ b/arch/powerpc/configs/tqm8xx_defconfig @@ -5,7 +5,7 @@ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_SYSCTL_SYSCALL is not set # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set diff --git a/arch/powerpc/configs/wii_defconfig b/arch/powerpc/configs/wii_defconfig index bb8ba75b7c68..175295fbf4f3 100644 --- a/arch/powerpc/configs/wii_defconfig +++ b/arch/powerpc/configs/wii_defconfig @@ -7,7 +7,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_ELF_CORE is not set CONFIG_PERF_COUNTERS=y # CONFIG_VM_EVENT_COUNTERS is not set diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig index 47a20cfb4486..e5bc9f75d474 100644 --- a/arch/powerpc/platforms/iseries/Kconfig +++ b/arch/powerpc/platforms/iseries/Kconfig @@ -2,7 +2,7 @@ config PPC_ISERIES bool "IBM Legacy iSeries" depends on PPC64 && PPC_BOOK3S select PPC_INDIRECT_IO - select PPC_PCI_CHOICE if EMBEDDED + select PPC_PCI_CHOICE if EXPERT menu "iSeries device drivers" depends on PPC_ISERIES diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 5d1b743dbe7e..5b3da4b4ea79 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -10,7 +10,7 @@ config PPC_PSERIES select RTAS_ERROR_LOGGING select PPC_UDBG_16550 select PPC_NATIVE - select PPC_PCI_CHOICE if EMBEDDED + select PPC_PCI_CHOICE if EXPERT default y config PPC_SPLPAR @@ -24,9 +24,9 @@ config PPC_SPLPAR two or more partitions. config EEH - bool "PCI Extended Error Handling (EEH)" if EMBEDDED + bool "PCI Extended Error Handling (EEH)" if EXPERT depends on PPC_PSERIES && PCI - default y if !EMBEDDED + default y if !EXPERT config PSERIES_MSI bool diff --git a/arch/score/configs/spct6600_defconfig b/arch/score/configs/spct6600_defconfig index 9883c50e4636..df1edbf507a2 100644 --- a/arch/score/configs/spct6600_defconfig +++ b/arch/score/configs/spct6600_defconfig @@ -9,7 +9,7 @@ CONFIG_LOG_BUF_SHIFT=12 CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_KALLSYMS is not set # CONFIG_HOTPLUG is not set CONFIG_SLAB=y diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index fff252209f63..ae555569823b 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -1,6 +1,6 @@ config SUPERH def_bool y - select EMBEDDED + select EXPERT select CLKDEV_LOOKUP select HAVE_IDE if HAS_IOPORT select HAVE_MEMBLOCK diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index e11b5fcb70eb..4e8b82bca9e5 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -220,7 +220,7 @@ config FORCE_MAX_ZONEORDER choice depends on !TILEGX - prompt "Memory split" if EMBEDDED + prompt "Memory split" if EXPERT default VMSPLIT_3G ---help--- Select the desired split between kernel and user memory. diff --git a/arch/tile/Kconfig.debug b/arch/tile/Kconfig.debug index a81f0fbf7e60..9bc161a02c71 100644 --- a/arch/tile/Kconfig.debug +++ b/arch/tile/Kconfig.debug @@ -3,7 +3,7 @@ menu "Kernel hacking" source "lib/Kconfig.debug" config EARLY_PRINTK - bool "Early printk" if EMBEDDED && DEBUG_KERNEL + bool "Early printk" if EXPERT && DEBUG_KERNEL default y help Write kernel log output directly via the hypervisor console. diff --git a/arch/tile/configs/tile_defconfig b/arch/tile/configs/tile_defconfig index 919c54afd981..0fe54445fda5 100644 --- a/arch/tile/configs/tile_defconfig +++ b/arch/tile/configs/tile_defconfig @@ -3,7 +3,7 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="usr/contents.txt" -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y # CONFIG_COMPAT_BRK is not set CONFIG_PROFILING=y CONFIG_MODULES=y diff --git a/arch/um/defconfig b/arch/um/defconfig index 564f3de65b4a..9f7634f08cf3 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig @@ -133,7 +133,7 @@ CONFIG_SYSFS_DEPRECATED=y # CONFIG_BLK_DEV_INITRD is not set CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y -# CONFIG_EMBEDDED is not set +# CONFIG_EXPERT is not set CONFIG_UID16=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 3ed5ad92b029..d5ed94d30aad 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -627,11 +627,11 @@ config APB_TIMER as it is off-chip. APB timers are always running regardless of CPU C states, they are used as per CPU clockevent device when possible. -# Mark as embedded because too many people got it wrong. +# Mark as expert because too many people got it wrong. # The code disables itself when not needed. config DMI default y - bool "Enable DMI scanning" if EMBEDDED + bool "Enable DMI scanning" if EXPERT ---help--- Enabled scanning of DMI to identify machine quirks. Say Y here unless you have verified that your setup is not @@ -639,7 +639,7 @@ config DMI BIOS code. config GART_IOMMU - bool "GART IOMMU support" if EMBEDDED + bool "GART IOMMU support" if EXPERT default y select SWIOTLB depends on X86_64 && PCI && AMD_NB @@ -889,7 +889,7 @@ config X86_THERMAL_VECTOR depends on X86_MCE_INTEL config VM86 - bool "Enable VM86 support" if EMBEDDED + bool "Enable VM86 support" if EXPERT default y depends on X86_32 ---help--- @@ -1073,7 +1073,7 @@ endchoice choice depends on EXPERIMENTAL - prompt "Memory split" if EMBEDDED + prompt "Memory split" if EXPERT default VMSPLIT_3G depends on X86_32 ---help--- @@ -1135,7 +1135,7 @@ config ARCH_DMA_ADDR_T_64BIT def_bool X86_64 || HIGHMEM64G config DIRECT_GBPAGES - bool "Enable 1GB pages for kernel pagetables" if EMBEDDED + bool "Enable 1GB pages for kernel pagetables" if EXPERT default y depends on X86_64 ---help--- @@ -1369,7 +1369,7 @@ config MATH_EMULATION config MTRR def_bool y - prompt "MTRR (Memory Type Range Register) support" if EMBEDDED + prompt "MTRR (Memory Type Range Register) support" if EXPERT ---help--- On Intel P6 family processors (Pentium Pro, Pentium II and later) the Memory Type Range Registers (MTRRs) may be used to control @@ -1435,7 +1435,7 @@ config MTRR_SANITIZER_SPARE_REG_NR_DEFAULT config X86_PAT def_bool y - prompt "x86 PAT support" if EMBEDDED + prompt "x86 PAT support" if EXPERT depends on MTRR ---help--- Use PAT attributes to setup page level cache control. @@ -1539,7 +1539,7 @@ config KEXEC_JUMP code in physical address mode via KEXEC config PHYSICAL_START - hex "Physical address where the kernel is loaded" if (EMBEDDED || CRASH_DUMP) + hex "Physical address where the kernel is loaded" if (EXPERT || CRASH_DUMP) default "0x1000000" ---help--- This gives the physical address where the kernel is loaded. @@ -1934,7 +1934,7 @@ config PCI_MMCONFIG depends on X86_64 && PCI && ACPI config PCI_CNB20LE_QUIRK - bool "Read CNB20LE Host Bridge Windows" if EMBEDDED + bool "Read CNB20LE Host Bridge Windows" if EXPERT default n depends on PCI && EXPERIMENTAL help diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index 15588a0ef466..283c5a6a03a6 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -424,7 +424,7 @@ config X86_DEBUGCTLMSR depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386) && !UML menuconfig PROCESSOR_SELECT - bool "Supported processor vendors" if EMBEDDED + bool "Supported processor vendors" if EXPERT ---help--- This lets you choose what x86 vendor support code your kernel will include. diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 45143bbcfe5e..615e18810f48 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -31,7 +31,7 @@ config X86_VERBOSE_BOOTUP see errors. Disable this if you want silent bootup. config EARLY_PRINTK - bool "Early printk" if EMBEDDED + bool "Early printk" if EXPERT default y ---help--- Write kernel log output directly into the VGA buffer or to a serial @@ -138,7 +138,7 @@ config DEBUG_NX_TEST config DOUBLEFAULT default y - bool "Enable doublefault exception handler" if EMBEDDED + bool "Enable doublefault exception handler" if EXPERT depends on X86_32 ---help--- This option allows trapping of rare doublefault exceptions that diff --git a/arch/xtensa/configs/common_defconfig b/arch/xtensa/configs/common_defconfig index 1d230ee081b4..b90038e40dd3 100644 --- a/arch/xtensa/configs/common_defconfig +++ b/arch/xtensa/configs/common_defconfig @@ -32,7 +32,7 @@ CONFIG_LOG_BUF_SHIFT=14 # CONFIG_HOTPLUG is not set CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set -# CONFIG_EMBEDDED is not set +# CONFIG_EXPERT is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set diff --git a/arch/xtensa/configs/iss_defconfig b/arch/xtensa/configs/iss_defconfig index 7368164843b9..0234cd198c54 100644 --- a/arch/xtensa/configs/iss_defconfig +++ b/arch/xtensa/configs/iss_defconfig @@ -55,7 +55,7 @@ CONFIG_LOG_BUF_SHIFT=14 # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_ANON_INODES=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set diff --git a/arch/xtensa/configs/s6105_defconfig b/arch/xtensa/configs/s6105_defconfig index bb84fbc9921f..095cd8084164 100644 --- a/arch/xtensa/configs/s6105_defconfig +++ b/arch/xtensa/configs/s6105_defconfig @@ -55,7 +55,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_INITRAMFS_SOURCE="" CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y -CONFIG_EMBEDDED=y +CONFIG_EXPERT=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set diff --git a/block/Kconfig b/block/Kconfig index 6c9213ef15a1..60be1e0455da 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -2,7 +2,7 @@ # Block layer core configuration # menuconfig BLOCK - bool "Enable the block layer" if EMBEDDED + bool "Enable the block layer" if EXPERT default y help Provide block layer support for the kernel. diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 10c7ad59c0e1..2aa042a5da6d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -318,7 +318,7 @@ config ACPI_PCI_SLOT the module will be called pci_slot. config X86_PM_TIMER - bool "Power Management Timer Support" if EMBEDDED + bool "Power Management Timer Support" if EXPERT depends on X86 default y help diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index c6b298d4c136..c2328aed0836 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -783,7 +783,7 @@ config PATA_PCMCIA config PATA_PLATFORM tristate "Generic platform device PATA support" - depends on EMBEDDED || PPC || HAVE_PATA_PLATFORM + depends on EXPERT || PPC || HAVE_PATA_PLATFORM help This option enables support for generic directly connected ATA devices commonly found on embedded systems. diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index fd96345bc35c..d57e8d0fb823 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -70,7 +70,7 @@ config PREVENT_FIRMWARE_BUILD If unsure say Y here. config FW_LOADER - tristate "Userspace firmware loading support" if EMBEDDED + tristate "Userspace firmware loading support" if EXPERT default y ---help--- This option is provided for the case where no in-kernel-tree modules diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 0f175a866ef0..4237602fd4cd 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -5,7 +5,7 @@ menu "Character devices" config VT - bool "Virtual terminal" if EMBEDDED + bool "Virtual terminal" if EXPERT depends on !S390 select INPUT default y @@ -39,13 +39,13 @@ config VT config CONSOLE_TRANSLATIONS depends on VT default y - bool "Enable character translations in console" if EMBEDDED + bool "Enable character translations in console" if EXPERT ---help--- This enables support for font mapping and Unicode translation on virtual consoles. config VT_CONSOLE - bool "Support for console on virtual terminal" if EMBEDDED + bool "Support for console on virtual terminal" if EXPERT depends on VT default y ---help--- @@ -429,7 +429,7 @@ config SGI_MBCS source "drivers/serial/Kconfig" config UNIX98_PTYS - bool "Unix98 PTY support" if EMBEDDED + bool "Unix98 PTY support" if EXPERT default y ---help--- A pseudo terminal (PTY) is a software device consisting of two @@ -495,7 +495,7 @@ config LEGACY_PTY_COUNT config TTY_PRINTK bool "TTY driver to output user messages via printk" - depends on EMBEDDED + depends on EXPERT default n ---help--- If you say Y here, the support for writing user messages (i.e. diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index a8c8d9c19d74..ca8ee8093d6c 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -71,7 +71,7 @@ config CPU_FREQ_DEFAULT_GOV_PERFORMANCE config CPU_FREQ_DEFAULT_GOV_POWERSAVE bool "powersave" - depends on EMBEDDED + depends on EXPERT select CPU_FREQ_GOV_POWERSAVE help Use the CPUFreq governor 'powersave' as default. This sets diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index e8b6a13515bd..e710424b59ea 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -27,7 +27,7 @@ config EDD_OFF using the kernel parameter 'edd={on|skipmbr|off}'. config FIRMWARE_MEMMAP - bool "Add firmware-provided memory map to sysfs" if EMBEDDED + bool "Add firmware-provided memory map to sysfs" if EXPERT default X86 help Add the firmware-provided (unmodified) memory map to /sys/firmware/memmap. diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 64828a7db77b..bea966f8ac84 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -23,7 +23,7 @@ config DRM_KMS_HELPER tristate depends on DRM select FB - select FRAMEBUFFER_CONSOLE if !EMBEDDED + select FRAMEBUFFER_CONSOLE if !EXPERT help FB and CRTC helpers for KMS drivers. diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 5c4f9b9ecdc0..6977a1ce9d98 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1533,11 +1533,11 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_hotplug_event); -/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EMBEDDED) +/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) * but the module doesn't depend on any fb console symbols. At least * attempt to load fbcon to avoid leaving the system without a usable console. */ -#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EMBEDDED) +#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT) static int __init drm_fb_helper_modinit(void) { const char *name = "fbcon"; diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 21d6c29c2d21..de70959b9ed5 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -8,7 +8,7 @@ config DRM_NOUVEAU select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB - select FRAMEBUFFER_CONSOLE if !EMBEDDED + select FRAMEBUFFER_CONSOLE if !EXPERT select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT help diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig index 8d0e31a22027..96c83a9a76bb 100644 --- a/drivers/gpu/vga/Kconfig +++ b/drivers/gpu/vga/Kconfig @@ -1,5 +1,5 @@ config VGA_ARB - bool "VGA Arbitration" if EMBEDDED + bool "VGA Arbitration" if EXPERT default y depends on PCI help diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24cca2f69dfc..2560f01c1a63 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -62,9 +62,9 @@ config HID_3M_PCT Support for 3M PCT touch screens. config HID_A4TECH - tristate "A4 tech mice" if EMBEDDED + tristate "A4 tech mice" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. @@ -77,9 +77,9 @@ config HID_ACRUX_FF game controllers. config HID_APPLE - tristate "Apple {i,Power,Mac}Books" if EMBEDDED + tristate "Apple {i,Power,Mac}Books" if EXPERT depends on (USB_HID || BT_HIDP) - default !EMBEDDED + default !EXPERT ---help--- Support for some Apple devices which less or more break HID specification. @@ -88,9 +88,9 @@ config HID_APPLE MacBooks, MacBook Pros and Apple Aluminum. config HID_BELKIN - tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED + tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Belkin Flip KVM and Wireless keyboard. @@ -101,16 +101,16 @@ config HID_CANDO Support for Cando dual touch panel. config HID_CHERRY - tristate "Cherry Cymotion keyboard" if EMBEDDED + tristate "Cherry Cymotion keyboard" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Cherry Cymotion keyboard. config HID_CHICONY - tristate "Chicony Tactical pad" if EMBEDDED + tristate "Chicony Tactical pad" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Chicony Tactical pad. @@ -130,9 +130,9 @@ config HID_PRODIKEYS and some additional multimedia keys. config HID_CYPRESS - tristate "Cypress mouse and barcode readers" if EMBEDDED + tristate "Cypress mouse and barcode readers" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for cypress mouse and barcode readers. @@ -174,16 +174,16 @@ config HID_ELECOM Support for the ELECOM BM084 (bluetooth mouse). config HID_EZKEY - tristate "Ezkey BTC 8193 keyboard" if EMBEDDED + tristate "Ezkey BTC 8193 keyboard" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Ezkey BTC 8193 keyboard. config HID_KYE - tristate "Kye/Genius Ergo Mouse" if EMBEDDED + tristate "Kye/Genius Ergo Mouse" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Kye/Genius Ergo Mouse. @@ -212,16 +212,16 @@ config HID_TWINHAN Support for Twinhan IR remote control. config HID_KENSINGTON - tristate "Kensington Slimblade Trackball" if EMBEDDED + tristate "Kensington Slimblade Trackball" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Kensington Slimblade Trackball. config HID_LOGITECH - tristate "Logitech devices" if EMBEDDED + tristate "Logitech devices" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Logitech devices that are not fully compliant with HID standard. @@ -276,9 +276,9 @@ config HID_MAGICMOUSE Apple Wireless "Magic" Mouse. config HID_MICROSOFT - tristate "Microsoft non-fully HID-compliant devices" if EMBEDDED + tristate "Microsoft non-fully HID-compliant devices" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Microsoft devices that are not fully compliant with HID standard. @@ -289,9 +289,9 @@ config HID_MOSART Support for MosArt dual-touch panels. config HID_MONTEREY - tristate "Monterey Genius KB29E keyboard" if EMBEDDED + tristate "Monterey Genius KB29E keyboard" if EXPERT depends on USB_HID - default !EMBEDDED + default !EXPERT ---help--- Support for Monterey Genius KB29E. @@ -365,8 +365,8 @@ config HID_PICOLCD - IR config HID_PICOLCD_FB - bool "Framebuffer support" if EMBEDDED - default !EMBEDDED + bool "Framebuffer support" if EXPERT + default !EXPERT depends on HID_PICOLCD depends on HID_PICOLCD=FB || FB=y select FB_DEFERRED_IO @@ -379,8 +379,8 @@ config HID_PICOLCD_FB frambuffer device. config HID_PICOLCD_BACKLIGHT - bool "Backlight control" if EMBEDDED - default !EMBEDDED + bool "Backlight control" if EXPERT + default !EXPERT depends on HID_PICOLCD depends on HID_PICOLCD=BACKLIGHT_CLASS_DEVICE || BACKLIGHT_CLASS_DEVICE=y ---help--- @@ -388,16 +388,16 @@ config HID_PICOLCD_BACKLIGHT class. config HID_PICOLCD_LCD - bool "Contrast control" if EMBEDDED - default !EMBEDDED + bool "Contrast control" if EXPERT + default !EXPERT depends on HID_PICOLCD depends on HID_PICOLCD=LCD_CLASS_DEVICE || LCD_CLASS_DEVICE=y ---help--- Provide access to PicoLCD's LCD contrast via lcd class. config HID_PICOLCD_LEDS - bool "GPO via leds class" if EMBEDDED - default !EMBEDDED + bool "GPO via leds class" if EXPERT + default !EXPERT depends on HID_PICOLCD depends on HID_PICOLCD=LEDS_CLASS || LEDS_CLASS=y ---help--- diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 4edb3bef94a6..0f20fd17cf06 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -45,7 +45,7 @@ config USB_HIDDEV If unsure, say Y. menu "USB HID Boot Protocol drivers" - depends on USB!=n && USB_HID!=y && EMBEDDED + depends on USB!=n && USB_HID!=y && EXPERT config USB_KBD tristate "USB HIDBP Keyboard (simple Boot) support" diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 98ccfeb3f5aa..9827c5e686cb 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -134,7 +134,7 @@ config BLK_DEV_IDECD module will be called ide-cd. config BLK_DEV_IDECD_VERBOSE_ERRORS - bool "Verbose error logging for IDE/ATAPI CDROM driver" if EMBEDDED + bool "Verbose error logging for IDE/ATAPI CDROM driver" if EXPERT depends on BLK_DEV_IDECD default y help diff --git a/drivers/infiniband/hw/mthca/Kconfig b/drivers/infiniband/hw/mthca/Kconfig index 03efc074967e..da314c3fec23 100644 --- a/drivers/infiniband/hw/mthca/Kconfig +++ b/drivers/infiniband/hw/mthca/Kconfig @@ -7,7 +7,7 @@ config INFINIBAND_MTHCA ("Tavor") and the MT25208 PCI Express HCA ("Arbel"). config INFINIBAND_MTHCA_DEBUG - bool "Verbose debugging output" if EMBEDDED + bool "Verbose debugging output" if EXPERT depends on INFINIBAND_MTHCA default y ---help--- diff --git a/drivers/infiniband/ulp/ipoib/Kconfig b/drivers/infiniband/ulp/ipoib/Kconfig index 55855eeabae7..cda8eac55fff 100644 --- a/drivers/infiniband/ulp/ipoib/Kconfig +++ b/drivers/infiniband/ulp/ipoib/Kconfig @@ -24,7 +24,7 @@ config INFINIBAND_IPOIB_CM unless you limit mtu for these destinations to 2044. config INFINIBAND_IPOIB_DEBUG - bool "IP-over-InfiniBand debugging" if EMBEDDED + bool "IP-over-InfiniBand debugging" if EXPERT depends on INFINIBAND_IPOIB default y ---help--- diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 07c2cd43109c..1903c0f5b925 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -6,7 +6,7 @@ menu "Input device support" depends on !S390 config INPUT - tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED + tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT default y help Say Y here if you have any input device (mouse, keyboard, tablet, @@ -67,7 +67,7 @@ config INPUT_SPARSEKMAP comment "Userland interfaces" config INPUT_MOUSEDEV - tristate "Mouse interface" if EMBEDDED + tristate "Mouse interface" if EXPERT default y help Say Y here if you want your mouse to be accessible as char devices @@ -150,7 +150,7 @@ config INPUT_EVBUG module will be called evbug. config INPUT_APMPOWER - tristate "Input Power Event -> APM Bridge" if EMBEDDED + tristate "Input Power Event -> APM Bridge" if EXPERT depends on INPUT && APM_EMULATION help Say Y here if you want suspend key events to trigger a user diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 7b3c0b8fa432..417507348bab 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # menuconfig INPUT_KEYBOARD - bool "Keyboards" if EMBEDDED || !X86 + bool "Keyboards" if EXPERT || !X86 default y help Say Y here, and a list of supported keyboards will be displayed. @@ -57,7 +57,7 @@ config KEYBOARD_ATARI module will be called atakbd. config KEYBOARD_ATKBD - tristate "AT keyboard" if EMBEDDED || !X86 + tristate "AT keyboard" if EXPERT || !X86 default y select SERIO select SERIO_LIBPS2 diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index bf5fd7f6a313..9c1e6ee83531 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -39,7 +39,7 @@ config MOUSE_PS2 module will be called psmouse. config MOUSE_PS2_ALPS - bool "ALPS PS/2 mouse protocol extension" if EMBEDDED + bool "ALPS PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -49,7 +49,7 @@ config MOUSE_PS2_ALPS If unsure, say Y. config MOUSE_PS2_LOGIPS2PP - bool "Logitech PS/2++ mouse protocol extension" if EMBEDDED + bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -59,7 +59,7 @@ config MOUSE_PS2_LOGIPS2PP If unsure, say Y. config MOUSE_PS2_SYNAPTICS - bool "Synaptics PS/2 mouse protocol extension" if EMBEDDED + bool "Synaptics PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help @@ -69,7 +69,7 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. config MOUSE_PS2_LIFEBOOK - bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EMBEDDED + bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 && X86 && DMI help @@ -79,7 +79,7 @@ config MOUSE_PS2_LIFEBOOK If unsure, say Y. config MOUSE_PS2_TRACKPOINT - bool "IBM Trackpoint PS/2 mouse protocol extension" if EMBEDDED + bool "IBM Trackpoint PS/2 mouse protocol extension" if EXPERT default y depends on MOUSE_PS2 help diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 307eef77a172..55f2c2293ec6 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -2,7 +2,7 @@ # Input core configuration # config SERIO - tristate "Serial I/O support" if EMBEDDED || !X86 + tristate "Serial I/O support" if EXPERT || !X86 default y help Say Yes here if you have any input device that uses serial I/O to @@ -19,7 +19,7 @@ config SERIO if SERIO config SERIO_I8042 - tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86 + tristate "i8042 PC Keyboard controller" if EXPERT || !X86 default y depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \ (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN @@ -168,7 +168,7 @@ config SERIO_MACEPS2 module will be called maceps2. config SERIO_LIBPS2 - tristate "PS/2 driver library" if EMBEDDED + tristate "PS/2 driver library" if EXPERT depends on SERIO_I8042 || SERIO_I8042=n help Say Y here if you are using a driver for device connected diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 0c9f4b158ff0..61834ae282e1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -540,62 +540,62 @@ config TOUCHSCREEN_MC13783 config TOUCHSCREEN_USB_EGALAX default y - bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED + bool "eGalax, eTurboTouch CT-410/510/700 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_PANJIT default y - bool "PanJit device support" if EMBEDDED + bool "PanJit device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_3M default y - bool "3M/Microtouch EX II series device support" if EMBEDDED + bool "3M/Microtouch EX II series device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ITM default y - bool "ITM device support" if EMBEDDED + bool "ITM device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ETURBO default y - bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED + bool "eTurboTouch (non-eGalax compatible) device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GUNZE default y - bool "Gunze AHL61 device support" if EMBEDDED + bool "Gunze AHL61 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_DMC_TSC10 default y - bool "DMC TSC-10/25 device support" if EMBEDDED + bool "DMC TSC-10/25 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_IRTOUCH default y - bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED + bool "IRTOUCHSYSTEMS/UNITOP device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_IDEALTEK default y - bool "IdealTEK URTC1000 device support" if EMBEDDED + bool "IdealTEK URTC1000 device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GENERAL_TOUCH default y - bool "GeneralTouch Touchscreen device support" if EMBEDDED + bool "GeneralTouch Touchscreen device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_GOTOP default y - bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED + bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_JASTEC default y - bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED + bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_E2I @@ -605,17 +605,17 @@ config TOUCHSCREEN_USB_E2I config TOUCHSCREEN_USB_ZYTRONIC default y - bool "Zytronic controller" if EMBEDDED + bool "Zytronic controller" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_ETT_TC45USB default y - bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EMBEDDED + bool "ET&T USB series TC4UM/TC5UH touchscreen controller support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_NEXIO default y - bool "NEXIO/iNexio device support" if EMBEDDED + bool "NEXIO/iNexio device support" if EXPERT depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_TOUCHIT213 diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 78b089526e02..6fc79f15dcbc 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -34,7 +34,7 @@ config MEDIA_TUNER config MEDIA_TUNER_CUSTOMISE bool "Customize analog and hybrid tuner modules to build" depends on MEDIA_TUNER - default y if EMBEDDED + default y if EXPERT help This allows the user to deselect tuner drivers unnecessary for their hardware from the build. Use this option with care diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index ef3e43a03199..b8519ba511e5 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -1,7 +1,7 @@ config DVB_FE_CUSTOMISE bool "Customise the frontend modules to build" depends on DVB_CORE - default y if EMBEDDED + default y if EXPERT help This allows the user to select/deselect frontend drivers for their hardware from the build. diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index eb875af05e79..34e7abadceaa 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -78,7 +78,7 @@ config VIDEO_FIXED_MINOR_RANGES config VIDEO_HELPER_CHIPS_AUTO bool "Autoselect pertinent encoders/decoders and other helper chips" - default y if !EMBEDDED + default y if !EXPERT ---help--- Most video cards may require additional modules to encode or decode audio/video standards. This option will autoselect diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 16fe4f9b719b..03823327db25 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2864,7 +2864,7 @@ config MLX4_CORE default n config MLX4_DEBUG - bool "Verbose debugging output" if (MLX4_CORE && EMBEDDED) + bool "Verbose debugging output" if (MLX4_CORE && EXPERT) depends on MLX4_CORE default y ---help--- diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index dda70981b7a6..dc29348264c6 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -31,7 +31,7 @@ source "drivers/pci/pcie/aer/Kconfig" # PCI Express ASPM # config PCIEASPM - bool "PCI Express ASPM control" if EMBEDDED + bool "PCI Express ASPM control" if EXPERT depends on PCI && PCIEPORTBUS default y help diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index de886f3dfd39..6e318ce41136 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -69,7 +69,7 @@ comment "PC-card bridges" config YENTA tristate "CardBus yenta-compatible bridge support" depends on PCI - select CARDBUS if !EMBEDDED + select CARDBUS if !EXPERT select PCCARD_NONSTATIC if PCMCIA != n ---help--- This option enables support for CardBus host bridges. Virtually @@ -84,27 +84,27 @@ config YENTA config YENTA_O2 default y - bool "Special initialization for O2Micro bridges" if EMBEDDED + bool "Special initialization for O2Micro bridges" if EXPERT depends on YENTA config YENTA_RICOH default y - bool "Special initialization for Ricoh bridges" if EMBEDDED + bool "Special initialization for Ricoh bridges" if EXPERT depends on YENTA config YENTA_TI default y - bool "Special initialization for TI and EnE bridges" if EMBEDDED + bool "Special initialization for TI and EnE bridges" if EXPERT depends on YENTA config YENTA_ENE_TUNE default y - bool "Auto-tune EnE bridges for CB cards" if EMBEDDED + bool "Auto-tune EnE bridges for CB cards" if EXPERT depends on YENTA_TI && CARDBUS config YENTA_TOSHIBA default y - bool "Special initialization for Toshiba ToPIC bridges" if EMBEDDED + bool "Special initialization for Toshiba ToPIC bridges" if EXPERT depends on YENTA config PD6729 diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index c1df7676a73d..b1682d7f1d8a 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -81,7 +81,7 @@ config SERIAL_8250_GSC default SERIAL_8250 config SERIAL_8250_PCI - tristate "8250/16550 PCI device support" if EMBEDDED + tristate "8250/16550 PCI device support" if EXPERT depends on SERIAL_8250 && PCI default SERIAL_8250 help @@ -90,7 +90,7 @@ config SERIAL_8250_PCI Saves about 9K. config SERIAL_8250_PNP - tristate "8250/16550 PNP device support" if EMBEDDED + tristate "8250/16550 PNP device support" if EXPERT depends on SERIAL_8250 && PNP default SERIAL_8250 help diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index 2d8cc455dbc7..42cdaa9a4d8a 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -82,7 +82,7 @@ config SSB_SDIOHOST config SSB_SILENT bool "No SSB kernel messages" - depends on SSB && EMBEDDED + depends on SSB && EXPERT help This option turns off all Sonics Silicon Backplane printks. Note that you won't be able to identify problems, once diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index bcc24779ba0e..18d02e32a3d5 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -123,9 +123,9 @@ config USB_OTG config USB_OTG_WHITELIST bool "Rely on OTG Targeted Peripherals List" - depends on USB_OTG || EMBEDDED + depends on USB_OTG || EXPERT default y if USB_OTG - default n if EMBEDDED + default n if EXPERT help If you say Y here, the "otg_whitelist.h" file will be used as a product whitelist, so USB peripherals not listed there will be @@ -141,7 +141,7 @@ config USB_OTG_WHITELIST config USB_OTG_BLACKLIST_HUB bool "Disable external hubs" - depends on USB_OTG || EMBEDDED + depends on USB_OTG || EXPERT help If you say Y here, then Linux will refuse to enumerate external hubs. OTG hosts are allowed to reduce hardware diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index d916ac04abab..6bafb51bb437 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1227,7 +1227,7 @@ config FB_CARILLO_RANCH config FB_INTEL tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G/945GM/965G/965GM support (EXPERIMENTAL)" - depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL && EMBEDDED + depends on EXPERIMENTAL && FB && PCI && X86 && AGP_INTEL && EXPERT select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 5a35f22372b9..2209e354f531 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -5,7 +5,7 @@ menu "Console display driver support" config VGA_CONSOLE - bool "VGA text console" if EMBEDDED || !X86 + bool "VGA text console" if EXPERT || !X86 depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) default y help diff --git a/fs/Kconfig b/fs/Kconfig index 9a7921ae4763..3db9caa57edc 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -50,7 +50,7 @@ config EXPORTFS tristate config FILE_LOCKING - bool "Enable POSIX file locking API" if EMBEDDED + bool "Enable POSIX file locking API" if EXPERT default y help This option enables standard file locking support, required diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index 6a0068841d96..15af6222f8a4 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -1,5 +1,5 @@ config PROC_FS - bool "/proc file system support" if EMBEDDED + bool "/proc file system support" if EXPERT default y help This is a virtual file system providing information about the status @@ -40,7 +40,7 @@ config PROC_VMCORE Exports the dump image of crashed kernel in ELF format. config PROC_SYSCTL - bool "Sysctl support (/proc/sys)" if EMBEDDED + bool "Sysctl support (/proc/sys)" if EXPERT depends on PROC_FS select SYSCTL default y @@ -61,7 +61,7 @@ config PROC_SYSCTL config PROC_PAGE_MONITOR default y depends on PROC_FS && MMU - bool "Enable /proc page monitoring" if EMBEDDED + bool "Enable /proc page monitoring" if EXPERT help Various /proc files exist to monitor process memory utilization: /proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap, diff --git a/fs/sysfs/Kconfig b/fs/sysfs/Kconfig index f4b67588b9d6..8c41feacbac5 100644 --- a/fs/sysfs/Kconfig +++ b/fs/sysfs/Kconfig @@ -1,5 +1,5 @@ config SYSFS - bool "sysfs file system support" if EMBEDDED + bool "sysfs file system support" if EXPERT default y help The sysfs filesystem is a virtual filesystem that the kernel uses to diff --git a/init/Kconfig b/init/Kconfig index 4e337906016e..be788c0957d4 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -745,8 +745,8 @@ config DEBUG_BLK_CGROUP endif # CGROUPS menuconfig NAMESPACES - bool "Namespaces support" if EMBEDDED - default !EMBEDDED + bool "Namespaces support" if EXPERT + default !EXPERT help Provides the way to make tasks work with different objects using the same id. For example same IPC id may refer to different objects @@ -899,23 +899,31 @@ config SYSCTL config ANON_INODES bool -menuconfig EMBEDDED - bool "Configure standard kernel features (for small systems)" +menuconfig EXPERT + bool "Configure standard kernel features (expert users)" help This option allows certain base kernel options and settings to be disabled or tweaked. This is for specialized environments which can tolerate a "non-standard" kernel. Only use this if you really know what you are doing. +config EMBEDDED + bool "Embedded system" + select EXPERT + help + This option should be enabled if compiling the kernel for + an embedded system so certain expert options are available + for configuration. + config UID16 - bool "Enable 16-bit UID system calls" if EMBEDDED + bool "Enable 16-bit UID system calls" if EXPERT depends on ARM || BLACKFIN || CRIS || FRV || H8300 || X86_32 || M68K || (S390 && !64BIT) || SUPERH || SPARC32 || (SPARC64 && COMPAT) || UML || (X86_64 && IA32_EMULATION) default y help This enables the legacy 16-bit UID syscall wrappers. config SYSCTL_SYSCALL - bool "Sysctl syscall support" if EMBEDDED + bool "Sysctl syscall support" if EXPERT depends on PROC_SYSCTL default y select SYSCTL @@ -932,7 +940,7 @@ config SYSCTL_SYSCALL If unsure say Y here. config KALLSYMS - bool "Load all symbols for debugging/ksymoops" if EMBEDDED + bool "Load all symbols for debugging/ksymoops" if EXPERT default y help Say Y here to let the kernel print out symbolic crash information and @@ -963,7 +971,7 @@ config KALLSYMS_EXTRA_PASS config HOTPLUG - bool "Support for hot-pluggable devices" if EMBEDDED + bool "Support for hot-pluggable devices" if EXPERT default y help This option is provided for the case where no hotplug or uevent @@ -973,7 +981,7 @@ config HOTPLUG config PRINTK default y - bool "Enable support for printk" if EMBEDDED + bool "Enable support for printk" if EXPERT help This option enables normal printk support. Removing it eliminates most of the message strings from the kernel image @@ -982,7 +990,7 @@ config PRINTK strongly discouraged. config BUG - bool "BUG() support" if EMBEDDED + bool "BUG() support" if EXPERT default y help Disabling this option eliminates support for BUG and WARN, reducing @@ -993,12 +1001,12 @@ config BUG config ELF_CORE default y - bool "Enable ELF core dumps" if EMBEDDED + bool "Enable ELF core dumps" if EXPERT help Enable support for generating core dumps. Disabling saves about 4k. config PCSPKR_PLATFORM - bool "Enable PC-Speaker support" if EMBEDDED + bool "Enable PC-Speaker support" if EXPERT depends on ALPHA || X86 || MIPS || PPC_PREP || PPC_CHRP || PPC_PSERIES default y help @@ -1007,14 +1015,14 @@ config PCSPKR_PLATFORM config BASE_FULL default y - bool "Enable full-sized data structures for core" if EMBEDDED + bool "Enable full-sized data structures for core" if EXPERT help Disabling this option reduces the size of miscellaneous core kernel data structures. This saves memory on small machines, but may reduce performance. config FUTEX - bool "Enable futex support" if EMBEDDED + bool "Enable futex support" if EXPERT default y select RT_MUTEXES help @@ -1023,7 +1031,7 @@ config FUTEX run glibc-based applications correctly. config EPOLL - bool "Enable eventpoll support" if EMBEDDED + bool "Enable eventpoll support" if EXPERT default y select ANON_INODES help @@ -1031,7 +1039,7 @@ config EPOLL support for epoll family of system calls. config SIGNALFD - bool "Enable signalfd() system call" if EMBEDDED + bool "Enable signalfd() system call" if EXPERT select ANON_INODES default y help @@ -1041,7 +1049,7 @@ config SIGNALFD If unsure, say Y. config TIMERFD - bool "Enable timerfd() system call" if EMBEDDED + bool "Enable timerfd() system call" if EXPERT select ANON_INODES default y help @@ -1051,7 +1059,7 @@ config TIMERFD If unsure, say Y. config EVENTFD - bool "Enable eventfd() system call" if EMBEDDED + bool "Enable eventfd() system call" if EXPERT select ANON_INODES default y help @@ -1061,7 +1069,7 @@ config EVENTFD If unsure, say Y. config SHMEM - bool "Use full shmem filesystem" if EMBEDDED + bool "Use full shmem filesystem" if EXPERT default y depends on MMU help @@ -1072,7 +1080,7 @@ config SHMEM which may be appropriate on small systems without swap. config AIO - bool "Enable AIO support" if EMBEDDED + bool "Enable AIO support" if EXPERT default y help This option enables POSIX asynchronous I/O which may by used @@ -1149,16 +1157,16 @@ endmenu config VM_EVENT_COUNTERS default y - bool "Enable VM event counters for /proc/vmstat" if EMBEDDED + bool "Enable VM event counters for /proc/vmstat" if EXPERT help VM event counters are needed for event counts to be shown. This option allows the disabling of the VM event counters - on EMBEDDED systems. /proc/vmstat will only show page counts + on EXPERT systems. /proc/vmstat will only show page counts if VM event counters are disabled. config PCI_QUIRKS default y - bool "Enable PCI quirk workarounds" if EMBEDDED + bool "Enable PCI quirk workarounds" if EXPERT depends on PCI help This enables workarounds for various PCI chipset @@ -1167,7 +1175,7 @@ config PCI_QUIRKS config SLUB_DEBUG default y - bool "Enable SLUB debugging support" if EMBEDDED + bool "Enable SLUB debugging support" if EXPERT depends on SLUB && SYSFS help SLUB has extensive debug support features. Disabling these can @@ -1211,7 +1219,7 @@ config SLUB a slab allocator. config SLOB - depends on EMBEDDED + depends on EXPERT bool "SLOB (Simple Allocator)" help SLOB replaces the stock allocator with a drastically simpler @@ -1222,7 +1230,7 @@ endchoice config MMAP_ALLOW_UNINITIALIZED bool "Allow mmapped anonymous memory to be uninitialized" - depends on EMBEDDED && !MMU + depends on EXPERT && !MMU default n help Normally, and according to the Linux spec, anonymous memory obtained diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 2d05adb98401..3967c2356e37 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -657,7 +657,7 @@ config DEBUG_HIGHMEM Disable for production systems. config DEBUG_BUGVERBOSE - bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED + bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EXPERT depends on BUG depends on ARM || AVR32 || M32R || M68K || SPARC32 || SPARC64 || \ FRV || SUPERH || GENERIC_BUG || BLACKFIN || MN10300 @@ -729,8 +729,8 @@ config DEBUG_WRITECOUNT If unsure, say N. config DEBUG_MEMORY_INIT - bool "Debug memory initialisation" if EMBEDDED - default !EMBEDDED + bool "Debug memory initialisation" if EXPERT + default !EXPERT help Enable this for additional checks during memory initialisation. The sanity checks verify aspects of the VM such as the memory model diff --git a/lib/xz/Kconfig b/lib/xz/Kconfig index e3b6e18fdac5..60a6088d0e5e 100644 --- a/lib/xz/Kconfig +++ b/lib/xz/Kconfig @@ -7,37 +7,37 @@ config XZ_DEC CRC32 is supported. See Documentation/xz.txt for more information. config XZ_DEC_X86 - bool "x86 BCJ filter decoder" if EMBEDDED + bool "x86 BCJ filter decoder" if EXPERT default y depends on XZ_DEC select XZ_DEC_BCJ config XZ_DEC_POWERPC - bool "PowerPC BCJ filter decoder" if EMBEDDED + bool "PowerPC BCJ filter decoder" if EXPERT default y depends on XZ_DEC select XZ_DEC_BCJ config XZ_DEC_IA64 - bool "IA-64 BCJ filter decoder" if EMBEDDED + bool "IA-64 BCJ filter decoder" if EXPERT default y depends on XZ_DEC select XZ_DEC_BCJ config XZ_DEC_ARM - bool "ARM BCJ filter decoder" if EMBEDDED + bool "ARM BCJ filter decoder" if EXPERT default y depends on XZ_DEC select XZ_DEC_BCJ config XZ_DEC_ARMTHUMB - bool "ARM-Thumb BCJ filter decoder" if EMBEDDED + bool "ARM-Thumb BCJ filter decoder" if EXPERT default y depends on XZ_DEC select XZ_DEC_BCJ config XZ_DEC_SPARC - bool "SPARC BCJ filter decoder" if EMBEDDED + bool "SPARC BCJ filter decoder" if EXPERT default y depends on XZ_DEC select XZ_DEC_BCJ diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 9109262abd24..c766056d0488 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -20,7 +20,7 @@ config MAC80211_HAS_RC def_bool n config MAC80211_RC_PID - bool "PID controller based rate control algorithm" if EMBEDDED + bool "PID controller based rate control algorithm" if EXPERT select MAC80211_HAS_RC ---help--- This option enables a TX rate control algorithm for @@ -28,14 +28,14 @@ config MAC80211_RC_PID rate. config MAC80211_RC_MINSTREL - bool "Minstrel" if EMBEDDED + bool "Minstrel" if EXPERT select MAC80211_HAS_RC default y ---help--- This option enables the 'minstrel' TX rate control algorithm config MAC80211_RC_MINSTREL_HT - bool "Minstrel 802.11n support" if EMBEDDED + bool "Minstrel 802.11n support" if EXPERT depends on MAC80211_RC_MINSTREL default y ---help--- diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index eaf765876458..7fce6dfd2180 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -18,7 +18,7 @@ config RFKILL_LEDS default y config RFKILL_INPUT - bool "RF switch input support" if EMBEDDED + bool "RF switch input support" if EXPERT depends on RFKILL depends on INPUT = y || RFKILL = INPUT - default y if !EMBEDDED + default y if !EXPERT diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index d0ee29063e5d..1f1ef70f34f2 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -95,7 +95,7 @@ config CFG80211_DEBUGFS If unsure, say N. config CFG80211_INTERNAL_REGDB - bool "use statically compiled regulatory rules database" if EMBEDDED + bool "use statically compiled regulatory rules database" if EXPERT default n depends on CFG80211 ---help--- diff --git a/usr/Kconfig b/usr/Kconfig index 4780deac5974..65b845bd4e3e 100644 --- a/usr/Kconfig +++ b/usr/Kconfig @@ -46,7 +46,7 @@ config INITRAMFS_ROOT_GID If you are not sure, leave it set to "0". config RD_GZIP - bool "Support initial ramdisks compressed using gzip" if EMBEDDED + bool "Support initial ramdisks compressed using gzip" if EXPERT default y depends on BLK_DEV_INITRD select DECOMPRESS_GZIP @@ -55,8 +55,8 @@ config RD_GZIP If unsure, say Y. config RD_BZIP2 - bool "Support initial ramdisks compressed using bzip2" if EMBEDDED - default !EMBEDDED + bool "Support initial ramdisks compressed using bzip2" if EXPERT + default !EXPERT depends on BLK_DEV_INITRD select DECOMPRESS_BZIP2 help @@ -64,8 +64,8 @@ config RD_BZIP2 If unsure, say N. config RD_LZMA - bool "Support initial ramdisks compressed using LZMA" if EMBEDDED - default !EMBEDDED + bool "Support initial ramdisks compressed using LZMA" if EXPERT + default !EXPERT depends on BLK_DEV_INITRD select DECOMPRESS_LZMA help @@ -73,8 +73,8 @@ config RD_LZMA If unsure, say N. config RD_XZ - bool "Support initial ramdisks compressed using XZ" if EMBEDDED - default !EMBEDDED + bool "Support initial ramdisks compressed using XZ" if EXPERT + default !EXPERT depends on BLK_DEV_INITRD select DECOMPRESS_XZ help @@ -82,8 +82,8 @@ config RD_XZ If unsure, say N. config RD_LZO - bool "Support initial ramdisks compressed using LZO" if EMBEDDED - default !EMBEDDED + bool "Support initial ramdisks compressed using LZO" if EXPERT + default !EXPERT depends on BLK_DEV_INITRD select DECOMPRESS_LZO help -- cgit v1.2.3 From 453c719261c0b4030b2676124adb6e81c5fb6833 Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 20 Jan 2011 14:44:18 -0800 Subject: thp: keep highpte mapped until it is no longer needed Two users reported THP-related crashes on 32-bit x86 machines. Their oops reports indicated an invalid pte, and subsequent code inspection showed that the highpte is actually used after unmap. The fix is to unmap the pte only after all operations against it are finished. Signed-off-by: Johannes Weiner Reported-by: Ilya Dryomov Reported-by: werner Cc: Andrea Arcangeli Tested-by: Ilya Dryomov Tested-by: Steven Rostedt Signed-off-by: Linus Torvalds --- mm/huge_memory.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 004c9c2aac78..c4f634b3a48e 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1837,9 +1837,9 @@ static void collapse_huge_page(struct mm_struct *mm, spin_lock(ptl); isolated = __collapse_huge_page_isolate(vma, address, pte); spin_unlock(ptl); - pte_unmap(pte); if (unlikely(!isolated)) { + pte_unmap(pte); spin_lock(&mm->page_table_lock); BUG_ON(!pmd_none(*pmd)); set_pmd_at(mm, address, pmd, _pmd); @@ -1856,6 +1856,7 @@ static void collapse_huge_page(struct mm_struct *mm, anon_vma_unlock(vma->anon_vma); __collapse_huge_page_copy(pte, new_page, vma, address, ptl); + pte_unmap(pte); __SetPageUptodate(new_page); pgtable = pmd_pgtable(_pmd); VM_BUG_ON(page_count(pgtable) != 1); -- cgit v1.2.3 From abb65272a190660790096628859e752172d822fd Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Thu, 20 Jan 2011 14:44:20 -0800 Subject: memblock: fix memblock_is_region_memory() memblock_is_region_memory() uses reserved memblocks to search for the given region, while it should use the memory memblocks. I encountered the problem with OMAP's framebuffer ram allocation. Normally the ram is allocated dynamically, and this function is not called. However, if we want to pass the framebuffer from the bootloader to the kernel (to retain the boot image), this function is used to check the validity of the kernel parameters for the framebuffer ram area. Signed-off-by: Tomi Valkeinen Acked-by: Yinghai Lu Cc: Benjamin Herrenschmidt Cc: "H. Peter Anvin" Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memblock.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/memblock.c b/mm/memblock.c index 400dc62697d7..bdba245d8afd 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -683,13 +683,13 @@ int __init_memblock memblock_is_memory(phys_addr_t addr) int __init_memblock memblock_is_region_memory(phys_addr_t base, phys_addr_t size) { - int idx = memblock_search(&memblock.reserved, base); + int idx = memblock_search(&memblock.memory, base); if (idx == -1) return 0; - return memblock.reserved.regions[idx].base <= base && - (memblock.reserved.regions[idx].base + - memblock.reserved.regions[idx].size) >= (base + size); + return memblock.memory.regions[idx].base <= base && + (memblock.memory.regions[idx].base + + memblock.memory.regions[idx].size) >= (base + size); } int __init_memblock memblock_is_region_reserved(phys_addr_t base, phys_addr_t size) -- cgit v1.2.3 From 3305de51bf612603c9a4e4dc98ceb839ef933749 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 20 Jan 2011 14:44:20 -0800 Subject: mm/vmscan.c: remove duplicate include of compaction.h Signed-off-by: Jesper Juhl Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/vmscan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 47a50962ce81..f5d90dedebba 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 82478fb7bca28e3ca2f3c55c14e690f749dd4dbb Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 20 Jan 2011 14:44:21 -0800 Subject: mm: compaction: prevent division-by-zero during user-requested compaction Up until 3e7d344 ("mm: vmscan: reclaim order-0 and use compaction instead of lumpy reclaim"), compaction skipped calculating the fragmentation index of a zone when compaction was explicitely requested through the procfs knob. However, when compaction_suitable was introduced, it did not come with an extra check for order == -1, set on explicit compaction requests, and passed this order on to the fragmentation index calculation, where it overshifts the number of requested pages, leading to a division by zero. This patch makes sure that order == -1 is recognized as the flag it is rather than passing it along as valid order parameter. [akpm@linux-foundation.org: add comment, per Mel] Signed-off-by: Johannes Weiner Reviewed-by: Mel Gorman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/compaction.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/compaction.c b/mm/compaction.c index 6d592a021072..8be430b812de 100644 --- a/mm/compaction.c +++ b/mm/compaction.c @@ -406,6 +406,10 @@ static int compact_finished(struct zone *zone, if (!zone_watermark_ok(zone, cc->order, watermark, 0, 0)) return COMPACT_CONTINUE; + /* + * order == -1 is expected when compacting via + * /proc/sys/vm/compact_memory + */ if (cc->order == -1) return COMPACT_CONTINUE; @@ -453,6 +457,13 @@ unsigned long compaction_suitable(struct zone *zone, int order) if (!zone_watermark_ok(zone, 0, watermark, 0, 0)) return COMPACT_SKIPPED; + /* + * order == -1 is expected when compacting via + * /proc/sys/vm/compact_memory + */ + if (order == -1) + return COMPACT_CONTINUE; + /* * fragmentation index determines if allocation failures are due to * low memory or external fragmentation -- cgit v1.2.3 From 20d9600cb407b0b55fef6ee814b60345c6f58264 Mon Sep 17 00:00:00 2001 From: David Dillow Date: Thu, 20 Jan 2011 14:44:22 -0800 Subject: fs/direct-io.c: don't try to allocate more than BIO_MAX_PAGES in a bio When using devices that support max_segments > BIO_MAX_PAGES (256), direct IO tries to allocate a bio with more pages than allowed, which leads to an oops in dio_bio_alloc(). Clamp the request to the supported maximum, and change dio_bio_alloc() to reflect that bio_alloc() will always return a bio when called with __GFP_WAIT and a valid number of vectors. [akpm@linux-foundation.org: remove redundant BUG_ON()] Signed-off-by: David Dillow Reviewed-by: Jeff Moyer Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/direct-io.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/direct-io.c b/fs/direct-io.c index 85882f6ba5f7..b044705eedd4 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -325,12 +325,16 @@ void dio_end_io(struct bio *bio, int error) } EXPORT_SYMBOL_GPL(dio_end_io); -static int +static void dio_bio_alloc(struct dio *dio, struct block_device *bdev, sector_t first_sector, int nr_vecs) { struct bio *bio; + /* + * bio_alloc() is guaranteed to return a bio when called with + * __GFP_WAIT and we request a valid number of vectors. + */ bio = bio_alloc(GFP_KERNEL, nr_vecs); bio->bi_bdev = bdev; @@ -342,7 +346,6 @@ dio_bio_alloc(struct dio *dio, struct block_device *bdev, dio->bio = bio; dio->logical_offset_in_bio = dio->cur_page_fs_offset; - return 0; } /* @@ -583,8 +586,9 @@ static int dio_new_bio(struct dio *dio, sector_t start_sector) goto out; sector = start_sector << (dio->blkbits - 9); nr_pages = min(dio->pages_in_io, bio_get_nr_vecs(dio->map_bh.b_bdev)); + nr_pages = min(nr_pages, BIO_MAX_PAGES); BUG_ON(nr_pages <= 0); - ret = dio_bio_alloc(dio, dio->map_bh.b_bdev, sector, nr_pages); + dio_bio_alloc(dio, dio->map_bh.b_bdev, sector, nr_pages); dio->boundary = 0; out: return ret; -- cgit v1.2.3 From e401f1761c0b01966e36e41e2c385d455a7b44ee Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Thu, 20 Jan 2011 14:44:23 -0800 Subject: memcg: modify accounting function for supporting THP better mem_cgroup_charge_statisics() was designed for charging a page but now, we have transparent hugepage. To fix problems (in following patch) it's required to change the function to get the number of pages as its arguments. The new function gets following as argument. - type of page rather than 'pc' - size of page which is accounted. Signed-off-by: KAMEZAWA Hiroyuki Cc: Daisuke Nishimura Cc: Balbir Singh Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 8ab841031436..6d59a2bd520a 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -600,23 +600,22 @@ static void mem_cgroup_swap_statistics(struct mem_cgroup *mem, } static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, - struct page_cgroup *pc, - bool charge) + bool file, int nr_pages) { - int val = (charge) ? 1 : -1; - preempt_disable(); - if (PageCgroupCache(pc)) - __this_cpu_add(mem->stat->count[MEM_CGROUP_STAT_CACHE], val); + if (file) + __this_cpu_add(mem->stat->count[MEM_CGROUP_STAT_CACHE], nr_pages); else - __this_cpu_add(mem->stat->count[MEM_CGROUP_STAT_RSS], val); + __this_cpu_add(mem->stat->count[MEM_CGROUP_STAT_RSS], nr_pages); - if (charge) + /* pagein of a big page is an event. So, ignore page size */ + if (nr_pages > 0) __this_cpu_inc(mem->stat->count[MEM_CGROUP_STAT_PGPGIN_COUNT]); else __this_cpu_inc(mem->stat->count[MEM_CGROUP_STAT_PGPGOUT_COUNT]); - __this_cpu_inc(mem->stat->count[MEM_CGROUP_EVENTS]); + + __this_cpu_add(mem->stat->count[MEM_CGROUP_EVENTS], nr_pages); preempt_enable(); } @@ -2115,7 +2114,7 @@ static void ____mem_cgroup_commit_charge(struct mem_cgroup *mem, break; } - mem_cgroup_charge_statistics(mem, pc, true); + mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), 1); } static void __mem_cgroup_commit_charge(struct mem_cgroup *mem, @@ -2186,14 +2185,14 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc, __this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]); preempt_enable(); } - mem_cgroup_charge_statistics(from, pc, false); + mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -1); if (uncharge) /* This is not "cancel", but cancel_charge does all we need. */ mem_cgroup_cancel_charge(from, PAGE_SIZE); /* caller should have done css_get */ pc->mem_cgroup = to; - mem_cgroup_charge_statistics(to, pc, true); + mem_cgroup_charge_statistics(to, PageCgroupCache(pc), 1); /* * We charges against "to" which may not have any tasks. Then, "to" * can be under rmdir(). But in current implementation, caller of @@ -2597,7 +2596,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) } for (i = 0; i < count; i++) - mem_cgroup_charge_statistics(mem, pc + i, false); + mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), -1); ClearPageCgroupUsed(pc); /* -- cgit v1.2.3 From ca3e021417eed30ec2b64ce88eb0acf64aa9bc29 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Thu, 20 Jan 2011 14:44:24 -0800 Subject: memcg: fix USED bit handling at uncharge in THP Now, under THP: at charge: - PageCgroupUsed bit is set to all page_cgroup on a hugepage. ....set to 512 pages. at uncharge - PageCgroupUsed bit is unset on the head page. So, some pages will remain with "Used" bit. This patch fixes that Used bit is set only to the head page. Used bits for tail pages will be set at splitting if necessary. This patch adds this lock order: compound_lock() -> page_cgroup_move_lock(). [akpm@linux-foundation.org: fix warning] Signed-off-by: KAMEZAWA Hiroyuki Cc: Daisuke Nishimura Cc: Balbir Singh Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/memcontrol.h | 9 +++++ mm/huge_memory.c | 2 + mm/memcontrol.c | 91 ++++++++++++++++++++++++++-------------------- 3 files changed, 62 insertions(+), 40 deletions(-) diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 6a576f989437..f512e189be5a 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -146,6 +146,10 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, gfp_t gfp_mask); u64 mem_cgroup_get_limit(struct mem_cgroup *mem); +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail); +#endif + #else /* CONFIG_CGROUP_MEM_RES_CTLR */ struct mem_cgroup; @@ -335,6 +339,11 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *mem) return 0; } +static inline void mem_cgroup_split_huge_fixup(struct page *head, + struct page *tail) +{ +} + #endif /* CONFIG_CGROUP_MEM_CONT */ #endif /* _LINUX_MEMCONTROL_H */ diff --git a/mm/huge_memory.c b/mm/huge_memory.c index c4f634b3a48e..e187454d82f6 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1203,6 +1203,8 @@ static void __split_huge_page_refcount(struct page *page) BUG_ON(!PageDirty(page_tail)); BUG_ON(!PageSwapBacked(page_tail)); + mem_cgroup_split_huge_fixup(page, page_tail); + lru_add_page_tail(zone, page, page_tail); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 6d59a2bd520a..848b42195e5b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1614,7 +1614,7 @@ void mem_cgroup_update_page_stat(struct page *page, if (unlikely(!mem || !PageCgroupUsed(pc))) goto out; /* pc->mem_cgroup is unstable ? */ - if (unlikely(mem_cgroup_stealed(mem))) { + if (unlikely(mem_cgroup_stealed(mem)) || PageTransHuge(page)) { /* take a lock against to access pc->mem_cgroup */ move_lock_page_cgroup(pc, &flags); need_unlock = true; @@ -2083,14 +2083,27 @@ struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page) return mem; } -/* - * commit a charge got by __mem_cgroup_try_charge() and makes page_cgroup to be - * USED state. If already USED, uncharge and return. - */ -static void ____mem_cgroup_commit_charge(struct mem_cgroup *mem, - struct page_cgroup *pc, - enum charge_type ctype) +static void __mem_cgroup_commit_charge(struct mem_cgroup *mem, + struct page_cgroup *pc, + enum charge_type ctype, + int page_size) { + int nr_pages = page_size >> PAGE_SHIFT; + + /* try_charge() can return NULL to *memcg, taking care of it. */ + if (!mem) + return; + + lock_page_cgroup(pc); + if (unlikely(PageCgroupUsed(pc))) { + unlock_page_cgroup(pc); + mem_cgroup_cancel_charge(mem, page_size); + return; + } + /* + * we don't need page_cgroup_lock about tail pages, becase they are not + * accessed by any other context at this point. + */ pc->mem_cgroup = mem; /* * We access a page_cgroup asynchronously without lock_page_cgroup(). @@ -2114,35 +2127,7 @@ static void ____mem_cgroup_commit_charge(struct mem_cgroup *mem, break; } - mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), 1); -} - -static void __mem_cgroup_commit_charge(struct mem_cgroup *mem, - struct page_cgroup *pc, - enum charge_type ctype, - int page_size) -{ - int i; - int count = page_size >> PAGE_SHIFT; - - /* try_charge() can return NULL to *memcg, taking care of it. */ - if (!mem) - return; - - lock_page_cgroup(pc); - if (unlikely(PageCgroupUsed(pc))) { - unlock_page_cgroup(pc); - mem_cgroup_cancel_charge(mem, page_size); - return; - } - - /* - * we don't need page_cgroup_lock about tail pages, becase they are not - * accessed by any other context at this point. - */ - for (i = 0; i < count; i++) - ____mem_cgroup_commit_charge(mem, pc + i, ctype); - + mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), nr_pages); unlock_page_cgroup(pc); /* * "charge_statistics" updated event counter. Then, check it. @@ -2152,6 +2137,34 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *mem, memcg_check_events(mem, pc->page); } +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + +#define PCGF_NOCOPY_AT_SPLIT ((1 << PCG_LOCK) | (1 << PCG_MOVE_LOCK) |\ + (1 << PCG_ACCT_LRU) | (1 << PCG_MIGRATION)) +/* + * Because tail pages are not marked as "used", set it. We're under + * zone->lru_lock, 'splitting on pmd' and compund_lock. + */ +void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail) +{ + struct page_cgroup *head_pc = lookup_page_cgroup(head); + struct page_cgroup *tail_pc = lookup_page_cgroup(tail); + unsigned long flags; + + /* + * We have no races witch charge/uncharge but will have races with + * page state accounting. + */ + move_lock_page_cgroup(head_pc, &flags); + + tail_pc->mem_cgroup = head_pc->mem_cgroup; + smp_wmb(); /* see __commit_charge() */ + /* we don't need to copy all flags...*/ + tail_pc->flags = head_pc->flags & ~PCGF_NOCOPY_AT_SPLIT; + move_unlock_page_cgroup(head_pc, &flags); +} +#endif + /** * __mem_cgroup_move_account - move account of the page * @pc: page_cgroup of the page. @@ -2545,7 +2558,6 @@ direct_uncharge: static struct mem_cgroup * __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) { - int i; int count; struct page_cgroup *pc; struct mem_cgroup *mem = NULL; @@ -2595,8 +2607,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype) break; } - for (i = 0; i < count; i++) - mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), -1); + mem_cgroup_charge_statistics(mem, PageCgroupCache(pc), -count); ClearPageCgroupUsed(pc); /* -- cgit v1.2.3 From ece35ca810326946ddc930c43356312ad5de44d4 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Thu, 20 Jan 2011 14:44:24 -0800 Subject: memcg: fix LRU accounting with THP memory cgroup's LRU stat should take care of size of pages because Transparent Hugepage inserts hugepage into LRU. If this value is the number wrong, memory reclaim will not work well. Note: only head page of THP's huge page is linked into LRU. Signed-off-by: KAMEZAWA Hiroyuki Cc: Daisuke Nishimura Cc: Balbir Singh Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 848b42195e5b..7a94ef6b35e2 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -814,7 +814,8 @@ void mem_cgroup_del_lru_list(struct page *page, enum lru_list lru) * removed from global LRU. */ mz = page_cgroup_zoneinfo(pc); - MEM_CGROUP_ZSTAT(mz, lru) -= 1; + /* huge page split is done under lru_lock. so, we have no races. */ + MEM_CGROUP_ZSTAT(mz, lru) -= 1 << compound_order(page); if (mem_cgroup_is_root(pc->mem_cgroup)) return; VM_BUG_ON(list_empty(&pc->lru)); @@ -865,7 +866,8 @@ void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru) return; mz = page_cgroup_zoneinfo(pc); - MEM_CGROUP_ZSTAT(mz, lru) += 1; + /* huge page split is done under lru_lock. so, we have no races. */ + MEM_CGROUP_ZSTAT(mz, lru) += 1 << compound_order(page); SetPageCgroupAcctLRU(pc); if (mem_cgroup_is_root(pc->mem_cgroup)) return; @@ -2152,14 +2154,26 @@ void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail) unsigned long flags; /* - * We have no races witch charge/uncharge but will have races with + * We have no races with charge/uncharge but will have races with * page state accounting. */ move_lock_page_cgroup(head_pc, &flags); tail_pc->mem_cgroup = head_pc->mem_cgroup; smp_wmb(); /* see __commit_charge() */ - /* we don't need to copy all flags...*/ + if (PageCgroupAcctLRU(head_pc)) { + enum lru_list lru; + struct mem_cgroup_per_zone *mz; + + /* + * LRU flags cannot be copied because we need to add tail + *.page to LRU by generic call and our hook will be called. + * We hold lru_lock, then, reduce counter directly. + */ + lru = page_lru(head); + mz = page_cgroup_zoneinfo(head_pc); + MEM_CGROUP_ZSTAT(mz, lru) -= 1; + } tail_pc->flags = head_pc->flags & ~PCGF_NOCOPY_AT_SPLIT; move_unlock_page_cgroup(head_pc, &flags); } -- cgit v1.2.3 From 987eba66e0e6aa654d60881a14731a353ee0acb4 Mon Sep 17 00:00:00 2001 From: KAMEZAWA Hiroyuki Date: Thu, 20 Jan 2011 14:44:25 -0800 Subject: memcg: fix rmdir, force_empty with THP Now, when THP is enabled, memcg's rmdir() function is broken because move_account() for THP page is not supported. This will cause account leak or -EBUSY issue at rmdir(). This patch fixes the issue by supporting move_account() THP pages. Signed-off-by: KAMEZAWA Hiroyuki Cc: Daisuke Nishimura Cc: Balbir Singh Cc: Johannes Weiner Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 7a94ef6b35e2..5b562b375cbd 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2197,8 +2197,11 @@ void mem_cgroup_split_huge_fixup(struct page *head, struct page *tail) */ static void __mem_cgroup_move_account(struct page_cgroup *pc, - struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge) + struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge, + int charge_size) { + int nr_pages = charge_size >> PAGE_SHIFT; + VM_BUG_ON(from == to); VM_BUG_ON(PageLRU(pc->page)); VM_BUG_ON(!page_is_cgroup_locked(pc)); @@ -2212,14 +2215,14 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc, __this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]); preempt_enable(); } - mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -1); + mem_cgroup_charge_statistics(from, PageCgroupCache(pc), -nr_pages); if (uncharge) /* This is not "cancel", but cancel_charge does all we need. */ - mem_cgroup_cancel_charge(from, PAGE_SIZE); + mem_cgroup_cancel_charge(from, charge_size); /* caller should have done css_get */ pc->mem_cgroup = to; - mem_cgroup_charge_statistics(to, PageCgroupCache(pc), 1); + mem_cgroup_charge_statistics(to, PageCgroupCache(pc), nr_pages); /* * We charges against "to" which may not have any tasks. Then, "to" * can be under rmdir(). But in current implementation, caller of @@ -2234,15 +2237,19 @@ static void __mem_cgroup_move_account(struct page_cgroup *pc, * __mem_cgroup_move_account() */ static int mem_cgroup_move_account(struct page_cgroup *pc, - struct mem_cgroup *from, struct mem_cgroup *to, bool uncharge) + struct mem_cgroup *from, struct mem_cgroup *to, + bool uncharge, int charge_size) { int ret = -EINVAL; unsigned long flags; + if ((charge_size > PAGE_SIZE) && !PageTransHuge(pc->page)) + return -EBUSY; + lock_page_cgroup(pc); if (PageCgroupUsed(pc) && pc->mem_cgroup == from) { move_lock_page_cgroup(pc, &flags); - __mem_cgroup_move_account(pc, from, to, uncharge); + __mem_cgroup_move_account(pc, from, to, uncharge, charge_size); move_unlock_page_cgroup(pc, &flags); ret = 0; } @@ -2267,6 +2274,8 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc, struct cgroup *cg = child->css.cgroup; struct cgroup *pcg = cg->parent; struct mem_cgroup *parent; + int charge = PAGE_SIZE; + unsigned long flags; int ret; /* Is ROOT ? */ @@ -2278,17 +2287,23 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc, goto out; if (isolate_lru_page(page)) goto put; + /* The page is isolated from LRU and we have no race with splitting */ + charge = PAGE_SIZE << compound_order(page); parent = mem_cgroup_from_cont(pcg); - ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, - PAGE_SIZE); + ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, charge); if (ret || !parent) goto put_back; - ret = mem_cgroup_move_account(pc, child, parent, true); + if (charge > PAGE_SIZE) + flags = compound_lock_irqsave(page); + + ret = mem_cgroup_move_account(pc, child, parent, true, charge); if (ret) - mem_cgroup_cancel_charge(parent, PAGE_SIZE); + mem_cgroup_cancel_charge(parent, charge); put_back: + if (charge > PAGE_SIZE) + compound_unlock_irqrestore(page, flags); putback_lru_page(page); put: put_page(page); @@ -4868,7 +4883,7 @@ retry: goto put; pc = lookup_page_cgroup(page); if (!mem_cgroup_move_account(pc, - mc.from, mc.to, false)) { + mc.from, mc.to, false, PAGE_SIZE)) { mc.precharge--; /* we uncharge from mc.from later. */ mc.moved_charge++; -- cgit v1.2.3 From 382e27daa542ce97c500dc357841c6416c735cc2 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 20 Jan 2011 14:44:26 -0800 Subject: mm: fix truncate_setsize() comment Contrary to what the comment says, truncate_setsize() should be called *before* filesystem truncated blocks. Signed-off-by: Jan Kara Cc: Christoph Hellwig Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/truncate.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mm/truncate.c b/mm/truncate.c index 3c2d5ddfa0d4..49feb46e77b8 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -549,13 +549,12 @@ EXPORT_SYMBOL(truncate_pagecache); * @inode: inode * @newsize: new file size * - * truncate_setsize updastes i_size update and performs pagecache - * truncation (if necessary) for a file size updates. It will be - * typically be called from the filesystem's setattr function when - * ATTR_SIZE is passed in. + * truncate_setsize updates i_size and performs pagecache truncation (if + * necessary) to @newsize. It will be typically be called from the filesystem's + * setattr function when ATTR_SIZE is passed in. * - * Must be called with inode_mutex held and after all filesystem - * specific block truncation has been performed. + * Must be called with inode_mutex held and before all filesystem specific + * block truncation has been performed. */ void truncate_setsize(struct inode *inode, loff_t newsize) { -- cgit v1.2.3 From c1fc8675c9e3dede8e7cd91cebb2025e2e6db2dc Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 20 Jan 2011 14:44:27 -0800 Subject: MAINTAINERS: update Atmel AT91 entry Add two co-maintainers and update the entry with new information. Signed-off-by: Nicolas Ferre Acked-by: Andrew Victor Acked-by: Jean-Christophe PLAGNIOL-VILLARD Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- MAINTAINERS | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 1af022e63668..09ea4de09e79 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -624,11 +624,15 @@ M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -ARM/ATMEL AT91RM9200 ARM ARCHITECTURE +ARM/ATMEL AT91RM9200 AND AT91SAM ARM ARCHITECTURES M: Andrew Victor +M: Nicolas Ferre +M: Jean-Christophe Plagniol-Villard L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) W: http://maxim.org.za/at91_26.html -S: Maintained +W: http://www.linux4sam.org +S: Supported +F: arch/arm/mach-at91/ ARM/BCMRING ARM ARCHITECTURE M: Jiandong Zheng -- cgit v1.2.3 From cc587ece12791d001095488bf060fac52f657ea9 Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Thu, 20 Jan 2011 14:44:29 -0800 Subject: drivers/leds/ledtrig-gpio.c: make output match input, tighten input checking Replicate changes made to drivers/leds/ledtrig-backlight.c. Cc: Paul Mundt Cc: Richard Purdie Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/leds/ledtrig-gpio.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/leds/ledtrig-gpio.c b/drivers/leds/ledtrig-gpio.c index 991d93be0f44..ecc4bf3f37a9 100644 --- a/drivers/leds/ledtrig-gpio.c +++ b/drivers/leds/ledtrig-gpio.c @@ -99,7 +99,7 @@ static ssize_t gpio_trig_inverted_show(struct device *dev, struct led_classdev *led = dev_get_drvdata(dev); struct gpio_trig_data *gpio_data = led->trigger_data; - return sprintf(buf, "%s\n", gpio_data->inverted ? "yes" : "no"); + return sprintf(buf, "%u\n", gpio_data->inverted); } static ssize_t gpio_trig_inverted_store(struct device *dev, @@ -107,16 +107,17 @@ static ssize_t gpio_trig_inverted_store(struct device *dev, { struct led_classdev *led = dev_get_drvdata(dev); struct gpio_trig_data *gpio_data = led->trigger_data; - unsigned inverted; + unsigned long inverted; int ret; - ret = sscanf(buf, "%u", &inverted); - if (ret < 1) { - dev_err(dev, "invalid value\n"); + ret = strict_strtoul(buf, 10, &inverted); + if (ret < 0) + return ret; + + if (inverted > 1) return -EINVAL; - } - gpio_data->inverted = !!inverted; + gpio_data->inverted = inverted; /* After inverting, we need to update the LED. */ schedule_work(&gpio_data->work); -- cgit v1.2.3 From 2550326ac7a062fdfc204f9a3b98bdb9179638fc Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 20 Jan 2011 14:44:31 -0800 Subject: backlight: fix 88pm860x_bl macro collision Fix collision with kernel-supplied #define: drivers/video/backlight/88pm860x_bl.c:24:1: warning: "CURRENT_MASK" redefined arch/x86/include/asm/page_64_types.h:6:1: warning: this is the location of the previous definition Signed-off-by: Randy Dunlap Cc: Haojian Zhuang Cc: Richard Purdie Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/backlight/88pm860x_bl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index c789c46e38af..b224396b86d5 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -21,7 +21,7 @@ #define MAX_BRIGHTNESS (0xFF) #define MIN_BRIGHTNESS (0) -#define CURRENT_MASK (0x1F << 1) +#define CURRENT_BITMASK (0x1F << 1) struct pm860x_backlight_data { struct pm860x_chip *chip; @@ -85,7 +85,7 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness) if ((data->current_brightness == 0) && brightness) { if (data->iset) { ret = pm860x_set_bits(data->i2c, wled_idc(data->port), - CURRENT_MASK, data->iset); + CURRENT_BITMASK, data->iset); if (ret < 0) goto out; } -- cgit v1.2.3 From 713735b4233fad3ae35b5cad656baa41413887ca Mon Sep 17 00:00:00 2001 From: Johannes Weiner Date: Thu, 20 Jan 2011 14:44:31 -0800 Subject: memcg: correctly order reading PCG_USED and pc->mem_cgroup The placement of the read-side barrier is confused: the writer first sets pc->mem_cgroup, then PCG_USED. The read-side barrier has to be between testing PCG_USED and reading pc->mem_cgroup. Signed-off-by: Johannes Weiner Acked-by: KAMEZAWA Hiroyuki Acked-by: Daisuke Nishimura Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/memcontrol.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5b562b375cbd..db76ef726293 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -836,13 +836,12 @@ void mem_cgroup_rotate_lru_list(struct page *page, enum lru_list lru) return; pc = lookup_page_cgroup(page); - /* - * Used bit is set without atomic ops but after smp_wmb(). - * For making pc->mem_cgroup visible, insert smp_rmb() here. - */ - smp_rmb(); /* unused or root page is not rotated. */ - if (!PageCgroupUsed(pc) || mem_cgroup_is_root(pc->mem_cgroup)) + if (!PageCgroupUsed(pc)) + return; + /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ + smp_rmb(); + if (mem_cgroup_is_root(pc->mem_cgroup)) return; mz = page_cgroup_zoneinfo(pc); list_move(&pc->lru, &mz->lists[lru]); @@ -857,14 +856,10 @@ void mem_cgroup_add_lru_list(struct page *page, enum lru_list lru) return; pc = lookup_page_cgroup(page); VM_BUG_ON(PageCgroupAcctLRU(pc)); - /* - * Used bit is set without atomic ops but after smp_wmb(). - * For making pc->mem_cgroup visible, insert smp_rmb() here. - */ - smp_rmb(); if (!PageCgroupUsed(pc)) return; - + /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ + smp_rmb(); mz = page_cgroup_zoneinfo(pc); /* huge page split is done under lru_lock. so, we have no races. */ MEM_CGROUP_ZSTAT(mz, lru) += 1 << compound_order(page); @@ -1031,14 +1026,10 @@ mem_cgroup_get_reclaim_stat_from_page(struct page *page) return NULL; pc = lookup_page_cgroup(page); - /* - * Used bit is set without atomic ops but after smp_wmb(). - * For making pc->mem_cgroup visible, insert smp_rmb() here. - */ - smp_rmb(); if (!PageCgroupUsed(pc)) return NULL; - + /* Ensure pc->mem_cgroup is visible after reading PCG_USED. */ + smp_rmb(); mz = page_cgroup_zoneinfo(pc); if (!mz) return NULL; -- cgit v1.2.3 From 6dc19899958e420a931274b94019e267e2396d3e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 20 Jan 2011 14:44:33 -0800 Subject: kernel/smp.c: fix smp_call_function_many() SMP race I noticed a failure where we hit the following WARN_ON in generic_smp_call_function_interrupt: if (!cpumask_test_and_clear_cpu(cpu, data->cpumask)) continue; data->csd.func(data->csd.info); refs = atomic_dec_return(&data->refs); WARN_ON(refs < 0); <------------------------- We atomically tested and cleared our bit in the cpumask, and yet the number of cpus left (ie refs) was 0. How can this be? It turns out commit 54fdade1c3332391948ec43530c02c4794a38172 ("generic-ipi: make struct call_function_data lockless") is at fault. It removes locking from smp_call_function_many and in doing so creates a rather complicated race. The problem comes about because: - The smp_call_function_many interrupt handler walks call_function.queue without any locking. - We reuse a percpu data structure in smp_call_function_many. - We do not wait for any RCU grace period before starting the next smp_call_function_many. Imagine a scenario where CPU A does two smp_call_functions back to back, and CPU B does an smp_call_function in between. We concentrate on how CPU C handles the calls: CPU A CPU B CPU C CPU D smp_call_function smp_call_function_interrupt walks call_function.queue sees data from CPU A on list smp_call_function smp_call_function_interrupt walks call_function.queue sees (stale) CPU A on list smp_call_function int clears last ref on A list_del_rcu, unlock smp_call_function reuses percpu *data A data->cpumask sees and clears bit in cpumask might be using old or new fn! decrements refs below 0 set data->refs (too late!) The important thing to note is since the interrupt handler walks a potentially stale call_function.queue without any locking, then another cpu can view the percpu *data structure at any time, even when the owner is in the process of initialising it. The following test case hits the WARN_ON 100% of the time on my PowerPC box (having 128 threads does help :) #include #include #define ITERATIONS 100 static void do_nothing_ipi(void *dummy) { } static void do_ipis(struct work_struct *dummy) { int i; for (i = 0; i < ITERATIONS; i++) smp_call_function(do_nothing_ipi, NULL, 1); printk(KERN_DEBUG "cpu %d finished\n", smp_processor_id()); } static struct work_struct work[NR_CPUS]; static int __init testcase_init(void) { int cpu; for_each_online_cpu(cpu) { INIT_WORK(&work[cpu], do_ipis); schedule_work_on(cpu, &work[cpu]); } return 0; } static void __exit testcase_exit(void) { } module_init(testcase_init) module_exit(testcase_exit) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anton Blanchard"); I tried to fix it by ordering the read and the write of ->cpumask and ->refs. In doing so I missed a critical case but Paul McKenney was able to spot my bug thankfully :) To ensure we arent viewing previous iterations the interrupt handler needs to read ->refs then ->cpumask then ->refs _again_. Thanks to Milton Miller and Paul McKenney for helping to debug this issue. [miltonm@bga.com: add WARN_ON and BUG_ON, remove extra read of refs before initial read of mask that doesn't help (also noted by Peter Zijlstra), adjust comments, hopefully clarify scenario ] [miltonm@bga.com: remove excess tests] Signed-off-by: Anton Blanchard Signed-off-by: Milton Miller Cc: Ingo Molnar Cc: "Paul E. McKenney" Cc: [2.6.32+] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/smp.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/kernel/smp.c b/kernel/smp.c index 4ec30e069987..17c6e5860231 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -195,6 +195,24 @@ void generic_smp_call_function_interrupt(void) list_for_each_entry_rcu(data, &call_function.queue, csd.list) { int refs; + /* + * Since we walk the list without any locks, we might + * see an entry that was completed, removed from the + * list and is in the process of being reused. + * + * We must check that the cpu is in the cpumask before + * checking the refs, and both must be set before + * executing the callback on this cpu. + */ + + if (!cpumask_test_cpu(cpu, data->cpumask)) + continue; + + smp_rmb(); + + if (atomic_read(&data->refs) == 0) + continue; + if (!cpumask_test_and_clear_cpu(cpu, data->cpumask)) continue; @@ -203,6 +221,8 @@ void generic_smp_call_function_interrupt(void) refs = atomic_dec_return(&data->refs); WARN_ON(refs < 0); if (!refs) { + WARN_ON(!cpumask_empty(data->cpumask)); + raw_spin_lock(&call_function.lock); list_del_rcu(&data->csd.list); raw_spin_unlock(&call_function.lock); @@ -454,11 +474,21 @@ void smp_call_function_many(const struct cpumask *mask, data = &__get_cpu_var(cfd_data); csd_lock(&data->csd); + BUG_ON(atomic_read(&data->refs) || !cpumask_empty(data->cpumask)); data->csd.func = func; data->csd.info = info; cpumask_and(data->cpumask, mask, cpu_online_mask); cpumask_clear_cpu(this_cpu, data->cpumask); + + /* + * To ensure the interrupt handler gets an complete view + * we order the cpumask and refs writes and order the read + * of them in the interrupt handler. In addition we may + * only clear our own cpu bit from the mask. + */ + smp_wmb(); + atomic_set(&data->refs, cpumask_weight(data->cpumask)); raw_spin_lock_irqsave(&call_function.lock, flags); -- cgit v1.2.3 From 225c8e010f2d17a62aef131e24c6e7c111f36f9b Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Thu, 20 Jan 2011 14:44:34 -0800 Subject: kernel/smp.c: consolidate writes in smp_call_function_interrupt() We have to test the cpu mask in the interrupt handler before checking the refs, otherwise we can start to follow an entry before its deleted and find it partially initailzed for the next trip. Presently we also clear the cpumask bit before executing the called function, which implies getting write access to the line. After the function is called we then decrement refs, and if they go to zero we then unlock the structure. However, this implies getting write access to the call function data before and after another the function is called. If we can assert that no smp_call_function execution function is allowed to enable interrupts, then we can move both writes to after the function is called, hopfully allowing both writes with one cache line bounce. On a 256 thread system with a kernel compiled for 1024 threads, the time to execute testcase in the "smp_call_function_many race" changelog was reduced by about 30-40ms out of about 545 ms. I decided to keep this as WARN because its now a buggy function, even though the stack trace is of no value -- a simple printk would give us the information needed. Raw data: Without patch: ipi_test startup took 1219366ns complete 539819014ns total 541038380ns ipi_test startup took 1695754ns complete 543439872ns total 545135626ns ipi_test startup took 7513568ns complete 539606362ns total 547119930ns ipi_test startup took 13304064ns complete 533898562ns total 547202626ns ipi_test startup took 8668192ns complete 544264074ns total 552932266ns ipi_test startup took 4977626ns complete 548862684ns total 553840310ns ipi_test startup took 2144486ns complete 541292318ns total 543436804ns ipi_test startup took 21245824ns complete 530280180ns total 551526004ns With patch: ipi_test startup took 5961748ns complete 500859628ns total 506821376ns ipi_test startup took 8975996ns complete 495098924ns total 504074920ns ipi_test startup took 19797750ns complete 492204740ns total 512002490ns ipi_test startup took 14824796ns complete 487495878ns total 502320674ns ipi_test startup took 11514882ns complete 494439372ns total 505954254ns ipi_test startup took 8288084ns complete 502570774ns total 510858858ns ipi_test startup took 6789954ns complete 493388112ns total 500178066ns #include #include #include /* sched clock */ #define ITERATIONS 100 static void do_nothing_ipi(void *dummy) { } static void do_ipis(struct work_struct *dummy) { int i; for (i = 0; i < ITERATIONS; i++) smp_call_function(do_nothing_ipi, NULL, 1); printk(KERN_DEBUG "cpu %d finished\n", smp_processor_id()); } static struct work_struct work[NR_CPUS]; static int __init testcase_init(void) { int cpu; u64 start, started, done; start = local_clock(); for_each_online_cpu(cpu) { INIT_WORK(&work[cpu], do_ipis); schedule_work_on(cpu, &work[cpu]); } started = local_clock(); for_each_online_cpu(cpu) flush_work(&work[cpu]); done = local_clock(); pr_info("ipi_test startup took %lldns complete %lldns total %lldns\n", started-start, done-started, done-start); return 0; } static void __exit testcase_exit(void) { } module_init(testcase_init) module_exit(testcase_exit) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anton Blanchard"); Signed-off-by: Milton Miller Cc: Anton Blanchard Cc: Ingo Molnar Cc: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/smp.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/kernel/smp.c b/kernel/smp.c index 17c6e5860231..2fe66f7c617a 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -194,6 +194,7 @@ void generic_smp_call_function_interrupt(void) */ list_for_each_entry_rcu(data, &call_function.queue, csd.list) { int refs; + void (*func) (void *info); /* * Since we walk the list without any locks, we might @@ -213,24 +214,32 @@ void generic_smp_call_function_interrupt(void) if (atomic_read(&data->refs) == 0) continue; - if (!cpumask_test_and_clear_cpu(cpu, data->cpumask)) - continue; - + func = data->csd.func; /* for later warn */ data->csd.func(data->csd.info); + /* + * If the cpu mask is not still set then it enabled interrupts, + * we took another smp interrupt, and executed the function + * twice on this cpu. In theory that copy decremented refs. + */ + if (!cpumask_test_and_clear_cpu(cpu, data->cpumask)) { + WARN(1, "%pS enabled interrupts and double executed\n", + func); + continue; + } + refs = atomic_dec_return(&data->refs); WARN_ON(refs < 0); - if (!refs) { - WARN_ON(!cpumask_empty(data->cpumask)); - - raw_spin_lock(&call_function.lock); - list_del_rcu(&data->csd.list); - raw_spin_unlock(&call_function.lock); - } if (refs) continue; + WARN_ON(!cpumask_empty(data->cpumask)); + + raw_spin_lock(&call_function.lock); + list_del_rcu(&data->csd.list); + raw_spin_unlock(&call_function.lock); + csd_unlock(&data->csd); } -- cgit v1.2.3 From 99d86c8f1b7101d7c55dbf644b32bb1f0d7eb303 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 20 Jan 2011 21:19:25 -0500 Subject: cifs: fix up CIFSSMBEcho for unaligned access Make sure that CIFSSMBEcho can handle unaligned fields. Also fix a minor bug that causes this warning: fs/cifs/cifssmb.c: In function 'CIFSSMBEcho': fs/cifs/cifssmb.c:740: warning: large integer implicitly truncated to unsigned type ...WordCount is u8, not __le16, so no need to convert it. This patch should apply cleanly on top of the rest of the patchset to clean up unaligned access. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifssmb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 675041a6949c..3106f5e5c633 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -733,9 +733,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server) /* set up echo request */ smb->hdr.Tid = cpu_to_le16(0xffff); - smb->hdr.WordCount = cpu_to_le16(1); - smb->EchoCount = cpu_to_le16(1); - smb->ByteCount = cpu_to_le16(1); + smb->hdr.WordCount = 1; + put_unaligned_le16(1, &smb->EchoCount); + put_bcc_le(1, &smb->hdr); smb->Data[0] = 'a'; smb->hdr.smb_buf_length += 3; -- cgit v1.2.3 From 2d6d9fd3a54a28c6f67f26eb6c74803307a1b11e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 19 Jan 2011 22:27:14 +0100 Subject: ACPI: Introduce acpi_os_ioremap() Commit ca9b600be38c ("ACPI / PM: Make suspend_nvs_save() use acpi_os_map_memory()") attempted to prevent the code in osl.c and nvs.c from using different ioremap() variants by making the latter use acpi_os_map_memory() for mapping the NVS pages. However, that also requires acpi_os_unmap_memory() to be used for unmapping them, which causes synchronize_rcu() to be executed many times in a row unnecessarily and introduces substantial delays during resume on some systems. Instead of using acpi_os_map_memory() for mapping the NVS pages in nvs.c introduce acpi_os_ioremap() calling ioremap_cache() and make the code in both osl.c and nvs.c use it. Reported-by: Jeff Chua Signed-off-by: Rafael J. Wysocki Signed-off-by: Linus Torvalds --- drivers/acpi/nvs.c | 7 ++++--- drivers/acpi/osl.c | 12 +++++++----- include/linux/acpi.h | 3 --- include/linux/acpi_io.h | 16 ++++++++++++++++ 4 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 include/linux/acpi_io.h diff --git a/drivers/acpi/nvs.c b/drivers/acpi/nvs.c index 54b6ab8040a6..fa5a1df42b79 100644 --- a/drivers/acpi/nvs.c +++ b/drivers/acpi/nvs.c @@ -12,6 +12,7 @@ #include #include #include +#include #include /* @@ -80,7 +81,7 @@ void suspend_nvs_free(void) free_page((unsigned long)entry->data); entry->data = NULL; if (entry->kaddr) { - acpi_os_unmap_memory(entry->kaddr, entry->size); + iounmap(entry->kaddr); entry->kaddr = NULL; } } @@ -114,8 +115,8 @@ int suspend_nvs_save(void) list_for_each_entry(entry, &nvs_list, node) if (entry->data) { - entry->kaddr = acpi_os_map_memory(entry->phys_start, - entry->size); + entry->kaddr = acpi_os_ioremap(entry->phys_start, + entry->size); if (!entry->kaddr) { suspend_nvs_free(); return -ENOMEM; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e2dd6de5d50c..b0931818cf98 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -302,9 +303,10 @@ void __iomem *__init_refok acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { struct acpi_ioremap *map, *tmp_map; - unsigned long flags, pg_sz; + unsigned long flags; void __iomem *virt; - phys_addr_t pg_off; + acpi_physical_address pg_off; + acpi_size pg_sz; if (phys > ULONG_MAX) { printk(KERN_ERR PREFIX "Cannot map memory that high\n"); @@ -320,7 +322,7 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) pg_off = round_down(phys, PAGE_SIZE); pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; - virt = ioremap_cache(pg_off, pg_sz); + virt = acpi_os_ioremap(pg_off, pg_sz); if (!virt) { kfree(map); return NULL; @@ -642,7 +644,7 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) virt_addr = acpi_map_vaddr_lookup(phys_addr, size); rcu_read_unlock(); if (!virt_addr) { - virt_addr = ioremap_cache(phys_addr, size); + virt_addr = acpi_os_ioremap(phys_addr, size); unmap = 1; } if (!value) @@ -678,7 +680,7 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) virt_addr = acpi_map_vaddr_lookup(phys_addr, size); rcu_read_unlock(); if (!virt_addr) { - virt_addr = ioremap_cache(phys_addr, size); + virt_addr = acpi_os_ioremap(phys_addr, size); unmap = 1; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index eb176bb1b15b..a2e910e01293 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -306,9 +306,6 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req); extern void acpi_early_init(void); -int acpi_os_map_generic_address(struct acpi_generic_address *addr); -void acpi_os_unmap_generic_address(struct acpi_generic_address *addr); - #else /* !CONFIG_ACPI */ #define acpi_disabled 1 diff --git a/include/linux/acpi_io.h b/include/linux/acpi_io.h new file mode 100644 index 000000000000..7180013a4a3a --- /dev/null +++ b/include/linux/acpi_io.h @@ -0,0 +1,16 @@ +#ifndef _ACPI_IO_H_ +#define _ACPI_IO_H_ + +#include +#include + +static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, + acpi_size size) +{ + return ioremap_cache(phys, size); +} + +int acpi_os_map_generic_address(struct acpi_generic_address *addr); +void acpi_os_unmap_generic_address(struct acpi_generic_address *addr); + +#endif -- cgit v1.2.3 From d551d81d6a720542873f478def60baab6b5df403 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 19 Jan 2011 22:27:55 +0100 Subject: ACPI / PM: Call suspend_nvs_free() earlier during resume It turns out that some device drivers map pages from the ACPI NVS region during resume using ioremap(), which conflicts with ioremap_cache() used for mapping those pages by the NVS save/restore code in nvs.c. Make the NVS pages mapped by the code in nvs.c be unmapped before device drivers' resume routines run. Signed-off-by: Rafael J. Wysocki Signed-off-by: Linus Torvalds --- drivers/acpi/sleep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index fdd3aeeb6def..d6a8cd14de2e 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -166,6 +166,7 @@ static void acpi_pm_finish(void) u32 acpi_state = acpi_target_sleep_state; acpi_ec_unblock_transactions(); + suspend_nvs_free(); if (acpi_state == ACPI_STATE_S0) return; @@ -186,7 +187,6 @@ static void acpi_pm_finish(void) */ static void acpi_pm_end(void) { - suspend_nvs_free(); /* * This is necessary in case acpi_pm_finish() is not called during a * failing transition to a sleep state. -- cgit v1.2.3 From c0337288ab165be17081d61d4ef13b79d3ac55d4 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Sun, 7 Nov 2010 18:22:29 +0000 Subject: powerpc: Ensure the else case of feature sections will fit When we create an alternative feature section, the else case must be the same size or smaller than the body. This is because when we patch the else case in we just overwrite the body, so there must be room. Up to now we just did this by inspection, but it's quite easy to enforce it in the assembler, so we should. The only change is to add the ifgt block, but that effects the alignment of the tabs and so the whole macro is modified. Also add a test, but #if 0 it because we don't want to break the build. Anyone who's modifying the feature macros should enable the test. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/feature-fixups.h | 27 +++++++++++++++------------ arch/powerpc/lib/feature-fixups-test.S | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/feature-fixups.h b/arch/powerpc/include/asm/feature-fixups.h index 96a7d067fbb2..921a8470e18a 100644 --- a/arch/powerpc/include/asm/feature-fixups.h +++ b/arch/powerpc/include/asm/feature-fixups.h @@ -37,18 +37,21 @@ label##2: \ .align 2; \ label##3: -#define MAKE_FTR_SECTION_ENTRY(msk, val, label, sect) \ -label##4: \ - .popsection; \ - .pushsection sect,"a"; \ - .align 3; \ -label##5: \ - FTR_ENTRY_LONG msk; \ - FTR_ENTRY_LONG val; \ - FTR_ENTRY_OFFSET label##1b-label##5b; \ - FTR_ENTRY_OFFSET label##2b-label##5b; \ - FTR_ENTRY_OFFSET label##3b-label##5b; \ - FTR_ENTRY_OFFSET label##4b-label##5b; \ +#define MAKE_FTR_SECTION_ENTRY(msk, val, label, sect) \ +label##4: \ + .popsection; \ + .pushsection sect,"a"; \ + .align 3; \ +label##5: \ + FTR_ENTRY_LONG msk; \ + FTR_ENTRY_LONG val; \ + FTR_ENTRY_OFFSET label##1b-label##5b; \ + FTR_ENTRY_OFFSET label##2b-label##5b; \ + FTR_ENTRY_OFFSET label##3b-label##5b; \ + FTR_ENTRY_OFFSET label##4b-label##5b; \ + .ifgt (label##4b-label##3b)-(label##2b-label##1b); \ + .error "Feature section else case larger than body"; \ + .endif; \ .popsection; diff --git a/arch/powerpc/lib/feature-fixups-test.S b/arch/powerpc/lib/feature-fixups-test.S index cb737484c5aa..f4613118132e 100644 --- a/arch/powerpc/lib/feature-fixups-test.S +++ b/arch/powerpc/lib/feature-fixups-test.S @@ -172,6 +172,25 @@ globl(ftr_fixup_test6_expected) 3: or 3,3,3 +#if 0 +/* Test that if we have a larger else case the assembler spots it and + * reports an error. #if 0'ed so as not to break the build normally. + */ +ftr_fixup_test7: + or 1,1,1 +BEGIN_FTR_SECTION + or 2,2,2 + or 2,2,2 + or 2,2,2 +FTR_SECTION_ELSE + or 3,3,3 + or 3,3,3 + or 3,3,3 + or 3,3,3 +ALT_FTR_SECTION_END(0, 1) + or 1,1,1 +#endif + #define MAKE_MACRO_TEST(TYPE) \ globl(ftr_fixup_test_ ##TYPE##_macros) \ or 1,1,1; \ -- cgit v1.2.3 From 3cb5f1a3e58c0bd70d47d9907cc5c65192281dee Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 23 Dec 2010 19:46:06 +0000 Subject: powerpc/ppc64/tracing: Add stack frame to calls of trace_hardirqs_on/off When an interrupt occurs in userspace, we can call trace_hardirqs_on/off() With one level stack. But if we have irqsoff tracing enabled, it checks both CALLER_ADDR0 and CALLER_ADDR1. The second call goes two stack frames up. If this is from user space, then there may not exist a second stack. Add a second stack when calling trace_hardirqs_on/off() otherwise the following oops might occur: Oops: Kernel access of bad area, sig: 11 [#1] PREEMPT SMP NR_CPUS=2 PA Semi PWRficient last sysfs file: /sys/block/sda/size Modules linked in: ohci_hcd ehci_hcd usbcore NIP: c0000000000e1c00 LR: c0000000000034d4 CTR: 000000011012c440 REGS: c00000003e2f3af0 TRAP: 0300 Not tainted (2.6.37-rc6+) MSR: 9000000000001032 CR: 48044444 XER: 20000000 DAR: 00000001ffb9db50, DSISR: 0000000040000000 TASK = c00000003e1a00a0[2088] 'emacs' THREAD: c00000003e2f0000 CPU: 1 GPR00: 0000000000000001 c00000003e2f3d70 c00000000084e0d0 c0000000008816e8 GPR04: 000000001034c678 000000001032e8f9 0000000010336540 0000000040020000 GPR08: 0000000040020000 00000001ffb9db40 c00000003e2f3e30 0000000060000000 GPR12: 100000000000f032 c00000000fff0280 000000001032e8c9 0000000000000008 GPR16: 00000000105be9c0 00000000105be950 00000000105be9b0 00000000105be950 GPR20: 00000000ffb9dc50 00000000ffb9dbf0 00000000102f0000 00000000102f0000 GPR24: 00000000102e0000 00000000102f0000 0000000010336540 c0000000009ded38 GPR28: 00000000102e0000 c0000000000034d4 c0000000007ccb10 c00000003e2f3d70 NIP [c0000000000e1c00] .trace_hardirqs_off+0xb0/0x1d0 LR [c0000000000034d4] decrementer_common+0xd4/0x100 Call Trace: [c00000003e2f3d70] [c00000003e2f3e30] 0xc00000003e2f3e30 (unreliable) [c00000003e2f3e30] [c0000000000034d4] decrementer_common+0xd4/0x100 Instruction dump: 81690000 7f8b0000 419e0018 f84a0028 60000000 60000000 60000000 e95f0000 80030000 e92a0000 eb6301f8 2f800000 41fe00dc a06d000a eb1e8050 ---[ end trace 4ec7fd2be9240928 ]--- Reported-by: Joerg Sommer Signed-off-by: Steven Rostedt Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/irqflags.h | 40 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/include/asm/irqflags.h b/arch/powerpc/include/asm/irqflags.h index b85d8ddbb666..b0b06d85788d 100644 --- a/arch/powerpc/include/asm/irqflags.h +++ b/arch/powerpc/include/asm/irqflags.h @@ -12,24 +12,44 @@ #else #ifdef CONFIG_TRACE_IRQFLAGS +#ifdef CONFIG_IRQSOFF_TRACER +/* + * Since the ftrace irqsoff latency trace checks CALLER_ADDR1, + * which is the stack frame here, we need to force a stack frame + * in case we came from user space. + */ +#define TRACE_WITH_FRAME_BUFFER(func) \ + mflr r0; \ + stdu r1, -32(r1); \ + std r0, 16(r1); \ + stdu r1, -32(r1); \ + bl func; \ + ld r1, 0(r1); \ + ld r1, 0(r1); +#else +#define TRACE_WITH_FRAME_BUFFER(func) \ + bl func; +#endif + /* * Most of the CPU's IRQ-state tracing is done from assembly code; we * have to call a C function so call a wrapper that saves all the * C-clobbered registers. */ -#define TRACE_ENABLE_INTS bl .trace_hardirqs_on -#define TRACE_DISABLE_INTS bl .trace_hardirqs_off -#define TRACE_AND_RESTORE_IRQ_PARTIAL(en,skip) \ - cmpdi en,0; \ - bne 95f; \ - stb en,PACASOFTIRQEN(r13); \ - bl .trace_hardirqs_off; \ - b skip; \ -95: bl .trace_hardirqs_on; \ +#define TRACE_ENABLE_INTS TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_on) +#define TRACE_DISABLE_INTS TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_off) + +#define TRACE_AND_RESTORE_IRQ_PARTIAL(en,skip) \ + cmpdi en,0; \ + bne 95f; \ + stb en,PACASOFTIRQEN(r13); \ + TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_off) \ + b skip; \ +95: TRACE_WITH_FRAME_BUFFER(.trace_hardirqs_on) \ li en,1; #define TRACE_AND_RESTORE_IRQ(en) \ TRACE_AND_RESTORE_IRQ_PARTIAL(en,96f); \ - stb en,PACASOFTIRQEN(r13); \ + stb en,PACASOFTIRQEN(r13); \ 96: #else #define TRACE_ENABLE_INTS -- cgit v1.2.3 From 06ca2188eccbd7932636ac5bde2837297800480e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 22 Dec 2010 16:42:56 +0000 Subject: powerpc/ppc32/tracing: Add stack frame to calls of trace_hardirqs_on/off 32-bit variant of the previous patch for 64-bit: << When an interrupt occurs in userspace, we can call trace_hardirqs_on/off() With one level stack. But if we have irqsoff tracing enabled, it checks both CALLER_ADDR0 and CALLER_ADDR1. The second call goes two stack frames up. If this is from user space, then there may not exist a second stack.... >> Signed-off-by: Steven Rostedt Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/entry_32.S | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S index c22dc1ec1c94..56212bc0ab08 100644 --- a/arch/powerpc/kernel/entry_32.S +++ b/arch/powerpc/kernel/entry_32.S @@ -880,7 +880,18 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_TYPE_47x) */ andi. r10,r9,MSR_EE beq 1f + /* + * Since the ftrace irqsoff latency trace checks CALLER_ADDR1, + * which is the stack frame here, we need to force a stack frame + * in case we came from user space. + */ + stwu r1,-32(r1) + mflr r0 + stw r0,4(r1) + stwu r1,-32(r1) bl trace_hardirqs_on + lwz r1,0(r1) + lwz r1,0(r1) lwz r9,_MSR(r1) 1: #endif /* CONFIG_TRACE_IRQFLAGS */ -- cgit v1.2.3 From 63c3b9d71bb66f4b62059713201f066ad9415bb5 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 24 Dec 2010 20:03:56 +0000 Subject: powerpc/spufs: Use simple_write_to_buffer Simplify several write fileoperations for spufs by using simple_write_to_buffer(). Signed-off-by: Akinobu Mita Acked-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/spufs/file.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 02f7b113a31b..3c7c3f82d842 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -219,24 +219,17 @@ spufs_mem_write(struct file *file, const char __user *buffer, loff_t pos = *ppos; int ret; - if (pos < 0) - return -EINVAL; if (pos > LS_SIZE) return -EFBIG; - if (size > LS_SIZE - pos) - size = LS_SIZE - pos; ret = spu_acquire(ctx); if (ret) return ret; local_store = ctx->ops->get_ls(ctx); - ret = copy_from_user(local_store + pos, buffer, size); + size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size); spu_release(ctx); - if (ret) - return -EFAULT; - *ppos = pos + size; return size; } @@ -574,18 +567,15 @@ spufs_regs_write(struct file *file, const char __user *buffer, if (*pos >= sizeof(lscsa->gprs)) return -EFBIG; - size = min_t(ssize_t, sizeof(lscsa->gprs) - *pos, size); - *pos += size; - ret = spu_acquire_saved(ctx); if (ret) return ret; - ret = copy_from_user((char *)lscsa->gprs + *pos - size, - buffer, size) ? -EFAULT : size; + size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos, + buffer, size); spu_release_saved(ctx); - return ret; + return size; } static const struct file_operations spufs_regs_fops = { @@ -630,18 +620,15 @@ spufs_fpcr_write(struct file *file, const char __user * buffer, if (*pos >= sizeof(lscsa->fpcr)) return -EFBIG; - size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); - ret = spu_acquire_saved(ctx); if (ret) return ret; - *pos += size; - ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, - buffer, size) ? -EFAULT : size; + size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos, + buffer, size); spu_release_saved(ctx); - return ret; + return size; } static const struct file_operations spufs_fpcr_fops = { -- cgit v1.2.3 From 4c4a5cf64bfa0ddbe4145e6ca18a62eb70706edd Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Fri, 24 Dec 2010 20:03:59 +0000 Subject: powerpc/rtas_flash: Use simple_read_from_buffer Simplify read file operation for /proc/powerpc/rtas/* interface by using simple_read_from_buffer. Signed-off-by: Akinobu Mita Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/rtas_flash.c | 53 +++++----------------------------------- 1 file changed, 6 insertions(+), 47 deletions(-) diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index 2b442e6c21e6..bf5f5ce3a7bd 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c @@ -256,31 +256,16 @@ static ssize_t rtas_flash_read(struct file *file, char __user *buf, struct proc_dir_entry *dp = PDE(file->f_path.dentry->d_inode); struct rtas_update_flash_t *uf; char msg[RTAS_MSG_MAXLEN]; - int msglen; - uf = (struct rtas_update_flash_t *) dp->data; + uf = dp->data; if (!strcmp(dp->name, FIRMWARE_FLASH_NAME)) { get_flash_status_msg(uf->status, msg); } else { /* FIRMWARE_UPDATE_NAME */ sprintf(msg, "%d\n", uf->status); } - msglen = strlen(msg); - if (msglen > count) - msglen = count; - - if (ppos && *ppos != 0) - return 0; /* be cheap */ - - if (!access_ok(VERIFY_WRITE, buf, msglen)) - return -EINVAL; - if (copy_to_user(buf, msg, msglen)) - return -EFAULT; - - if (ppos) - *ppos = msglen; - return msglen; + return simple_read_from_buffer(buf, count, ppos, msg, strlen(msg)); } /* constructor for flash_block_cache */ @@ -394,26 +379,13 @@ static ssize_t manage_flash_read(struct file *file, char __user *buf, char msg[RTAS_MSG_MAXLEN]; int msglen; - args_buf = (struct rtas_manage_flash_t *) dp->data; + args_buf = dp->data; if (args_buf == NULL) return 0; msglen = sprintf(msg, "%d\n", args_buf->status); - if (msglen > count) - msglen = count; - if (ppos && *ppos != 0) - return 0; /* be cheap */ - - if (!access_ok(VERIFY_WRITE, buf, msglen)) - return -EINVAL; - - if (copy_to_user(buf, msg, msglen)) - return -EFAULT; - - if (ppos) - *ppos = msglen; - return msglen; + return simple_read_from_buffer(buf, count, ppos, msg, msglen); } static ssize_t manage_flash_write(struct file *file, const char __user *buf, @@ -495,24 +467,11 @@ static ssize_t validate_flash_read(struct file *file, char __user *buf, char msg[RTAS_MSG_MAXLEN]; int msglen; - args_buf = (struct rtas_validate_flash_t *) dp->data; + args_buf = dp->data; - if (ppos && *ppos != 0) - return 0; /* be cheap */ - msglen = get_validate_flash_msg(args_buf, msg); - if (msglen > count) - msglen = count; - - if (!access_ok(VERIFY_WRITE, buf, msglen)) - return -EINVAL; - - if (copy_to_user(buf, msg, msglen)) - return -EFAULT; - if (ppos) - *ppos = msglen; - return msglen; + return simple_read_from_buffer(buf, count, ppos, msg, msglen); } static ssize_t validate_flash_write(struct file *file, const char __user *buf, -- cgit v1.2.3 From f32be0c54057faac90e0b5e9e13fa9f8fab127ac Mon Sep 17 00:00:00 2001 From: roel kluin Date: Fri, 31 Dec 2010 04:57:46 +0000 Subject: powerpc/macintosh: Fix wrong test in fan_{read,write}_reg() Fix error test in fan_{read,write}_reg() Signed-off-by: Roel Kluin Signed-off-by: Benjamin Herrenschmidt --- drivers/macintosh/therm_pm72.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 2e041fd0a00c..f3a29f264db9 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -443,7 +443,7 @@ static int fan_read_reg(int reg, unsigned char *buf, int nb) tries = 0; for (;;) { nr = i2c_master_recv(fcu, buf, nb); - if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100) + if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100) break; msleep(10); ++tries; @@ -464,7 +464,7 @@ static int fan_write_reg(int reg, const unsigned char *ptr, int nb) tries = 0; for (;;) { nw = i2c_master_send(fcu, buf, nb); - if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100) + if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) break; msleep(10); ++tries; -- cgit v1.2.3 From b18ae08deac23187e4a22a8c94a1a473be8e8c93 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jan 2011 03:49:25 +0000 Subject: powerpc/cell: Use system_wq in cpufreq_spudemand With cmwq, there's no reason to use a separate workqueue in cpufreq_spudemand. Use system_wq instead. The work items are already sync canceled on stop, so it's already guaranteed that no work is running when spu_gov_exit() is entered. Signed-off-by: Tejun Heo Cc: Arnd Bergmann Cc: linuxppc-dev@lists.ozlabs.org Cc: Dave Jones Cc: cpufreq@vger.kernel.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/time.c | 25 ++++++++++++++++++++----- arch/powerpc/platforms/cell/cpufreq_spudemand.c | 20 +++----------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 09e4dea4a85a..09d31dbf43f9 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -265,11 +265,26 @@ void accumulate_stolen_time(void) { u64 sst, ust; - sst = scan_dispatch_log(get_paca()->starttime_user); - ust = scan_dispatch_log(get_paca()->starttime); - get_paca()->system_time -= sst; - get_paca()->user_time -= ust; - get_paca()->stolen_time += ust + sst; + u8 save_soft_enabled = local_paca->soft_enabled; + u8 save_hard_enabled = local_paca->hard_enabled; + + /* We are called early in the exception entry, before + * soft/hard_enabled are sync'ed to the expected state + * for the exception. We are hard disabled but the PACA + * needs to reflect that so various debug stuff doesn't + * complain + */ + local_paca->soft_enabled = 0; + local_paca->hard_enabled = 0; + + sst = scan_dispatch_log(local_paca->starttime_user); + ust = scan_dispatch_log(local_paca->starttime); + local_paca->system_time -= sst; + local_paca->user_time -= ust; + local_paca->stolen_time += ust + sst; + + local_paca->soft_enabled = save_soft_enabled; + local_paca->hard_enabled = save_hard_enabled; } static inline u64 calculate_stolen_time(u64 stop_tb) diff --git a/arch/powerpc/platforms/cell/cpufreq_spudemand.c b/arch/powerpc/platforms/cell/cpufreq_spudemand.c index 968c1c0b4d5b..d809836bcf5f 100644 --- a/arch/powerpc/platforms/cell/cpufreq_spudemand.c +++ b/arch/powerpc/platforms/cell/cpufreq_spudemand.c @@ -39,8 +39,6 @@ struct spu_gov_info_struct { }; static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); -static struct workqueue_struct *kspugov_wq; - static int calc_freq(struct spu_gov_info_struct *info) { int cpu; @@ -71,14 +69,14 @@ static void spu_gov_work(struct work_struct *work) __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); delay = usecs_to_jiffies(info->poll_int); - queue_delayed_work_on(info->policy->cpu, kspugov_wq, &info->work, delay); + schedule_delayed_work_on(info->policy->cpu, &info->work, delay); } static void spu_gov_init_work(struct spu_gov_info_struct *info) { int delay = usecs_to_jiffies(info->poll_int); INIT_DELAYED_WORK_DEFERRABLE(&info->work, spu_gov_work); - queue_delayed_work_on(info->policy->cpu, kspugov_wq, &info->work, delay); + schedule_delayed_work_on(info->policy->cpu, &info->work, delay); } static void spu_gov_cancel_work(struct spu_gov_info_struct *info) @@ -152,27 +150,15 @@ static int __init spu_gov_init(void) { int ret; - kspugov_wq = create_workqueue("kspugov"); - if (!kspugov_wq) { - printk(KERN_ERR "creation of kspugov failed\n"); - ret = -EFAULT; - goto out; - } - ret = cpufreq_register_governor(&spu_governor); - if (ret) { + if (ret) printk(KERN_ERR "registration of governor failed\n"); - destroy_workqueue(kspugov_wq); - goto out; - } -out: return ret; } static void __exit spu_gov_exit(void) { cpufreq_unregister_governor(&spu_governor); - destroy_workqueue(kspugov_wq); } -- cgit v1.2.3 From 50266a1f8a6618f2e477635cfcf457b5da8d5a68 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 17:54:15 +0000 Subject: powerpc/kexec: Move all ppc_md kexec function pointers together Move all the kexec handlers together. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 8433d36619a1..b2e8d69280cc 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -116,9 +116,6 @@ struct machdep_calls { * If for some reason there is no irq, but the interrupt * shouldn't be counted as spurious, return NO_IRQ_IGNORE. */ unsigned int (*get_irq)(void); -#ifdef CONFIG_KEXEC - void (*kexec_cpu_down)(int crash_shutdown, int secondary); -#endif /* PCI stuff */ /* Called after scanning the bus, before allocating resources */ @@ -235,6 +232,8 @@ struct machdep_calls { void (*machine_shutdown)(void); #ifdef CONFIG_KEXEC + void (*kexec_cpu_down)(int crash_shutdown, int secondary); + /* Called to do the minimal shutdown needed to run a kexec'd kernel * to run successfully. * XXX Should we move this one out of kexec scope? -- cgit v1.2.3 From 619b267724e8cc41072d650ae28114851dd8bf56 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 17:54:58 +0000 Subject: powerpc/kexec: Remove ppc_md.machine_kexec_cleanup No one uses ppc_md.machine_kexec_cleanup, so remove it. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 3 --- arch/powerpc/kernel/machine_kexec.c | 2 -- 2 files changed, 5 deletions(-) diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index b2e8d69280cc..67cd82e4e27f 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -247,9 +247,6 @@ struct machdep_calls { */ int (*machine_kexec_prepare)(struct kimage *image); - /* Called to handle any machine specific cleanup on image */ - void (*machine_kexec_cleanup)(struct kimage *image); - /* Called to perform the _real_ kexec. * Do NOT allocate memory or fail here. We are past the point of * no return. diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index df7e20c191cd..fae67642499d 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -65,8 +65,6 @@ int machine_kexec_prepare(struct kimage *image) void machine_kexec_cleanup(struct kimage *image) { - if (ppc_md.machine_kexec_cleanup) - ppc_md.machine_kexec_cleanup(image); } void arch_crash_save_vmcoreinfo(void) -- cgit v1.2.3 From c94868788cf26d5ad816646dd9af3cdf6fbd92fd Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 17:55:36 +0000 Subject: powerpc/kexec: Remove ppc_md.machine_kexec No one uses ppc_md.machine_kexec, so remove it. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 6 ------ arch/powerpc/kernel/machine_kexec.c | 5 +---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 67cd82e4e27f..cb8139617442 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -246,12 +246,6 @@ struct machdep_calls { * claims to support kexec. */ int (*machine_kexec_prepare)(struct kimage *image); - - /* Called to perform the _real_ kexec. - * Do NOT allocate memory or fail here. We are past the point of - * no return. - */ - void (*machine_kexec)(struct kimage *image); #endif /* CONFIG_KEXEC */ #ifdef CONFIG_SUSPEND diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index fae67642499d..43836aa8348e 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -85,10 +85,7 @@ void arch_crash_save_vmcoreinfo(void) */ void machine_kexec(struct kimage *image) { - if (ppc_md.machine_kexec) - ppc_md.machine_kexec(image); - else - default_machine_kexec(image); + default_machine_kexec(image); /* Fall back to normal restart if we're still alive. */ machine_restart(NULL); -- cgit v1.2.3 From c1f784e553490a2602567666fc79ff142cb4413b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 17:56:09 +0000 Subject: powerpc/kdump: Remove ppc_md.machine_crash_shutdown No one uses ppc_md.machine_crash_shutdown, so remove it. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/machdep.h | 6 ------ arch/powerpc/kernel/machine_kexec.c | 5 +---- arch/powerpc/platforms/cell/qpace_setup.c | 1 - arch/powerpc/platforms/pseries/kexec.c | 1 - 4 files changed, 1 insertion(+), 12 deletions(-) diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index cb8139617442..991d5998d6be 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -234,12 +234,6 @@ struct machdep_calls { #ifdef CONFIG_KEXEC void (*kexec_cpu_down)(int crash_shutdown, int secondary); - /* Called to do the minimal shutdown needed to run a kexec'd kernel - * to run successfully. - * XXX Should we move this one out of kexec scope? - */ - void (*machine_crash_shutdown)(struct pt_regs *regs); - /* Called to do what every setup is needed on image and the * reboot code buffer. Returns 0 on success. * Provide your own (maybe dummy) implementation if your platform diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index 43836aa8348e..3427b2faa19a 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -44,10 +44,7 @@ void machine_kexec_mask_interrupts(void) { void machine_crash_shutdown(struct pt_regs *regs) { - if (ppc_md.machine_crash_shutdown) - ppc_md.machine_crash_shutdown(regs); - else - default_machine_crash_shutdown(regs); + default_machine_crash_shutdown(regs); } /* diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c index 1b5749042756..7970eec4c3a2 100644 --- a/arch/powerpc/platforms/cell/qpace_setup.c +++ b/arch/powerpc/platforms/cell/qpace_setup.c @@ -148,6 +148,5 @@ define_machine(qpace) { #ifdef CONFIG_KEXEC .machine_kexec = default_machine_kexec, .machine_kexec_prepare = default_machine_kexec_prepare, - .machine_crash_shutdown = default_machine_crash_shutdown, #endif }; diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c index 53cbd53d8740..3d6279267221 100644 --- a/arch/powerpc/platforms/pseries/kexec.c +++ b/arch/powerpc/platforms/pseries/kexec.c @@ -66,7 +66,6 @@ static int __init pseries_kexec_setup(void) { ppc_md.machine_kexec = default_machine_kexec; ppc_md.machine_kexec_prepare = default_machine_kexec_prepare; - ppc_md.machine_crash_shutdown = default_machine_crash_shutdown; return 0; } -- cgit v1.2.3 From 2bb44d628c5d7a3ac0882aad01c423268d5d3b23 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 17:57:03 +0000 Subject: powerpc/kexec: Don't initialise kexec hooks to default handlers There's no need to initialise ppc_md.machine_kexec and ppc_md.machine_kexec_prepare to the default handlers. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/qpace_setup.c | 4 ---- arch/powerpc/platforms/pseries/kexec.c | 9 --------- 2 files changed, 13 deletions(-) diff --git a/arch/powerpc/platforms/cell/qpace_setup.c b/arch/powerpc/platforms/cell/qpace_setup.c index 7970eec4c3a2..d31c594cfdf3 100644 --- a/arch/powerpc/platforms/cell/qpace_setup.c +++ b/arch/powerpc/platforms/cell/qpace_setup.c @@ -145,8 +145,4 @@ define_machine(qpace) { .calibrate_decr = generic_calibrate_decr, .progress = qpace_progress, .init_IRQ = iic_init_IRQ, -#ifdef CONFIG_KEXEC - .machine_kexec = default_machine_kexec, - .machine_kexec_prepare = default_machine_kexec_prepare, -#endif }; diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c index 3d6279267221..77d38a5e2ff9 100644 --- a/arch/powerpc/platforms/pseries/kexec.c +++ b/arch/powerpc/platforms/pseries/kexec.c @@ -61,12 +61,3 @@ void __init setup_kexec_cpu_down_xics(void) { ppc_md.kexec_cpu_down = pseries_kexec_cpu_down_xics; } - -static int __init pseries_kexec_setup(void) -{ - ppc_md.machine_kexec = default_machine_kexec; - ppc_md.machine_kexec_prepare = default_machine_kexec_prepare; - - return 0; -} -machine_device_initcall(pseries, pseries_kexec_setup); -- cgit v1.2.3 From c6baabfb84b37e7df52363c6c95c90f3b34afb56 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 17:58:36 +0000 Subject: powerpc/kexec: Remove empty ppc_md.machine_kexec_prepare We check for a valid handler before calling ppc_md.machine_kexec_prepare so we can just remove these empty handlers. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/embedded6xx/gamecube.c | 11 ----------- arch/powerpc/platforms/embedded6xx/wii.c | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/arch/powerpc/platforms/embedded6xx/gamecube.c b/arch/powerpc/platforms/embedded6xx/gamecube.c index 1106fd99627f..a138e14bad2e 100644 --- a/arch/powerpc/platforms/embedded6xx/gamecube.c +++ b/arch/powerpc/platforms/embedded6xx/gamecube.c @@ -75,14 +75,6 @@ static void gamecube_shutdown(void) flipper_quiesce(); } -#ifdef CONFIG_KEXEC -static int gamecube_kexec_prepare(struct kimage *image) -{ - return 0; -} -#endif /* CONFIG_KEXEC */ - - define_machine(gamecube) { .name = "gamecube", .probe = gamecube_probe, @@ -95,9 +87,6 @@ define_machine(gamecube) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, .machine_shutdown = gamecube_shutdown, -#ifdef CONFIG_KEXEC - .machine_kexec_prepare = gamecube_kexec_prepare, -#endif }; diff --git a/arch/powerpc/platforms/embedded6xx/wii.c b/arch/powerpc/platforms/embedded6xx/wii.c index 649473a729b8..1b5dc1a2e145 100644 --- a/arch/powerpc/platforms/embedded6xx/wii.c +++ b/arch/powerpc/platforms/embedded6xx/wii.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -226,13 +225,6 @@ static void wii_shutdown(void) flipper_quiesce(); } -#ifdef CONFIG_KEXEC -static int wii_machine_kexec_prepare(struct kimage *image) -{ - return 0; -} -#endif /* CONFIG_KEXEC */ - define_machine(wii) { .name = "wii", .probe = wii_probe, @@ -246,9 +238,6 @@ define_machine(wii) { .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, .machine_shutdown = wii_shutdown, -#ifdef CONFIG_KEXEC - .machine_kexec_prepare = wii_machine_kexec_prepare, -#endif }; static struct of_device_id wii_of_bus[] = { -- cgit v1.2.3 From 158d5b5e3654938cca963eec82cd10af49cda17f Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Fri, 21 Jan 2011 13:43:59 +1100 Subject: powerpc/kdump: Move crash_kexec_stop_spus to kdump crash handler Use the crash handler hooks to run the SPU stop code, just like we do for ehea and cell RAS code. While I'm here I noticed "CPUSs reliabally" so fix the spelling MISTAKESs reliabally. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/spu.h | 8 ---- arch/powerpc/kernel/crash.c | 72 +--------------------------------- arch/powerpc/platforms/cell/spu_base.c | 70 +++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 78 deletions(-) diff --git a/arch/powerpc/include/asm/spu.h b/arch/powerpc/include/asm/spu.h index 0ab8d869e3d6..0c8b35d75232 100644 --- a/arch/powerpc/include/asm/spu.h +++ b/arch/powerpc/include/asm/spu.h @@ -203,14 +203,6 @@ void spu_irq_setaffinity(struct spu *spu, int cpu); void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa, void *code, int code_size); -#ifdef CONFIG_KEXEC -void crash_register_spus(struct list_head *list); -#else -static inline void crash_register_spus(struct list_head *list) -{ -} -#endif - extern void spu_invalidate_slbs(struct spu *spu); extern void spu_associate_mm(struct spu *spu, struct mm_struct *mm); int spu_64k_pages_available(void); diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c index 832c8c4db254..3d569e2aff18 100644 --- a/arch/powerpc/kernel/crash.c +++ b/arch/powerpc/kernel/crash.c @@ -48,7 +48,7 @@ int crashing_cpu = -1; static cpumask_t cpus_in_crash = CPU_MASK_NONE; cpumask_t cpus_in_sr = CPU_MASK_NONE; -#define CRASH_HANDLER_MAX 2 +#define CRASH_HANDLER_MAX 3 /* NULL terminated list of shutdown handles */ static crash_shutdown_t crash_shutdown_handles[CRASH_HANDLER_MAX+1]; static DEFINE_SPINLOCK(crash_handlers_lock); @@ -125,7 +125,7 @@ static void crash_kexec_prepare_cpus(int cpu) smp_wmb(); /* - * FIXME: Until we will have the way to stop other CPUSs reliabally, + * FIXME: Until we will have the way to stop other CPUs reliably, * the crash CPU will send an IPI and wait for other CPUs to * respond. * Delay of at least 10 seconds. @@ -254,72 +254,6 @@ void crash_kexec_secondary(struct pt_regs *regs) cpus_in_sr = CPU_MASK_NONE; } #endif -#ifdef CONFIG_SPU_BASE - -#include -#include - -struct crash_spu_info { - struct spu *spu; - u32 saved_spu_runcntl_RW; - u32 saved_spu_status_R; - u32 saved_spu_npc_RW; - u64 saved_mfc_sr1_RW; - u64 saved_mfc_dar; - u64 saved_mfc_dsisr; -}; - -#define CRASH_NUM_SPUS 16 /* Enough for current hardware */ -static struct crash_spu_info crash_spu_info[CRASH_NUM_SPUS]; - -static void crash_kexec_stop_spus(void) -{ - struct spu *spu; - int i; - u64 tmp; - - for (i = 0; i < CRASH_NUM_SPUS; i++) { - if (!crash_spu_info[i].spu) - continue; - - spu = crash_spu_info[i].spu; - - crash_spu_info[i].saved_spu_runcntl_RW = - in_be32(&spu->problem->spu_runcntl_RW); - crash_spu_info[i].saved_spu_status_R = - in_be32(&spu->problem->spu_status_R); - crash_spu_info[i].saved_spu_npc_RW = - in_be32(&spu->problem->spu_npc_RW); - - crash_spu_info[i].saved_mfc_dar = spu_mfc_dar_get(spu); - crash_spu_info[i].saved_mfc_dsisr = spu_mfc_dsisr_get(spu); - tmp = spu_mfc_sr1_get(spu); - crash_spu_info[i].saved_mfc_sr1_RW = tmp; - - tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK; - spu_mfc_sr1_set(spu, tmp); - - __delay(200); - } -} - -void crash_register_spus(struct list_head *list) -{ - struct spu *spu; - - list_for_each_entry(spu, list, full_list) { - if (WARN_ON(spu->number >= CRASH_NUM_SPUS)) - continue; - - crash_spu_info[spu->number].spu = spu; - } -} - -#else -static inline void crash_kexec_stop_spus(void) -{ -} -#endif /* CONFIG_SPU_BASE */ /* * Register a function to be called on shutdown. Only use this if you @@ -439,8 +373,6 @@ void default_machine_crash_shutdown(struct pt_regs *regs) crash_shutdown_cpu = -1; __debugger_fault_handler = old_handler; - crash_kexec_stop_spus(); - if (ppc_md.kexec_cpu_down) ppc_md.kexec_cpu_down(1, 0); } diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 8547e86bfb42..acfaccea5f4f 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -37,6 +37,7 @@ #include #include #include +#include const struct spu_management_ops *spu_management_ops; EXPORT_SYMBOL_GPL(spu_management_ops); @@ -727,6 +728,75 @@ static ssize_t spu_stat_show(struct sys_device *sysdev, static SYSDEV_ATTR(stat, 0644, spu_stat_show, NULL); +#ifdef CONFIG_KEXEC + +struct crash_spu_info { + struct spu *spu; + u32 saved_spu_runcntl_RW; + u32 saved_spu_status_R; + u32 saved_spu_npc_RW; + u64 saved_mfc_sr1_RW; + u64 saved_mfc_dar; + u64 saved_mfc_dsisr; +}; + +#define CRASH_NUM_SPUS 16 /* Enough for current hardware */ +static struct crash_spu_info crash_spu_info[CRASH_NUM_SPUS]; + +static void crash_kexec_stop_spus(void) +{ + struct spu *spu; + int i; + u64 tmp; + + for (i = 0; i < CRASH_NUM_SPUS; i++) { + if (!crash_spu_info[i].spu) + continue; + + spu = crash_spu_info[i].spu; + + crash_spu_info[i].saved_spu_runcntl_RW = + in_be32(&spu->problem->spu_runcntl_RW); + crash_spu_info[i].saved_spu_status_R = + in_be32(&spu->problem->spu_status_R); + crash_spu_info[i].saved_spu_npc_RW = + in_be32(&spu->problem->spu_npc_RW); + + crash_spu_info[i].saved_mfc_dar = spu_mfc_dar_get(spu); + crash_spu_info[i].saved_mfc_dsisr = spu_mfc_dsisr_get(spu); + tmp = spu_mfc_sr1_get(spu); + crash_spu_info[i].saved_mfc_sr1_RW = tmp; + + tmp &= ~MFC_STATE1_MASTER_RUN_CONTROL_MASK; + spu_mfc_sr1_set(spu, tmp); + + __delay(200); + } +} + +static void crash_register_spus(struct list_head *list) +{ + struct spu *spu; + int ret; + + list_for_each_entry(spu, list, full_list) { + if (WARN_ON(spu->number >= CRASH_NUM_SPUS)) + continue; + + crash_spu_info[spu->number].spu = spu; + } + + ret = crash_shutdown_register(&crash_kexec_stop_spus); + if (ret) + printk(KERN_ERR "Could not register SPU crash handler"); +} + +#else +static inline void crash_register_spus(struct list_head *list) +{ +} +#endif + static int __init init_spu_base(void) { int i, ret = 0; -- cgit v1.2.3 From ac4414e4d3024a3c9ac5f54a734ac77dd7edfdb3 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 6 Jan 2011 18:00:36 +0000 Subject: powerpc/kdump: Disable ftrace during kexec We should disable ftrace during kexec, some of the tracers are very invasive and we do not want them going off while doing the low level work of swapping one kernel out for another. This mirrors what we do on x86. Even though we cannot return from a kexec on powerpc (since we do not implement CONFIG_KEXEC_JUMP), add the restore code in case we do one day. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/machine_kexec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index 3427b2faa19a..49a170af8145 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -82,8 +83,14 @@ void arch_crash_save_vmcoreinfo(void) */ void machine_kexec(struct kimage *image) { + int save_ftrace_enabled; + + save_ftrace_enabled = __ftrace_enabled_save(); + default_machine_kexec(image); + __ftrace_enabled_restore(save_ftrace_enabled); + /* Fall back to normal restart if we're still alive. */ machine_restart(NULL); for(;;); -- cgit v1.2.3 From 7071854bb248926b85141d791f9fa17901a6fa4b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:44:30 +0000 Subject: powerpc: Print 32 bits of DSISR in show_regs We were printing 64 bits of DSISR in show_regs even though it is 32 bit. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 84906d3fc860..7a1d5cb76932 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -631,7 +631,7 @@ void show_regs(struct pt_regs * regs) #ifdef CONFIG_PPC_ADV_DEBUG_REGS printk("DEAR: "REG", ESR: "REG"\n", regs->dar, regs->dsisr); #else - printk("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr); + printk("DAR: "REG", DSISR: %08lx\n", regs->dar, regs->dsisr); #endif printk("TASK = %p[%d] '%s' THREAD: %p", current, task_pid_nr(current), current->comm, task_thread_info(current)); -- cgit v1.2.3 From a443506b8598bbd784cfe403ad1db2c7083ff465 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:45:31 +0000 Subject: powerpc: Don't force MSR_RI in machine_check_exception We should never force MSR_RI on. If we take a machine check with MSR_RI off then we have no chance of recovering safely. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/traps.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 1b2cdc8eec90..6865002df6ce 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -627,7 +627,6 @@ void machine_check_exception(struct pt_regs *regs) return; if (user_mode(regs)) { - regs->msr |= MSR_RI; _exception(SIGBUS, regs, BUS_ADRERR, regs->nip); return; } @@ -643,10 +642,8 @@ void machine_check_exception(struct pt_regs *regs) return; #endif - if (debugger_fault_handler(regs)) { - regs->msr |= MSR_RI; + if (debugger_fault_handler(regs)) return; - } if (check_io_access(regs)) return; -- cgit v1.2.3 From 3f9793e6b66efcf196974ee4f0d62de6282eb41b Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:46:29 +0000 Subject: powerpc: Never halt RTAS error logging after receiving an unrecoverable machine check Newer versions of the System p firwmare send a partial RTAS error log in the machine check handler with a more detailed response appearing sometime later via check event. This means at machine check time we do not have enough information to ascertain exactly what went on. Furthermore, I have found the RTAS error logs in the machine check handler contain no useful information, so halting on them makes little sense. If we want to halt it would make more sense to do it following the error log received sometime later via check event. In light of this, never halt the error log in the pseries machine check handler. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/ras.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index a4fc6da87c2e..048e25711c6b 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -281,7 +281,7 @@ static int recover_mce(struct pt_regs *regs, struct rtas_error_log * err) nonfatal = 1; } - log_error((char *)err, ERR_TYPE_RTAS_LOG, !nonfatal); + log_error((char *)err, ERR_TYPE_RTAS_LOG, 0); return nonfatal; } -- cgit v1.2.3 From dfb5509f8f49ffd52922060c830978ed5a9b9731 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:47:20 +0000 Subject: powerpc: Remove duplicate debugger hook in machine_check_exception We are calling debugger_fault_handler twice in machine_check_exception. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/traps.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 6865002df6ce..9e24224962d9 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -648,8 +648,6 @@ void machine_check_exception(struct pt_regs *regs) if (check_io_access(regs)) return; - if (debugger_fault_handler(regs)) - return; die("Machine check", regs, SIGBUS); /* Must die if the interrupt is not recoverable */ -- cgit v1.2.3 From e49b1fae0ba4d06b29bd753a961abb447566bf4a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:48:14 +0000 Subject: powerpc: Don't silently handle machine checks from userspace If a machine check comes from userspace we send a SIGBUS to the task and fail to printk anything. If we are taking machine checks due to bad hardware we want to know about it right away. Furthermore if we don't complain loudly then it will look a lot like a bug in the userspace application, potentially causing a lot of confusion. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/traps.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 9e24224962d9..bd74fac169be 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -626,11 +626,6 @@ void machine_check_exception(struct pt_regs *regs) if (recover > 0) return; - if (user_mode(regs)) { - _exception(SIGBUS, regs, BUS_ADRERR, regs->nip); - return; - } - #if defined(CONFIG_8xx) && defined(CONFIG_PCI) /* the qspan pci read routines can cause machine checks -- Cort * -- cgit v1.2.3 From d47d1d8af52e37bcf9059dd86878474e5ccc9c2a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:49:19 +0000 Subject: powerpc: Rework pseries machine check handler Rework pseries machine check handler: - If MSR_RI isn't set, we cannot recover even if the machine check was fully recovered - Rename nonfatal to recovered - Handle RTAS_DISP_LIMITED_RECOVERY - Use BUS_MCEERR_AR instead of BUS_ADRERR - Don't check all the RTAS error log fields when receiving a synchronous machine check. Recent versions of the pseries firmware do not fill them in during a machine check and instead send a follow up error log with the detailed information. If we see a synchronous machine check, and we came from userspace then kill the task. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/ras.c | 48 ++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index 048e25711c6b..d194150cf342 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -259,31 +259,43 @@ int pSeries_system_reset_exception(struct pt_regs *regs) * Return 1 if corrected (or delivered a signal). * Return 0 if there is nothing we can do. */ -static int recover_mce(struct pt_regs *regs, struct rtas_error_log * err) +static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err) { - int nonfatal = 0; + int recovered = 0; - if (err->disposition == RTAS_DISP_FULLY_RECOVERED) { + if (!(regs->msr & MSR_RI)) { + /* If MSR_RI isn't set, we cannot recover */ + recovered = 0; + + } else if (err->disposition == RTAS_DISP_FULLY_RECOVERED) { /* Platform corrected itself */ - nonfatal = 1; - } else if ((regs->msr & MSR_RI) && - user_mode(regs) && - err->severity == RTAS_SEVERITY_ERROR_SYNC && - err->disposition == RTAS_DISP_NOT_RECOVERED && - err->target == RTAS_TARGET_MEMORY && - err->type == RTAS_TYPE_ECC_UNCORR && - !(current->pid == 0 || is_global_init(current))) { - /* Kill off a user process with an ECC error */ - printk(KERN_ERR "MCE: uncorrectable ecc error for pid %d\n", - current->pid); - /* XXX something better for ECC error? */ - _exception(SIGBUS, regs, BUS_ADRERR, regs->nip); - nonfatal = 1; + recovered = 1; + + } else if (err->disposition == RTAS_DISP_LIMITED_RECOVERY) { + /* Platform corrected itself but could be degraded */ + printk(KERN_ERR "MCE: limited recovery, system may " + "be degraded\n"); + recovered = 1; + + } else if (user_mode(regs) && !is_global_init(current) && + err->severity == RTAS_SEVERITY_ERROR_SYNC) { + + /* + * If we received a synchronous error when in userspace + * kill the task. Firmware may report details of the fail + * asynchronously, so we can't rely on the target and type + * fields being valid here. + */ + printk(KERN_ERR "MCE: uncorrectable error, killing task " + "%s:%d\n", current->comm, current->pid); + + _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); + recovered = 1; } log_error((char *)err, ERR_TYPE_RTAS_LOG, 0); - return nonfatal; + return recovered; } /* -- cgit v1.2.3 From d368514c3097a48a109b6ba64e12047106c7473d Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:50:51 +0000 Subject: powerpc: Fix corruption when grabbing FWNMI data The FWNMI code uses a global buffer without any locks to read the RTAS error information. If two CPUs take a machine check at once then we will corrupt this buffer. Since most FWNMI rtas messages are not of the extended type, we can create a 64bit percpu buffer and use it where possible. If we do receive an extended RTAS log then we fall back to the old behaviour of using the global buffer. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/ras.c | 52 ++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index d194150cf342..c55d7ad9c648 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -54,7 +54,8 @@ static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX]; static DEFINE_SPINLOCK(ras_log_buf_lock); -static char mce_data_buf[RTAS_ERROR_LOG_MAX]; +static char global_mce_data_buf[RTAS_ERROR_LOG_MAX]; +static DEFINE_PER_CPU(__u64, mce_data_buf); static int ras_get_sensor_state_token; static int ras_check_exception_token; @@ -196,12 +197,24 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -/* Get the error information for errors coming through the +/* + * Some versions of FWNMI place the buffer inside the 4kB page starting at + * 0x7000. Other versions place it inside the rtas buffer. We check both. + */ +#define VALID_FWNMI_BUFFER(A) \ + ((((A) >= 0x7000) && ((A) < 0x7ff0)) || \ + (((A) >= rtas.base) && ((A) < (rtas.base + rtas.size - 16)))) + +/* + * Get the error information for errors coming through the * FWNMI vectors. The pt_regs' r3 will be updated to reflect * the actual r3 if possible, and a ptr to the error log entry * will be returned if found. * - * The mce_data_buf does not have any locks or protection around it, + * If the RTAS error is not of the extended type, then we put it in a per + * cpu 64bit buffer. If it is the extended type we use global_mce_data_buf. + * + * The global_mce_data_buf does not have any locks or protection around it, * if a second machine check comes in, or a system reset is done * before we have logged the error, then we will get corruption in the * error log. This is preferable over holding off on calling @@ -210,20 +223,31 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) */ static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs) { - unsigned long errdata = regs->gpr[3]; - struct rtas_error_log *errhdr = NULL; unsigned long *savep; + struct rtas_error_log *h, *errhdr = NULL; - if ((errdata >= 0x7000 && errdata < 0x7fff0) || - (errdata >= rtas.base && errdata < rtas.base + rtas.size - 16)) { - savep = __va(errdata); - regs->gpr[3] = savep[0]; /* restore original r3 */ - memset(mce_data_buf, 0, RTAS_ERROR_LOG_MAX); - memcpy(mce_data_buf, (char *)(savep + 1), RTAS_ERROR_LOG_MAX); - errhdr = (struct rtas_error_log *)mce_data_buf; + if (!VALID_FWNMI_BUFFER(regs->gpr[3])) { + printk(KERN_ERR "FWNMI: corrupt r3\n"); + return NULL; + } + + savep = __va(regs->gpr[3]); + regs->gpr[3] = savep[0]; /* restore original r3 */ + + /* If it isn't an extended log we can use the per cpu 64bit buffer */ + h = (struct rtas_error_log *)&savep[1]; + if (!h->extended) { + memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64)); + errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf); } else { - printk("FWNMI: corrupt r3\n"); + int len; + + len = max_t(int, 8+h->extended_log_length, RTAS_ERROR_LOG_MAX); + memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX); + memcpy(global_mce_data_buf, h, len); + errhdr = (struct rtas_error_log *)global_mce_data_buf; } + return errhdr; } @@ -235,7 +259,7 @@ static void fwnmi_release_errinfo(void) { int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL); if (ret != 0) - printk("FWNMI: nmi-interlock failed: %d\n", ret); + printk(KERN_ERR "FWNMI: nmi-interlock failed: %d\n", ret); } int pSeries_system_reset_exception(struct pt_regs *regs) -- cgit v1.2.3 From 7f32c9c60089bfdb5eeeaae6e9f59046db998234 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:51:31 +0000 Subject: powerpc: Check RTAS extended log flag before checking length The spec suggests we should first check the extended log flag before checking the length field. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/rtasd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c index 0438f819fe6b..049dbecb5dbc 100644 --- a/arch/powerpc/kernel/rtasd.c +++ b/arch/powerpc/kernel/rtasd.c @@ -160,7 +160,7 @@ static int log_rtas_len(char * buf) /* rtas fixed header */ len = 8; err = (struct rtas_error_log *)buf; - if (err->extended_log_length) { + if (err->extended && err->extended_log_length) { /* extended header */ len += err->extended_log_length; -- cgit v1.2.3 From fbe754ca3a7579131ecec47c4fa10c40994b3804 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 11 Jan 2011 19:52:31 +0000 Subject: powerpc: machine_check_generic is wrong on 64bit Decoding machine checks is CPU specific and so machine_check_generic doesn't do the right thing on 64bit chips. Luckily we never call into this code because we call ppc_md.machine_check_exception instead if available. Since we check cur_cpu_spec->machine_check before calling it, we may as well remove machine_check_generic from 64bit archs. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/cputable.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index be5ab18b03b5..8d74a24c5502 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -116,7 +116,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/power3", .oprofile_type = PPC_OPROFILE_RS64, - .machine_check = machine_check_generic, .platform = "power3", }, { /* Power3+ */ @@ -132,7 +131,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/power3", .oprofile_type = PPC_OPROFILE_RS64, - .machine_check = machine_check_generic, .platform = "power3", }, { /* Northstar */ @@ -148,7 +146,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/rs64", .oprofile_type = PPC_OPROFILE_RS64, - .machine_check = machine_check_generic, .platform = "rs64", }, { /* Pulsar */ @@ -164,7 +161,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/rs64", .oprofile_type = PPC_OPROFILE_RS64, - .machine_check = machine_check_generic, .platform = "rs64", }, { /* I-star */ @@ -180,7 +176,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/rs64", .oprofile_type = PPC_OPROFILE_RS64, - .machine_check = machine_check_generic, .platform = "rs64", }, { /* S-star */ @@ -196,7 +191,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/rs64", .oprofile_type = PPC_OPROFILE_RS64, - .machine_check = machine_check_generic, .platform = "rs64", }, { /* Power4 */ @@ -212,7 +206,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/power4", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "power4", }, { /* Power4+ */ @@ -228,7 +221,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/power4", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "power4", }, { /* PPC970 */ @@ -247,7 +239,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_restore = __restore_cpu_ppc970, .oprofile_cpu_type = "ppc64/970", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "ppc970", }, { /* PPC970FX */ @@ -266,7 +257,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_restore = __restore_cpu_ppc970, .oprofile_cpu_type = "ppc64/970", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "ppc970", }, { /* PPC970MP DD1.0 - no DEEPNAP, use regular 970 init */ @@ -285,7 +275,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_restore = __restore_cpu_ppc970, .oprofile_cpu_type = "ppc64/970MP", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "ppc970", }, { /* PPC970MP */ @@ -304,7 +293,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_restore = __restore_cpu_ppc970, .oprofile_cpu_type = "ppc64/970MP", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "ppc970", }, { /* PPC970GX */ @@ -322,7 +310,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_setup = __setup_cpu_ppc970, .oprofile_cpu_type = "ppc64/970", .oprofile_type = PPC_OPROFILE_POWER4, - .machine_check = machine_check_generic, .platform = "ppc970", }, { /* Power5 GR */ @@ -343,7 +330,6 @@ static struct cpu_spec __initdata cpu_specs[] = { */ .oprofile_mmcra_sihv = MMCRA_SIHV, .oprofile_mmcra_sipr = MMCRA_SIPR, - .machine_check = machine_check_generic, .platform = "power5", }, { /* Power5++ */ @@ -360,7 +346,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .oprofile_type = PPC_OPROFILE_POWER4, .oprofile_mmcra_sihv = MMCRA_SIHV, .oprofile_mmcra_sipr = MMCRA_SIPR, - .machine_check = machine_check_generic, .platform = "power5+", }, { /* Power5 GS */ @@ -378,7 +363,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .oprofile_type = PPC_OPROFILE_POWER4, .oprofile_mmcra_sihv = MMCRA_SIHV, .oprofile_mmcra_sipr = MMCRA_SIPR, - .machine_check = machine_check_generic, .platform = "power5+", }, { /* POWER6 in P5+ mode; 2.04-compliant processor */ @@ -390,7 +374,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .mmu_features = MMU_FTR_HPTE_TABLE, .icache_bsize = 128, .dcache_bsize = 128, - .machine_check = machine_check_generic, .oprofile_cpu_type = "ppc64/ibm-compat-v1", .oprofile_type = PPC_OPROFILE_POWER4, .platform = "power5+", @@ -413,7 +396,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .oprofile_mmcra_sipr = POWER6_MMCRA_SIPR, .oprofile_mmcra_clear = POWER6_MMCRA_THRM | POWER6_MMCRA_OTHER, - .machine_check = machine_check_generic, .platform = "power6x", }, { /* 2.05-compliant processor, i.e. Power6 "architected" mode */ @@ -425,7 +407,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .mmu_features = MMU_FTR_HPTE_TABLE, .icache_bsize = 128, .dcache_bsize = 128, - .machine_check = machine_check_generic, .oprofile_cpu_type = "ppc64/ibm-compat-v1", .oprofile_type = PPC_OPROFILE_POWER4, .platform = "power6", @@ -440,7 +421,6 @@ static struct cpu_spec __initdata cpu_specs[] = { MMU_FTR_TLBIE_206, .icache_bsize = 128, .dcache_bsize = 128, - .machine_check = machine_check_generic, .oprofile_type = PPC_OPROFILE_POWER4, .oprofile_cpu_type = "ppc64/ibm-compat-v1", .platform = "power7", @@ -492,7 +472,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .pmc_type = PPC_PMC_IBM, .oprofile_cpu_type = "ppc64/cell-be", .oprofile_type = PPC_OPROFILE_CELL, - .machine_check = machine_check_generic, .platform = "ppc-cell-be", }, { /* PA Semi PA6T */ @@ -510,7 +489,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .cpu_restore = __restore_cpu_pa6t, .oprofile_cpu_type = "ppc64/pa6t", .oprofile_type = PPC_OPROFILE_PA6T, - .machine_check = machine_check_generic, .platform = "pa6t", }, { /* default match */ @@ -524,7 +502,6 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 128, .num_pmcs = 6, .pmc_type = PPC_PMC_IBM, - .machine_check = machine_check_generic, .platform = "power4", } #endif /* CONFIG_PPC_BOOK3S_64 */ -- cgit v1.2.3 From 4d9ef89dee13e964ea8b064d82ff55cf36209237 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 8 Jan 2011 14:24:01 +0000 Subject: powerpc/boot/dts: Install dts from the right directory The dts-installed variable is initialised using a wildcard path that will be expanded relative to the build directory. Use the existing variable dtstree to generate an absolute wildcard path that will work when building in a separate directory. Reported-by: Gerhard Pircher Signed-off-by: Ben Hutchings Tested-by: Gerhard Pircher [against 2.6.32] Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 96deec63bcf3..89178164af5e 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -368,7 +368,7 @@ INSTALL := install extra-installed := $(patsubst $(obj)/%, $(DESTDIR)$(WRAPPER_OBJDIR)/%, $(extra-y)) hostprogs-installed := $(patsubst %, $(DESTDIR)$(WRAPPER_BINDIR)/%, $(hostprogs-y)) wrapper-installed := $(DESTDIR)$(WRAPPER_BINDIR)/wrapper -dts-installed := $(patsubst $(obj)/dts/%, $(DESTDIR)$(WRAPPER_DTSDIR)/%, $(wildcard $(obj)/dts/*.dts)) +dts-installed := $(patsubst $(dtstree)/%, $(DESTDIR)$(WRAPPER_DTSDIR)/%, $(wildcard $(dtstree)/*.dts)) all-installed := $(extra-installed) $(hostprogs-installed) $(wrapper-installed) $(dts-installed) -- cgit v1.2.3 From 7d2dcd046d259d309b4e0da387a53660a8f146ad Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 12 Jan 2011 02:12:43 +0000 Subject: powerpc: Disable mcount tracers in pseries defconfig IRQSOFF_TRACER and STACK_TRACER force the kernel to be built with -pg which is a substantial overhead. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/pseries_defconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index f87f0e15cfa7..e30286c266fa 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -311,9 +311,7 @@ CONFIG_DEBUG_KERNEL=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set CONFIG_LATENCYTOP=y CONFIG_SYSCTL_SYSCALL_CHECK=y -CONFIG_IRQSOFF_TRACER=y CONFIG_SCHED_TRACER=y -CONFIG_STACK_TRACER=y CONFIG_BLK_DEV_IO_TRACE=y CONFIG_DEBUG_STACKOVERFLOW=y CONFIG_DEBUG_STACK_USAGE=y -- cgit v1.2.3 From ef4f7c2d075d09ed833dc086e07a0d1eee488168 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 12 Jan 2011 02:14:32 +0000 Subject: powerpc: Enable 64kB pages and 1024 threads in pseries config - Enable 64kB pages so it gets some regular testing. - The largest POWER7 has 1024 threads so bump NR_CPUS it to match. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/pseries_defconfig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index e30286c266fa..0a4e13d090d7 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -2,7 +2,7 @@ CONFIG_PPC64=y CONFIG_ALTIVEC=y CONFIG_VSX=y CONFIG_SMP=y -CONFIG_NR_CPUS=128 +CONFIG_NR_CPUS=1024 CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y @@ -45,6 +45,8 @@ CONFIG_KEXEC=y CONFIG_IRQ_ALL_CPUS=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTREMOVE=y +CONFIG_PPC_64K_PAGES=y +CONFIG_PPC_SUBPAGE_PROT=y CONFIG_SCHED_SMT=y CONFIG_HOTPLUG_PCI=m CONFIG_HOTPLUG_PCI_RPA=m -- cgit v1.2.3 From cb046de7581993e7d486b40a6878be2afa1b588f Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Thu, 13 Jan 2011 13:22:39 +0000 Subject: powerpc/pseries: Add BNX2=m to defconfig Upcoming servers will include a Broadcom NIC, add to the defconfig to increase testing coverage and make sure mainline builds come up with networking. Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/configs/pseries_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/pseries_defconfig b/arch/powerpc/configs/pseries_defconfig index 0a4e13d090d7..9c3f22c6cde1 100644 --- a/arch/powerpc/configs/pseries_defconfig +++ b/arch/powerpc/configs/pseries_defconfig @@ -186,6 +186,7 @@ CONFIG_ACENIC_OMIT_TIGON_I=y CONFIG_E1000=y CONFIG_E1000E=y CONFIG_TIGON3=y +CONFIG_BNX2=m CONFIG_CHELSIO_T1=m CONFIG_CHELSIO_T3=m CONFIG_EHEA=y -- cgit v1.2.3 From 8bfc5e36619e8464bf529137f834f55bd75de381 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 17 Jan 2011 12:10:41 +0000 Subject: powerpc/mpic: Fix mask/unmask timeout message Don't say that enable timed out when it was disable, and show which IRQ had the problem. Signed-off-by: Scott Wood Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/sysdev/mpic.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 7c1342618a30..b0c8469e5ddd 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -674,7 +674,8 @@ void mpic_unmask_irq(unsigned int irq) /* make sure mask gets to controller before we return to user */ do { if (!loops--) { - printk(KERN_ERR "mpic_enable_irq timeout\n"); + printk(KERN_ERR "%s: timeout on hwirq %u\n", + __func__, src); break; } } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); @@ -695,7 +696,8 @@ void mpic_mask_irq(unsigned int irq) /* make sure mask gets to controller before we return to user */ do { if (!loops--) { - printk(KERN_ERR "mpic_enable_irq timeout\n"); + printk(KERN_ERR "%s: timeout on hwirq %u\n", + __func__, src); break; } } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); -- cgit v1.2.3 From 9190b3b3208d052d98cb601fcc192f3f71a5658b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 20 Jan 2011 23:31:33 -0800 Subject: net_sched: accurate bytes/packets stats/rates In commit 44b8288308ac9d (net_sched: pfifo_head_drop problem), we fixed a problem with pfifo_head drops that incorrectly decreased sch->bstats.bytes and sch->bstats.packets Several qdiscs (CHOKe, SFQ, pfifo_head, ...) are able to drop a previously enqueued packet, and bstats cannot be changed, so bstats/rates are not accurate (over estimated) This patch changes the qdisc_bstats updates to be done at dequeue() time instead of enqueue() time. bstats counters no longer account for dropped frames, and rates are more correct, since enqueue() bursts dont have effect on dequeue() rate. Signed-off-by: Eric Dumazet Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/net/sch_generic.h | 8 +++++--- net/sched/sch_cbq.c | 3 +-- net/sched/sch_drr.c | 2 +- net/sched/sch_dsmark.c | 2 +- net/sched/sch_fifo.c | 5 +---- net/sched/sch_hfsc.c | 2 +- net/sched/sch_htb.c | 12 +++++------- net/sched/sch_multiq.c | 2 +- net/sched/sch_netem.c | 3 +-- net/sched/sch_prio.c | 2 +- net/sched/sch_red.c | 11 ++++++----- net/sched/sch_sfq.c | 5 ++--- net/sched/sch_tbf.c | 2 +- net/sched/sch_teql.c | 3 ++- 14 files changed, 29 insertions(+), 33 deletions(-) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index e9eee99d8b1f..160a407c1963 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -445,7 +445,6 @@ static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, { __skb_queue_tail(list, skb); sch->qstats.backlog += qdisc_pkt_len(skb); - qdisc_bstats_update(sch, skb); return NET_XMIT_SUCCESS; } @@ -460,8 +459,10 @@ static inline struct sk_buff *__qdisc_dequeue_head(struct Qdisc *sch, { struct sk_buff *skb = __skb_dequeue(list); - if (likely(skb != NULL)) + if (likely(skb != NULL)) { sch->qstats.backlog -= qdisc_pkt_len(skb); + qdisc_bstats_update(sch, skb); + } return skb; } @@ -474,10 +475,11 @@ static inline struct sk_buff *qdisc_dequeue_head(struct Qdisc *sch) static inline unsigned int __qdisc_queue_drop_head(struct Qdisc *sch, struct sk_buff_head *list) { - struct sk_buff *skb = __qdisc_dequeue_head(sch, list); + struct sk_buff *skb = __skb_dequeue(list); if (likely(skb != NULL)) { unsigned int len = qdisc_pkt_len(skb); + sch->qstats.backlog -= len; kfree_skb(skb); return len; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index c80d1c210c5d..5f63ec58942c 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -390,7 +390,6 @@ cbq_enqueue(struct sk_buff *skb, struct Qdisc *sch) ret = qdisc_enqueue(skb, cl->q); if (ret == NET_XMIT_SUCCESS) { sch->q.qlen++; - qdisc_bstats_update(sch, skb); cbq_mark_toplevel(q, cl); if (!cl->next_alive) cbq_activate_class(cl); @@ -649,7 +648,6 @@ static int cbq_reshape_fail(struct sk_buff *skb, struct Qdisc *child) ret = qdisc_enqueue(skb, cl->q); if (ret == NET_XMIT_SUCCESS) { sch->q.qlen++; - qdisc_bstats_update(sch, skb); if (!cl->next_alive) cbq_activate_class(cl); return 0; @@ -971,6 +969,7 @@ cbq_dequeue(struct Qdisc *sch) skb = cbq_dequeue_1(sch); if (skb) { + qdisc_bstats_update(sch, skb); sch->q.qlen--; sch->flags &= ~TCQ_F_THROTTLED; return skb; diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index de55e642eafc..6b7fe4a84f13 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -376,7 +376,6 @@ static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch) } bstats_update(&cl->bstats, skb); - qdisc_bstats_update(sch, skb); sch->q.qlen++; return err; @@ -403,6 +402,7 @@ static struct sk_buff *drr_dequeue(struct Qdisc *sch) skb = qdisc_dequeue_peeked(cl->qdisc); if (cl->qdisc->q.qlen == 0) list_del(&cl->alist); + qdisc_bstats_update(sch, skb); sch->q.qlen--; return skb; } diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 60f4bdd4408e..0f7bf3fdfea5 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -260,7 +260,6 @@ static int dsmark_enqueue(struct sk_buff *skb, struct Qdisc *sch) return err; } - qdisc_bstats_update(sch, skb); sch->q.qlen++; return NET_XMIT_SUCCESS; @@ -283,6 +282,7 @@ static struct sk_buff *dsmark_dequeue(struct Qdisc *sch) if (skb == NULL) return NULL; + qdisc_bstats_update(sch, skb); sch->q.qlen--; index = skb->tc_index & (p->indices - 1); diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index aa4d6337e43c..d468b479aa93 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -46,17 +46,14 @@ static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc* sch) static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc* sch) { - struct sk_buff *skb_head; struct fifo_sched_data *q = qdisc_priv(sch); if (likely(skb_queue_len(&sch->q) < q->limit)) return qdisc_enqueue_tail(skb, sch); /* queue full, remove one skb to fulfill the limit */ - skb_head = qdisc_dequeue_head(sch); + __qdisc_queue_drop_head(sch, &sch->q); sch->qstats.drops++; - kfree_skb(skb_head); - qdisc_enqueue_tail(skb, sch); return NET_XMIT_CN; diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 2e45791d4f6c..14a799de1c35 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1600,7 +1600,6 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch) set_active(cl, qdisc_pkt_len(skb)); bstats_update(&cl->bstats, skb); - qdisc_bstats_update(sch, skb); sch->q.qlen++; return NET_XMIT_SUCCESS; @@ -1666,6 +1665,7 @@ hfsc_dequeue(struct Qdisc *sch) } sch->flags &= ~TCQ_F_THROTTLED; + qdisc_bstats_update(sch, skb); sch->q.qlen--; return skb; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 984c1b0c6836..fc12fe6f5597 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -574,7 +574,6 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch) } sch->q.qlen++; - qdisc_bstats_update(sch, skb); return NET_XMIT_SUCCESS; } @@ -842,7 +841,7 @@ next: static struct sk_buff *htb_dequeue(struct Qdisc *sch) { - struct sk_buff *skb = NULL; + struct sk_buff *skb; struct htb_sched *q = qdisc_priv(sch); int level; psched_time_t next_event; @@ -851,6 +850,8 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) /* try to dequeue direct packets as high prio (!) to minimize cpu work */ skb = __skb_dequeue(&q->direct_queue); if (skb != NULL) { +ok: + qdisc_bstats_update(sch, skb); sch->flags &= ~TCQ_F_THROTTLED; sch->q.qlen--; return skb; @@ -884,11 +885,8 @@ static struct sk_buff *htb_dequeue(struct Qdisc *sch) int prio = ffz(m); m |= 1 << prio; skb = htb_dequeue_tree(q, prio, level); - if (likely(skb != NULL)) { - sch->q.qlen--; - sch->flags &= ~TCQ_F_THROTTLED; - goto fin; - } + if (likely(skb != NULL)) + goto ok; } } sch->qstats.overlimits++; diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 21f13da24763..436a2e75b322 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -83,7 +83,6 @@ multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch) ret = qdisc_enqueue(skb, qdisc); if (ret == NET_XMIT_SUCCESS) { - qdisc_bstats_update(sch, skb); sch->q.qlen++; return NET_XMIT_SUCCESS; } @@ -112,6 +111,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) qdisc = q->queues[q->curband]; skb = qdisc->dequeue(qdisc); if (skb) { + qdisc_bstats_update(sch, skb); sch->q.qlen--; return skb; } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 1c4bce863479..6a3006b38dc5 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -240,7 +240,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) if (likely(ret == NET_XMIT_SUCCESS)) { sch->q.qlen++; - qdisc_bstats_update(sch, skb); } else if (net_xmit_drop_count(ret)) { sch->qstats.drops++; } @@ -289,6 +288,7 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch) skb->tstamp.tv64 = 0; #endif pr_debug("netem_dequeue: return skb=%p\n", skb); + qdisc_bstats_update(sch, skb); sch->q.qlen--; return skb; } @@ -476,7 +476,6 @@ static int tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) __skb_queue_after(list, skb, nskb); sch->qstats.backlog += qdisc_pkt_len(nskb); - qdisc_bstats_update(sch, nskb); return NET_XMIT_SUCCESS; } diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 966158d49dd1..fbd710d619bf 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -84,7 +84,6 @@ prio_enqueue(struct sk_buff *skb, struct Qdisc *sch) ret = qdisc_enqueue(skb, qdisc); if (ret == NET_XMIT_SUCCESS) { - qdisc_bstats_update(sch, skb); sch->q.qlen++; return NET_XMIT_SUCCESS; } @@ -116,6 +115,7 @@ static struct sk_buff *prio_dequeue(struct Qdisc* sch) struct Qdisc *qdisc = q->queues[prio]; struct sk_buff *skb = qdisc->dequeue(qdisc); if (skb) { + qdisc_bstats_update(sch, skb); sch->q.qlen--; return skb; } diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index a6009c5a2c97..9f98dbd32d4c 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -94,7 +94,6 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc* sch) ret = qdisc_enqueue(skb, child); if (likely(ret == NET_XMIT_SUCCESS)) { - qdisc_bstats_update(sch, skb); sch->q.qlen++; } else if (net_xmit_drop_count(ret)) { q->stats.pdrop++; @@ -114,11 +113,13 @@ static struct sk_buff * red_dequeue(struct Qdisc* sch) struct Qdisc *child = q->qdisc; skb = child->dequeue(child); - if (skb) + if (skb) { + qdisc_bstats_update(sch, skb); sch->q.qlen--; - else if (!red_is_idling(&q->parms)) - red_start_of_idle_period(&q->parms); - + } else { + if (!red_is_idling(&q->parms)) + red_start_of_idle_period(&q->parms); + } return skb; } diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 239ec53a634d..edea8cefec6c 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -402,10 +402,8 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) q->tail = slot; slot->allot = q->scaled_quantum; } - if (++sch->q.qlen <= q->limit) { - qdisc_bstats_update(sch, skb); + if (++sch->q.qlen <= q->limit) return NET_XMIT_SUCCESS; - } sfq_drop(sch); return NET_XMIT_CN; @@ -445,6 +443,7 @@ next_slot: } skb = slot_dequeue_head(slot); sfq_dec(q, a); + qdisc_bstats_update(sch, skb); sch->q.qlen--; sch->qstats.backlog -= qdisc_pkt_len(skb); diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 77565e721811..e93165820c3f 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -134,7 +134,6 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch) } sch->q.qlen++; - qdisc_bstats_update(sch, skb); return NET_XMIT_SUCCESS; } @@ -187,6 +186,7 @@ static struct sk_buff *tbf_dequeue(struct Qdisc* sch) q->ptokens = ptoks; sch->q.qlen--; sch->flags &= ~TCQ_F_THROTTLED; + qdisc_bstats_update(sch, skb); return skb; } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 84ce48eadff4..d84e7329660f 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -87,7 +87,6 @@ teql_enqueue(struct sk_buff *skb, struct Qdisc* sch) if (q->q.qlen < dev->tx_queue_len) { __skb_queue_tail(&q->q, skb); - qdisc_bstats_update(sch, skb); return NET_XMIT_SUCCESS; } @@ -111,6 +110,8 @@ teql_dequeue(struct Qdisc* sch) dat->m->slaves = sch; netif_wake_queue(m); } + } else { + qdisc_bstats_update(sch, skb); } sch->q.qlen = dat->q.qlen + dat_queue->qdisc->q.qlen; return skb; -- cgit v1.2.3 From 12174aac376f2c9390c51e66995d38c9e5e94eff Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 12:15:29 +0100 Subject: h8300: Convert to new irq_chip functions No functional change, just straight forward conversion. Signed-off-by: Thomas Gleixner Cc: Yoshinori Sato Cc: Paul Mundt --- arch/h8300/kernel/irq.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/arch/h8300/kernel/irq.c b/arch/h8300/kernel/irq.c index c25dc2c2b1da..e23f34647889 100644 --- a/arch/h8300/kernel/irq.c +++ b/arch/h8300/kernel/irq.c @@ -38,34 +38,34 @@ static inline int is_ext_irq(unsigned int irq) return (irq >= EXT_IRQ0 && irq <= (EXT_IRQ0 + EXT_IRQS)); } -static void h8300_enable_irq(unsigned int irq) +static void h8300_enable_irq(struct irq_data *data) { - if (is_ext_irq(irq)) - IER_REGS |= 1 << (irq - EXT_IRQ0); + if (is_ext_irq(data->irq)) + IER_REGS |= 1 << (data->irq - EXT_IRQ0); } -static void h8300_disable_irq(unsigned int irq) +static void h8300_disable_irq(struct irq_data *data) { - if (is_ext_irq(irq)) - IER_REGS &= ~(1 << (irq - EXT_IRQ0)); + if (is_ext_irq(data->irq)) + IER_REGS &= ~(1 << (data->irq - EXT_IRQ0)); } static void h8300_end_irq(unsigned int irq) { } -static unsigned int h8300_startup_irq(unsigned int irq) +static unsigned int h8300_startup_irq(struct irq_data *data) { - if (is_ext_irq(irq)) - return h8300_enable_irq_pin(irq); + if (is_ext_irq(data->irq)) + return h8300_enable_irq_pin(data->irq); else return 0; } -static void h8300_shutdown_irq(unsigned int irq) +static void h8300_shutdown_irq(struct irq_data *data) { - if (is_ext_irq(irq)) - h8300_disable_irq_pin(irq); + if (is_ext_irq(data->irq)) + h8300_disable_irq_pin(data->irq); } /* @@ -73,11 +73,10 @@ static void h8300_shutdown_irq(unsigned int irq) */ struct irq_chip h8300irq_chip = { .name = "H8300-INTC", - .startup = h8300_startup_irq, - .shutdown = h8300_shutdown_irq, - .enable = h8300_enable_irq, - .disable = h8300_disable_irq, - .ack = NULL, + .irq_startup = h8300_startup_irq, + .irq_shutdown = h8300_shutdown_irq, + .irq_enable = h8300_enable_irq, + .irq_disable = h8300_disable_irq, .end = h8300_end_irq, }; -- cgit v1.2.3 From f9f91ef5e3a5bc1d4aa05b5d26c4d9e6dd930750 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 12:18:57 +0100 Subject: h8300: Convert interrupt handling to flow handler __do_IRQ is deprecated so h8300 needs to be converted to proper flow handling. The irq chip is simple and does not required any mask/ack/eoi functions, so we can use handle_simple_irq. Signed-off-by: Thomas Gleixner Cc: Yoshinori Sato Cc: Paul Mundt --- arch/h8300/kernel/irq.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/arch/h8300/kernel/irq.c b/arch/h8300/kernel/irq.c index e23f34647889..0c56735a2ff9 100644 --- a/arch/h8300/kernel/irq.c +++ b/arch/h8300/kernel/irq.c @@ -50,10 +50,6 @@ static void h8300_disable_irq(struct irq_data *data) IER_REGS &= ~(1 << (data->irq - EXT_IRQ0)); } -static void h8300_end_irq(unsigned int irq) -{ -} - static unsigned int h8300_startup_irq(struct irq_data *data) { if (is_ext_irq(data->irq)) @@ -77,7 +73,6 @@ struct irq_chip h8300irq_chip = { .irq_shutdown = h8300_shutdown_irq, .irq_enable = h8300_enable_irq, .irq_disable = h8300_disable_irq, - .end = h8300_end_irq, }; #if defined(CONFIG_RAMKERNEL) @@ -159,18 +154,14 @@ void __init init_IRQ(void) setup_vector(); - for (c = 0; c < NR_IRQS; c++) { - irq_desc[c].status = IRQ_DISABLED; - irq_desc[c].action = NULL; - irq_desc[c].depth = 1; - irq_desc[c].chip = &h8300irq_chip; - } + for (c = 0; c < NR_IRQS; c++) + set_irq_chip_and_handler(c, &h8300irq_chip, handle_simple_irq); } asmlinkage void do_IRQ(int irq) { irq_enter(); - __do_IRQ(irq); + generic_handle_irq(irq); irq_exit(); } -- cgit v1.2.3 From 51f3f15945aa2ccbc90baf17d912aed9981697b2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 12:26:32 +0100 Subject: h8300: Use generic irq Kconfig Switch to the generic irq Kconfig. h8300 has all irq chips converted to the new functions, so select the GENERIC_HARDIRQS_NO_DEPRECATED switch as well. Fixup the resulting fallout in show_interrupts(). Signed-off-by: Thomas Gleixner Cc: Yoshinori Sato Cc: Paul Mundt --- arch/h8300/Kconfig | 6 ++---- arch/h8300/kernel/irq.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 65f897d8c1e9..6df692d1475f 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -2,6 +2,8 @@ config H8300 bool default y select HAVE_IDE + select HAVE_GENERIC_HARDIRQS + select GENERIC_HARDIRQS_NO_DEPRECATED config SYMBOL_PREFIX string @@ -47,10 +49,6 @@ config GENERIC_HWEIGHT bool default y -config GENERIC_HARDIRQS - bool - default y - config GENERIC_CALIBRATE_DELAY bool default y diff --git a/arch/h8300/kernel/irq.c b/arch/h8300/kernel/irq.c index 0c56735a2ff9..7643d39925d6 100644 --- a/arch/h8300/kernel/irq.c +++ b/arch/h8300/kernel/irq.c @@ -182,7 +182,7 @@ int show_interrupts(struct seq_file *p, void *v) goto unlock; seq_printf(p, "%3d: ",i); seq_printf(p, "%10u ", kstat_irqs(i)); - seq_printf(p, " %14s", irq_desc[i].chip->name); + seq_printf(p, " %14s", irq_desc[i].irq_data.chip->name); seq_printf(p, "-%-8s", irq_desc[i].name); seq_printf(p, " %s", action->name); -- cgit v1.2.3 From c84077ac29978cbfb42dccbba3fee8523605e839 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 13:59:01 +0100 Subject: cris: Use irq handling wrapper Use the wrapper around __do_IRQ() so we can convert V10 and V32 seperately. Signed-off-by: Thomas Gleixner Cc: Mikael Starvik --- arch/cris/kernel/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/cris/kernel/irq.c b/arch/cris/kernel/irq.c index 469f7f9d62e0..b6117b464bed 100644 --- a/arch/cris/kernel/irq.c +++ b/arch/cris/kernel/irq.c @@ -93,8 +93,8 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs) printk("do_IRQ: stack overflow: %lX\n", sp); show_stack(NULL, (unsigned long *)sp); } - __do_IRQ(irq); - irq_exit(); + generic_handle_irq(irq); + irq_exit(); set_irq_regs(old_regs); } -- cgit v1.2.3 From f7a004baa202f919d060c2da250535b085813a4b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 13:54:54 +0100 Subject: cris: Convert V10 interrupt handling Convert the irq_chip functions and install handle_simple_irq for each interrupt. This converts V10 to the flow handling and lets us remove __do_IRQ(). Signed-off-by: Thomas Gleixner Cc: Mikael Starvik --- arch/cris/arch-v10/kernel/irq.c | 41 ++++++++++------------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c index a0c0df8be9c8..7328a7cf7449 100644 --- a/arch/cris/arch-v10/kernel/irq.c +++ b/arch/cris/arch-v10/kernel/irq.c @@ -104,43 +104,21 @@ static void (*interrupt[NR_IRQS])(void) = { IRQ31_interrupt }; -static void enable_crisv10_irq(unsigned int irq); - -static unsigned int startup_crisv10_irq(unsigned int irq) -{ - enable_crisv10_irq(irq); - return 0; -} - -#define shutdown_crisv10_irq disable_crisv10_irq - -static void enable_crisv10_irq(unsigned int irq) -{ - crisv10_unmask_irq(irq); -} - -static void disable_crisv10_irq(unsigned int irq) -{ - crisv10_mask_irq(irq); -} - -static void ack_crisv10_irq(unsigned int irq) +static void enable_crisv10_irq(struct irq_data *data) { + crisv10_unmask_irq(data->irq); } -static void end_crisv10_irq(unsigned int irq) +static void disable_crisv10_irq(struct irq_data *data) { + crisv10_mask_irq(data->irq); } static struct irq_chip crisv10_irq_type = { - .name = "CRISv10", - .startup = startup_crisv10_irq, - .shutdown = shutdown_crisv10_irq, - .enable = enable_crisv10_irq, - .disable = disable_crisv10_irq, - .ack = ack_crisv10_irq, - .end = end_crisv10_irq, - .set_affinity = NULL + .name = "CRISv10", + .irq_shutdown = disable_crisv10_irq, + .irq_enable = enable_crisv10_irq, + .irq_disable = disable_crisv10_irq, }; void weird_irq(void); @@ -221,7 +199,8 @@ init_IRQ(void) /* Initialize IRQ handler descriptors. */ for(i = 2; i < NR_IRQS; i++) { - irq_desc[i].chip = &crisv10_irq_type; + set_irq_desc_and_handler(i, &crisv10_irq_type, + handle_simple_irq); set_int_vector(i, interrupt[i]); } -- cgit v1.2.3 From 9af7503dbd9c4c5e11fcf253ac93fefc9793dff8 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 14:05:30 +0100 Subject: cris: Convert V32 interrupt handling Convert the irq chip functions and install handle_simple_irq for each interrupt to get rid of __do_IRQ() Signed-off-by: Thomas Gleixner Cc: Mikael Starvik --- arch/cris/arch-v32/kernel/irq.c | 50 +++++++++++++---------------------------- 1 file changed, 15 insertions(+), 35 deletions(-) diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c index 2ed48ae3d313..0ad9db5126c7 100644 --- a/arch/cris/arch-v32/kernel/irq.c +++ b/arch/cris/arch-v32/kernel/irq.c @@ -291,54 +291,33 @@ void crisv32_unmask_irq(int irq) } -static unsigned int startup_crisv32_irq(unsigned int irq) +static void enable_crisv32_irq(struct irq_data *data) { - crisv32_unmask_irq(irq); - return 0; -} - -static void shutdown_crisv32_irq(unsigned int irq) -{ - crisv32_mask_irq(irq); + crisv32_unmask_irq(data->irq); } -static void enable_crisv32_irq(unsigned int irq) +static void disable_crisv32_irq(struct irq_data *data) { - crisv32_unmask_irq(irq); + crisv32_mask_irq(data->irq); } -static void disable_crisv32_irq(unsigned int irq) -{ - crisv32_mask_irq(irq); -} - -static void ack_crisv32_irq(unsigned int irq) -{ -} - -static void end_crisv32_irq(unsigned int irq) -{ -} - -int set_affinity_crisv32_irq(unsigned int irq, const struct cpumask *dest) +static int set_affinity_crisv32_irq(struct irq_data *data, + const struct cpumask *dest, bool force) { unsigned long flags; + spin_lock_irqsave(&irq_lock, flags); - irq_allocations[irq - FIRST_IRQ].mask = *dest; + irq_allocations[data->irq - FIRST_IRQ].mask = *dest; spin_unlock_irqrestore(&irq_lock, flags); - return 0; } static struct irq_chip crisv32_irq_type = { - .name = "CRISv32", - .startup = startup_crisv32_irq, - .shutdown = shutdown_crisv32_irq, - .enable = enable_crisv32_irq, - .disable = disable_crisv32_irq, - .ack = ack_crisv32_irq, - .end = end_crisv32_irq, - .set_affinity = set_affinity_crisv32_irq + .name = "CRISv32", + .irq_shutdown = disable_crisv32_irq, + .irq_enable = enable_crisv32_irq, + .irq_disable = disable_crisv32_irq, + .irq_set_affinity = set_affinity_crisv32_irq, }; void @@ -472,7 +451,8 @@ init_IRQ(void) /* Point all IRQ's to bad handlers. */ for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) { - irq_desc[j].chip = &crisv32_irq_type; + set_irq_chip_and_handler(j, &crisv32_irq_type, + handle_simple_irq); set_exception_vector(i, interrupt[j]); } -- cgit v1.2.3 From 30139785e615c97497864a89511db02839fa7bf5 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 14:20:13 +0100 Subject: cris: Use generic irq Kconfig Use the generic irq Kconfig. Select GENERIC_HARDIRQS_NO_DEPRECATED as we have converted all irq_chip functions. Fix the fallout in show_interrupts(). Signed-off-by: Thomas Gleixner Cc: Mikael Starvik --- arch/cris/Kconfig | 6 ++---- arch/cris/kernel/irq.c | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index 613e62831c55..0a7a4c11d8b1 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -54,6 +54,8 @@ config CRIS bool default y select HAVE_IDE + select HAVE_GENERIC_HARDIRQS + select GENERIC_HARDIRQS_NO_DEPRECATED config HZ int @@ -67,10 +69,6 @@ menu "General setup" source "fs/Kconfig.binfmt" -config GENERIC_HARDIRQS - bool - default y - config ETRAX_CMDLINE string "Kernel command line" default "root=/dev/mtdblock3" diff --git a/arch/cris/kernel/irq.c b/arch/cris/kernel/irq.c index b6117b464bed..c346952f06dc 100644 --- a/arch/cris/kernel/irq.c +++ b/arch/cris/kernel/irq.c @@ -62,7 +62,7 @@ int show_interrupts(struct seq_file *p, void *v) for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); #endif - seq_printf(p, " %14s", irq_desc[i].chip->name); + seq_printf(p, " %14s", irq_desc[i].irq_data.chip->name); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) -- cgit v1.2.3 From 863018a7a24a29c0862c62e70c89244fdd5a08bf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 22 Sep 2010 19:13:16 +0200 Subject: m32r: Cleanup direct irq_desc access The irq descriptors are already initialized by the generic code. Remove the redundant init code and set the irq chip with the proper accessor function. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/kernel/irq.c | 10 +++-- arch/m32r/platforms/m32104ut/setup.c | 20 ++-------- arch/m32r/platforms/m32700ut/setup.c | 74 ++++++++---------------------------- arch/m32r/platforms/mappi/setup.c | 41 ++++---------------- arch/m32r/platforms/mappi2/setup.c | 50 +++++------------------- arch/m32r/platforms/mappi3/setup.c | 50 +++++------------------- arch/m32r/platforms/oaks32r/setup.c | 30 +++------------ arch/m32r/platforms/opsput/setup.c | 70 +++++++--------------------------- arch/m32r/platforms/usrv/setup.c | 45 +++++----------------- 9 files changed, 83 insertions(+), 307 deletions(-) diff --git a/arch/m32r/kernel/irq.c b/arch/m32r/kernel/irq.c index 7db26f1f082d..f745c1287f3a 100644 --- a/arch/m32r/kernel/irq.c +++ b/arch/m32r/kernel/irq.c @@ -40,8 +40,10 @@ int show_interrupts(struct seq_file *p, void *v) } if (i < NR_IRQS) { - raw_spin_lock_irqsave(&irq_desc[i].lock, flags); - action = irq_desc[i].action; + struct irq_desc *desc = irq_to_desc(i); + + raw_spin_lock_irqsave(&desc->lock, flags); + action = desc->action; if (!action) goto skip; seq_printf(p, "%3d: ",i); @@ -51,7 +53,7 @@ int show_interrupts(struct seq_file *p, void *v) for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_irqs_cpu(i, j)); #endif - seq_printf(p, " %14s", irq_desc[i].chip->name); + seq_printf(p, " %14s", desc->irq_data.chip->name); seq_printf(p, " %s", action->name); for (action=action->next; action; action = action->next) @@ -59,7 +61,7 @@ int show_interrupts(struct seq_file *p, void *v) seq_putc(p, '\n'); skip: - raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags); + raw_spin_unlock_irqrestore(&desc->lock, flags); } return 0; } diff --git a/arch/m32r/platforms/m32104ut/setup.c b/arch/m32r/platforms/m32104ut/setup.c index 402a59d7219b..962f38e58acc 100644 --- a/arch/m32r/platforms/m32104ut/setup.c +++ b/arch/m32r/platforms/m32104ut/setup.c @@ -85,36 +85,24 @@ void __init init_IRQ(void) #if defined(CONFIG_SMC91X) /* INT#0: LAN controller on M32104UT-LAN (SMC91C111)*/ - irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT0].chip = &m32104ut_irq_type; - irq_desc[M32R_IRQ_INT0].action = 0; - irq_desc[M32R_IRQ_INT0].depth = 1; + set_irq_chip(M32R_IRQ_INT0, &m32104ut_irq_type); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD11; /* "H" level sense */ disable_m32104ut_irq(M32R_IRQ_INT0); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &m32104ut_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &m32104ut_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_m32104ut_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &m32104ut_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &m32104ut_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = M32R_ICUCR_IEN; disable_m32104ut_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &m32104ut_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &m32104ut_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = M32R_ICUCR_IEN; disable_m32104ut_irq(M32R_IRQ_SIO0_S); #endif /* CONFIG_SERIAL_M32R_SIO */ diff --git a/arch/m32r/platforms/m32700ut/setup.c b/arch/m32r/platforms/m32700ut/setup.c index 80b1a026795a..433bcbca2c3a 100644 --- a/arch/m32r/platforms/m32700ut/setup.c +++ b/arch/m32r/platforms/m32700ut/setup.c @@ -299,101 +299,65 @@ void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT#0: LAN controller on M32700UT-LAN (SMC91C111)*/ - irq_desc[M32700UT_LAN_IRQ_LAN].status = IRQ_DISABLED; - irq_desc[M32700UT_LAN_IRQ_LAN].chip = &m32700ut_lanpld_irq_type; - irq_desc[M32700UT_LAN_IRQ_LAN].action = 0; - irq_desc[M32700UT_LAN_IRQ_LAN].depth = 1; /* disable nested irq */ + set_irq_chip(M32700UT_LAN_IRQ_LAN, &m32700ut_lanpld_irq_type); lanpld_icu_data[irq2lanpldirq(M32700UT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ disable_m32700ut_lanpld_irq(M32700UT_LAN_IRQ_LAN); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &m32700ut_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_m32700ut_irq(M32R_IRQ_MFT2); /* SIO0 : receive */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &m32700ut_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO0_R); /* SIO0 : send */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &m32700ut_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO0_S); /* SIO1 : receive */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = 0; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &m32700ut_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO1_R); /* SIO1 : send */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = 0; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &m32700ut_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO1_S); /* DMA1 : */ - irq_desc[M32R_IRQ_DMA1].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_DMA1].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_DMA1].action = 0; - irq_desc[M32R_IRQ_DMA1].depth = 1; + set_irq_chip(M32R_IRQ_DMA1, &m32700ut_irq_type); icu_data[M32R_IRQ_DMA1].icucr = 0; disable_m32700ut_irq(M32R_IRQ_DMA1); #ifdef CONFIG_SERIAL_M32R_PLDSIO /* INT#1: SIO0 Receive on PLD */ - irq_desc[PLD_IRQ_SIO0_RCV].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_SIO0_RCV].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_SIO0_RCV].action = 0; - irq_desc[PLD_IRQ_SIO0_RCV].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_SIO0_RCV, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_m32700ut_pld_irq(PLD_IRQ_SIO0_RCV); /* INT#1: SIO0 Send on PLD */ - irq_desc[PLD_IRQ_SIO0_SND].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_SIO0_SND].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_SIO0_SND].action = 0; - irq_desc[PLD_IRQ_SIO0_SND].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_SIO0_SND, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_m32700ut_pld_irq(PLD_IRQ_SIO0_SND); #endif /* CONFIG_SERIAL_M32R_PLDSIO */ /* INT#1: CFC IREQ on PLD */ - irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFIREQ].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_CFIREQ].action = 0; - irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFIREQ, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_CFIREQ); /* INT#1: CFC Insert on PLD */ - irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_INSERT].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_CFC_INSERT].action = 0; - irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_INSERT, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ disable_m32700ut_pld_irq(PLD_IRQ_CFC_INSERT); /* INT#1: CFC Eject on PLD */ - irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_EJECT].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_CFC_EJECT].action = 0; - irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_EJECT, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ disable_m32700ut_pld_irq(PLD_IRQ_CFC_EJECT); @@ -413,13 +377,10 @@ void __init init_IRQ(void) #if defined(CONFIG_USB) outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ + set_irq_chip(M32700UT_LCD_IRQ_USB_INT1, &m32700ut_lcdpld_irq_type); - irq_desc[M32700UT_LCD_IRQ_USB_INT1].status = IRQ_DISABLED; - irq_desc[M32700UT_LCD_IRQ_USB_INT1].chip = &m32700ut_lcdpld_irq_type; - irq_desc[M32700UT_LCD_IRQ_USB_INT1].action = 0; - irq_desc[M32700UT_LCD_IRQ_USB_INT1].depth = 1; - lcdpld_icu_data[irq2lcdpldirq(M32700UT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ - disable_m32700ut_lcdpld_irq(M32700UT_LCD_IRQ_USB_INT1); + lcdpld_icu_data[irq2lcdpldirq(M32700UT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ + disable_m32700ut_lcdpld_irq(M32700UT_LCD_IRQ_USB_INT1); #endif /* * INT2# is used for BAT, USB, AUDIO @@ -432,10 +393,7 @@ void __init init_IRQ(void) /* * INT3# is used for AR */ - irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT3].chip = &m32700ut_irq_type; - irq_desc[M32R_IRQ_INT3].action = 0; - irq_desc[M32R_IRQ_INT3].depth = 1; + set_irq_chip(M32R_IRQ_INT3, &m32700ut_irq_type); icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_m32700ut_irq(M32R_IRQ_INT3); #endif /* CONFIG_VIDEO_M32R_AR */ diff --git a/arch/m32r/platforms/mappi/setup.c b/arch/m32r/platforms/mappi/setup.c index ea00c84d6b1b..bc3fdaf21ca2 100644 --- a/arch/m32r/platforms/mappi/setup.c +++ b/arch/m32r/platforms/mappi/setup.c @@ -45,7 +45,6 @@ static void mask_and_ack_mappi(unsigned int irq) static void end_mappi_irq(unsigned int irq) { - if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) enable_mappi_irq(irq); } @@ -85,70 +84,46 @@ void __init init_IRQ(void) #ifdef CONFIG_NE2000 /* INT0 : LAN controller (RTL8019AS) */ - irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT0].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_INT0].action = NULL; - irq_desc[M32R_IRQ_INT0].depth = 1; + set_irq_chip(M32R_IRQ_INT0, &mappi_irq_type); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; disable_mappi_irq(M32R_IRQ_INT0); #endif /* CONFIG_M32R_NE2000 */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_MFT2].action = NULL; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &mappi_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = NULL; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &mappi_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = NULL; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &mappi_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = NULL; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &mappi_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = NULL; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &mappi_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_SERIAL_M32R_SIO */ #if defined(CONFIG_M32R_PCC) /* INT1 : pccard0 interrupt */ - irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT1].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_INT1].action = NULL; - irq_desc[M32R_IRQ_INT1].depth = 1; + set_irq_chip(M32R_IRQ_INT1, &mappi_irq_type); icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; disable_mappi_irq(M32R_IRQ_INT1); /* INT2 : pccard1 interrupt */ - irq_desc[M32R_IRQ_INT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT2].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_INT2].action = NULL; - irq_desc[M32R_IRQ_INT2].depth = 1; + set_irq_chip(M32R_IRQ_INT2, &mappi_irq_type); icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; disable_mappi_irq(M32R_IRQ_INT2); #endif /* CONFIG_M32RPCC */ diff --git a/arch/m32r/platforms/mappi2/setup.c b/arch/m32r/platforms/mappi2/setup.c index c049376d0270..87bba1e01719 100644 --- a/arch/m32r/platforms/mappi2/setup.c +++ b/arch/m32r/platforms/mappi2/setup.c @@ -85,87 +85,57 @@ void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT0 : LAN controller (SMC91111) */ - irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT0].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_INT0].action = 0; - irq_desc[M32R_IRQ_INT0].depth = 1; + set_irq_chip(M32R_IRQ_INT0, &mappi2_irq_type); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi2_irq(M32R_IRQ_INT0); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &mappi2_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi2_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &mappi2_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &mappi2_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = 0; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &mappi2_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = 0; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &mappi2_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_M32R_USE_DBG_CONSOLE */ #if defined(CONFIG_USB) /* INT1 : USB Host controller interrupt */ - irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT1].chip = &mappi2_irq_type; - irq_desc[M32R_IRQ_INT1].action = 0; - irq_desc[M32R_IRQ_INT1].depth = 1; + set_irq_chip(M32R_IRQ_INT1, &mappi2_irq_type); icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD01; disable_mappi2_irq(M32R_IRQ_INT1); #endif /* CONFIG_USB */ /* ICUCR40: CFC IREQ */ - irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFIREQ].chip = &mappi2_irq_type; - irq_desc[PLD_IRQ_CFIREQ].action = 0; - irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFIREQ, &mappi2_irq_type); icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; disable_mappi2_irq(PLD_IRQ_CFIREQ); #if defined(CONFIG_M32R_CFC) /* ICUCR41: CFC Insert */ - irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_INSERT].chip = &mappi2_irq_type; - irq_desc[PLD_IRQ_CFC_INSERT].action = 0; - irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_INSERT, &mappi2_irq_type); icu_data[PLD_IRQ_CFC_INSERT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; disable_mappi2_irq(PLD_IRQ_CFC_INSERT); /* ICUCR42: CFC Eject */ - irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_EJECT].chip = &mappi2_irq_type; - irq_desc[PLD_IRQ_CFC_EJECT].action = 0; - irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_EJECT, &mappi2_irq_type); icu_data[PLD_IRQ_CFC_EJECT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi2_irq(PLD_IRQ_CFC_EJECT); #endif /* CONFIG_MAPPI2_CFC */ diff --git a/arch/m32r/platforms/mappi3/setup.c b/arch/m32r/platforms/mappi3/setup.c index 882de25c6e8c..74098142cb2c 100644 --- a/arch/m32r/platforms/mappi3/setup.c +++ b/arch/m32r/platforms/mappi3/setup.c @@ -85,89 +85,59 @@ void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT0 : LAN controller (SMC91111) */ - irq_desc[M32R_IRQ_INT0].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT0].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_INT0].action = 0; - irq_desc[M32R_IRQ_INT0].depth = 1; + set_irq_chip(M32R_IRQ_INT0, &mappi3_irq_type); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi3_irq(M32R_IRQ_INT0); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &mappi3_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi3_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &mappi3_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &mappi3_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = 0; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &mappi3_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = 0; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &mappi3_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_M32R_USE_DBG_CONSOLE */ #if defined(CONFIG_USB) /* INT1 : USB Host controller interrupt */ - irq_desc[M32R_IRQ_INT1].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT1].chip = &mappi3_irq_type; - irq_desc[M32R_IRQ_INT1].action = 0; - irq_desc[M32R_IRQ_INT1].depth = 1; + set_irq_chip(M32R_IRQ_INT1, &mappi3_irq_type); icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD01; disable_mappi3_irq(M32R_IRQ_INT1); #endif /* CONFIG_USB */ /* CFC IREQ */ - irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFIREQ].chip = &mappi3_irq_type; - irq_desc[PLD_IRQ_CFIREQ].action = 0; - irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFIREQ, &mappi3_irq_type); icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; disable_mappi3_irq(PLD_IRQ_CFIREQ); #if defined(CONFIG_M32R_CFC) /* ICUCR41: CFC Insert & eject */ - irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_INSERT].chip = &mappi3_irq_type; - irq_desc[PLD_IRQ_CFC_INSERT].action = 0; - irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_INSERT, &mappi3_irq_type); icu_data[PLD_IRQ_CFC_INSERT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; disable_mappi3_irq(PLD_IRQ_CFC_INSERT); #endif /* CONFIG_M32R_CFC */ /* IDE IREQ */ - irq_desc[PLD_IRQ_IDEIREQ].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_IDEIREQ].chip = &mappi3_irq_type; - irq_desc[PLD_IRQ_IDEIREQ].action = 0; - irq_desc[PLD_IRQ_IDEIREQ].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_IDEIREQ, &mappi3_irq_type); icu_data[PLD_IRQ_IDEIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi3_irq(PLD_IRQ_IDEIREQ); diff --git a/arch/m32r/platforms/oaks32r/setup.c b/arch/m32r/platforms/oaks32r/setup.c index d11d93bf74f5..43bf5a0c9515 100644 --- a/arch/m32r/platforms/oaks32r/setup.c +++ b/arch/m32r/platforms/oaks32r/setup.c @@ -83,52 +83,34 @@ void __init init_IRQ(void) #ifdef CONFIG_NE2000 /* INT3 : LAN controller (RTL8019AS) */ - irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT3].chip = &oaks32r_irq_type; - irq_desc[M32R_IRQ_INT3].action = 0; - irq_desc[M32R_IRQ_INT3].depth = 1; + set_irq_chip(M32R_IRQ_INT3, &oaks32r_irq_type); icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_oaks32r_irq(M32R_IRQ_INT3); #endif /* CONFIG_M32R_NE2000 */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &oaks32r_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &oaks32r_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_oaks32r_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &oaks32r_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &oaks32r_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &oaks32r_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &oaks32r_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &oaks32r_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = 0; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &oaks32r_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &oaks32r_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = 0; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &oaks32r_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_SERIAL_M32R_SIO */ diff --git a/arch/m32r/platforms/opsput/setup.c b/arch/m32r/platforms/opsput/setup.c index 5f3402a2fbaf..a16a7fe0e6f9 100644 --- a/arch/m32r/platforms/opsput/setup.c +++ b/arch/m32r/platforms/opsput/setup.c @@ -300,101 +300,65 @@ void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT#0: LAN controller on OPSPUT-LAN (SMC91C111)*/ - irq_desc[OPSPUT_LAN_IRQ_LAN].status = IRQ_DISABLED; - irq_desc[OPSPUT_LAN_IRQ_LAN].chip = &opsput_lanpld_irq_type; - irq_desc[OPSPUT_LAN_IRQ_LAN].action = 0; - irq_desc[OPSPUT_LAN_IRQ_LAN].depth = 1; /* disable nested irq */ + set_irq_chip(OPSPUT_LAN_IRQ_LAN, &opsput_lanpld_irq_type); lanpld_icu_data[irq2lanpldirq(OPSPUT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ disable_opsput_lanpld_irq(OPSPUT_LAN_IRQ_LAN); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &opsput_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_opsput_irq(M32R_IRQ_MFT2); /* SIO0 : receive */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &opsput_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO0_R); /* SIO0 : send */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &opsput_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO0_S); /* SIO1 : receive */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = 0; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &opsput_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO1_R); /* SIO1 : send */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = 0; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &opsput_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO1_S); /* DMA1 : */ - irq_desc[M32R_IRQ_DMA1].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_DMA1].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_DMA1].action = 0; - irq_desc[M32R_IRQ_DMA1].depth = 1; + set_irq_chip(M32R_IRQ_DMA1, &opsput_irq_type); icu_data[M32R_IRQ_DMA1].icucr = 0; disable_opsput_irq(M32R_IRQ_DMA1); #ifdef CONFIG_SERIAL_M32R_PLDSIO /* INT#1: SIO0 Receive on PLD */ - irq_desc[PLD_IRQ_SIO0_RCV].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_SIO0_RCV].chip = &opsput_pld_irq_type; - irq_desc[PLD_IRQ_SIO0_RCV].action = 0; - irq_desc[PLD_IRQ_SIO0_RCV].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_SIO0_RCV, &opsput_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_opsput_pld_irq(PLD_IRQ_SIO0_RCV); /* INT#1: SIO0 Send on PLD */ - irq_desc[PLD_IRQ_SIO0_SND].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_SIO0_SND].chip = &opsput_pld_irq_type; - irq_desc[PLD_IRQ_SIO0_SND].action = 0; - irq_desc[PLD_IRQ_SIO0_SND].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_SIO0_SND, &opsput_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_opsput_pld_irq(PLD_IRQ_SIO0_SND); #endif /* CONFIG_SERIAL_M32R_PLDSIO */ /* INT#1: CFC IREQ on PLD */ - irq_desc[PLD_IRQ_CFIREQ].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFIREQ].chip = &opsput_pld_irq_type; - irq_desc[PLD_IRQ_CFIREQ].action = 0; - irq_desc[PLD_IRQ_CFIREQ].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFIREQ, &opsput_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_opsput_pld_irq(PLD_IRQ_CFIREQ); /* INT#1: CFC Insert on PLD */ - irq_desc[PLD_IRQ_CFC_INSERT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_INSERT].chip = &opsput_pld_irq_type; - irq_desc[PLD_IRQ_CFC_INSERT].action = 0; - irq_desc[PLD_IRQ_CFC_INSERT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_INSERT, &opsput_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ disable_opsput_pld_irq(PLD_IRQ_CFC_INSERT); /* INT#1: CFC Eject on PLD */ - irq_desc[PLD_IRQ_CFC_EJECT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CFC_EJECT].chip = &opsput_pld_irq_type; - irq_desc[PLD_IRQ_CFC_EJECT].action = 0; - irq_desc[PLD_IRQ_CFC_EJECT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CFC_EJECT, &opsput_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ disable_opsput_pld_irq(PLD_IRQ_CFC_EJECT); @@ -415,10 +379,7 @@ void __init init_IRQ(void) #if defined(CONFIG_USB) outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ - irq_desc[OPSPUT_LCD_IRQ_USB_INT1].status = IRQ_DISABLED; - irq_desc[OPSPUT_LCD_IRQ_USB_INT1].chip = &opsput_lcdpld_irq_type; - irq_desc[OPSPUT_LCD_IRQ_USB_INT1].action = 0; - irq_desc[OPSPUT_LCD_IRQ_USB_INT1].depth = 1; + set_irq_chip(OPSPUT_LCD_IRQ_USB_INT1, &opsput_lcdpld_irq_type); lcdpld_icu_data[irq2lcdpldirq(OPSPUT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ disable_opsput_lcdpld_irq(OPSPUT_LCD_IRQ_USB_INT1); #endif @@ -433,10 +394,7 @@ void __init init_IRQ(void) /* * INT3# is used for AR */ - irq_desc[M32R_IRQ_INT3].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_INT3].chip = &opsput_irq_type; - irq_desc[M32R_IRQ_INT3].action = 0; - irq_desc[M32R_IRQ_INT3].depth = 1; + set_irq_chip(M32R_IRQ_INT3, &opsput_irq_type); icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_opsput_irq(M32R_IRQ_INT3); #endif /* CONFIG_VIDEO_M32R_AR */ diff --git a/arch/m32r/platforms/usrv/setup.c b/arch/m32r/platforms/usrv/setup.c index 1beac7a51ed4..d6eb586a099e 100644 --- a/arch/m32r/platforms/usrv/setup.c +++ b/arch/m32r/platforms/usrv/setup.c @@ -156,53 +156,35 @@ void __init init_IRQ(void) once++; /* MFT2 : system timer */ - irq_desc[M32R_IRQ_MFT2].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_MFT2].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_MFT2].action = 0; - irq_desc[M32R_IRQ_MFT2].depth = 1; + set_irq_chip(M32R_IRQ_MFT2, &mappi_irq_type); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi_irq(M32R_IRQ_MFT2); #if defined(CONFIG_SERIAL_M32R_SIO) /* SIO0_R : uart receive data */ - irq_desc[M32R_IRQ_SIO0_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_R].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO0_R].action = 0; - irq_desc[M32R_IRQ_SIO0_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_R, &mappi_irq_type); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - irq_desc[M32R_IRQ_SIO0_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO0_S].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO0_S].action = 0; - irq_desc[M32R_IRQ_SIO0_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO0_S, &mappi_irq_type); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - irq_desc[M32R_IRQ_SIO1_R].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_R].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO1_R].action = 0; - irq_desc[M32R_IRQ_SIO1_R].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_R, &mappi_irq_type); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - irq_desc[M32R_IRQ_SIO1_S].status = IRQ_DISABLED; - irq_desc[M32R_IRQ_SIO1_S].chip = &mappi_irq_type; - irq_desc[M32R_IRQ_SIO1_S].action = 0; - irq_desc[M32R_IRQ_SIO1_S].depth = 1; + set_irq_chip(M32R_IRQ_SIO1_S, &mappi_irq_type); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_SERIAL_M32R_SIO */ /* INT#67-#71: CFC#0 IREQ on PLD */ for (i = 0 ; i < CONFIG_M32R_CFC_NUM ; i++ ) { - irq_desc[PLD_IRQ_CF0 + i].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_CF0 + i].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_CF0 + i].action = 0; - irq_desc[PLD_IRQ_CF0 + i].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_CF0 + i, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_CF0 + i)].icucr = PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_CF0 + i); @@ -210,19 +192,13 @@ void __init init_IRQ(void) #if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) /* INT#76: 16552D#0 IREQ on PLD */ - irq_desc[PLD_IRQ_UART0].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_UART0].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_UART0].action = 0; - irq_desc[PLD_IRQ_UART0].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_UART0, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_UART0)].icucr = PLD_ICUCR_ISMOD03; /* 'H' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_UART0); /* INT#77: 16552D#1 IREQ on PLD */ - irq_desc[PLD_IRQ_UART1].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_UART1].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_UART1].action = 0; - irq_desc[PLD_IRQ_UART1].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_UART1, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_UART1)].icucr = PLD_ICUCR_ISMOD03; /* 'H' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_UART1); @@ -230,10 +206,7 @@ void __init init_IRQ(void) #if defined(CONFIG_IDC_AK4524) || defined(CONFIG_IDC_AK4524_MODULE) /* INT#80: AK4524 IREQ on PLD */ - irq_desc[PLD_IRQ_SNDINT].status = IRQ_DISABLED; - irq_desc[PLD_IRQ_SNDINT].chip = &m32700ut_pld_irq_type; - irq_desc[PLD_IRQ_SNDINT].action = 0; - irq_desc[PLD_IRQ_SNDINT].depth = 1; /* disable nested irq */ + set_irq_chip(PLD_IRQ_SNDINT, &m32700ut_pld_irq_type); pld_icu_data[irq2pldirq(PLD_IRQ_SNDINT)].icucr = PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_SNDINT); -- cgit v1.2.3 From 8afb53b9ac0ac196d58b741d7bdfba8702ae7945 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 17:02:29 +0100 Subject: m32r: Convert m32104ut irq handling Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/m32104ut/setup.c | 46 ++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/arch/m32r/platforms/m32104ut/setup.c b/arch/m32r/platforms/m32104ut/setup.c index 962f38e58acc..4a693d02c1e1 100644 --- a/arch/m32r/platforms/m32104ut/setup.c +++ b/arch/m32r/platforms/m32104ut/setup.c @@ -39,39 +39,30 @@ static void enable_m32104ut_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_m32104ut(unsigned int irq) +static void mask_m32104ut_irq(struct irq_data *data) { - disable_m32104ut_irq(irq); + disable_m32104ut_irq(data->irq); } -static void end_m32104ut_irq(unsigned int irq) +static void unmask_m32104ut_irq(struct irq_data *data) { - enable_m32104ut_irq(irq); + enable_m32104ut_irq(data->irq); } -static unsigned int startup_m32104ut_irq(unsigned int irq) +static void shutdown_m32104ut_irq(struct irq_data *data) { - enable_m32104ut_irq(irq); - return (0); -} - -static void shutdown_m32104ut_irq(unsigned int irq) -{ - unsigned long port; + unsigned int irq = data->irq; + unsigned long port = irq2port(irq); - port = irq2port(irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip m32104ut_irq_type = { - .name = "M32104UT-IRQ", - .startup = startup_m32104ut_irq, - .shutdown = shutdown_m32104ut_irq, - .enable = enable_m32104ut_irq, - .disable = disable_m32104ut_irq, - .ack = mask_and_ack_m32104ut, - .end = end_m32104ut_irq + .name = "M32104UT-IRQ", + .irq_shutdown = shutdown_m32104ut_irq, + .irq_unmask = unmask_m32104ut_irq, + .irq_mask = mask_m32104ut_irq, }; void __init init_IRQ(void) @@ -85,24 +76,29 @@ void __init init_IRQ(void) #if defined(CONFIG_SMC91X) /* INT#0: LAN controller on M32104UT-LAN (SMC91C111)*/ - set_irq_chip(M32R_IRQ_INT0, &m32104ut_irq_type); - icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD11; /* "H" level sense */ + set_irq_chip_and_handler(M32R_IRQ_INT0, &m32104ut_irq_type, + handle_level_irq); + /* "H" level sense */ + cu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD11; disable_m32104ut_irq(M32R_IRQ_INT0); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &m32104ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &m32104ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_m32104ut_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO0_R, &m32104ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &m32104ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = M32R_ICUCR_IEN; disable_m32104ut_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO0_S, &m32104ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &m32104ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = M32R_ICUCR_IEN; disable_m32104ut_irq(M32R_IRQ_SIO0_S); #endif /* CONFIG_SERIAL_M32R_SIO */ -- cgit v1.2.3 From 364a9ba07b0341c7e16809d810a19e06b6e64b05 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 11 Jan 2011 10:43:49 +0100 Subject: m32r: Convert m32104ut irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/m32700ut/setup.c | 56 +++++++++++++++++------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/arch/m32r/platforms/m32700ut/setup.c b/arch/m32r/platforms/m32700ut/setup.c index 433bcbca2c3a..812cea993585 100644 --- a/arch/m32r/platforms/m32700ut/setup.c +++ b/arch/m32r/platforms/m32700ut/setup.c @@ -45,39 +45,30 @@ static void enable_m32700ut_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_m32700ut(unsigned int irq) +static void mask_m32700ut(struct irq_data *data) { - disable_m32700ut_irq(irq); + disable_m32700ut_irq(data->irq); } -static void end_m32700ut_irq(unsigned int irq) +static void unmask_m32700ut(struct irq_data *data) { - enable_m32700ut_irq(irq); + enable_m32700ut_irq(data->irq); } -static unsigned int startup_m32700ut_irq(unsigned int irq) -{ - enable_m32700ut_irq(irq); - return (0); -} - -static void shutdown_m32700ut_irq(unsigned int irq) +static void shutdown_m32700ut(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip m32700ut_irq_type = { - .name = "M32700UT-IRQ", - .startup = startup_m32700ut_irq, - .shutdown = shutdown_m32700ut_irq, - .enable = enable_m32700ut_irq, - .disable = disable_m32700ut_irq, - .ack = mask_and_ack_m32700ut, - .end = end_m32700ut_irq + .name = "M32700UT-IRQ", + .irq_shutdown = shutdown_m32700ut, + .irq_mask = mask_m32700ut, + .irq_unmask = unmask_m32700ut }; /* @@ -126,7 +117,7 @@ static void mask_and_ack_m32700ut_pld(unsigned int irq) static void end_m32700ut_pld_irq(unsigned int irq) { enable_m32700ut_pld_irq(irq); - end_m32700ut_irq(M32R_IRQ_INT1); + enable_m32700ut_irq(M32R_IRQ_INT1); } static unsigned int startup_m32700ut_pld_irq(unsigned int irq) @@ -196,7 +187,7 @@ static void mask_and_ack_m32700ut_lanpld(unsigned int irq) static void end_m32700ut_lanpld_irq(unsigned int irq) { enable_m32700ut_lanpld_irq(irq); - end_m32700ut_irq(M32R_IRQ_INT0); + enable_m32700ut_irq(M32R_IRQ_INT0); } static unsigned int startup_m32700ut_lanpld_irq(unsigned int irq) @@ -265,7 +256,7 @@ static void mask_and_ack_m32700ut_lcdpld(unsigned int irq) static void end_m32700ut_lcdpld_irq(unsigned int irq) { enable_m32700ut_lcdpld_irq(irq); - end_m32700ut_irq(M32R_IRQ_INT2); + enable_m32700ut_irq(M32R_IRQ_INT2); } static unsigned int startup_m32700ut_lcdpld_irq(unsigned int irq) @@ -305,32 +296,38 @@ void __init init_IRQ(void) #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_m32700ut_irq(M32R_IRQ_MFT2); /* SIO0 : receive */ - set_irq_chip(M32R_IRQ_SIO0_R, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO0_R); /* SIO0 : send */ - set_irq_chip(M32R_IRQ_SIO0_S, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO0_S); /* SIO1 : receive */ - set_irq_chip(M32R_IRQ_SIO1_R, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO1_R); /* SIO1 : send */ - set_irq_chip(M32R_IRQ_SIO1_S, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_m32700ut_irq(M32R_IRQ_SIO1_S); /* DMA1 : */ - set_irq_chip(M32R_IRQ_DMA1, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_DMA1, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_DMA1].icucr = 0; disable_m32700ut_irq(M32R_IRQ_DMA1); @@ -393,7 +390,8 @@ void __init init_IRQ(void) /* * INT3# is used for AR */ - set_irq_chip(M32R_IRQ_INT3, &m32700ut_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT3, &m32700ut_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_m32700ut_irq(M32R_IRQ_INT3); #endif /* CONFIG_VIDEO_M32R_AR */ -- cgit v1.2.3 From 63b549b6552c7869c25a1c60783aa86a3380e06c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 17:41:51 +0100 Subject: m32r: Convert m32700ut pld irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/m32700ut/setup.c | 48 +++++++++++++++--------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/arch/m32r/platforms/m32700ut/setup.c b/arch/m32r/platforms/m32700ut/setup.c index 812cea993585..99d92411e84f 100644 --- a/arch/m32r/platforms/m32700ut/setup.c +++ b/arch/m32r/platforms/m32700ut/setup.c @@ -90,7 +90,6 @@ static void disable_m32700ut_pld_irq(unsigned int irq) unsigned int pldirq; pldirq = irq2pldirq(irq); -// disable_m32700ut_irq(M32R_IRQ_INT1); port = pldirq2port(pldirq); data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; outw(data, port); @@ -102,50 +101,38 @@ static void enable_m32700ut_pld_irq(unsigned int irq) unsigned int pldirq; pldirq = irq2pldirq(irq); -// enable_m32700ut_irq(M32R_IRQ_INT1); port = pldirq2port(pldirq); data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; outw(data, port); } -static void mask_and_ack_m32700ut_pld(unsigned int irq) +static void mask_m32700ut_pld(struct irq_data *data) { - disable_m32700ut_pld_irq(irq); -// mask_and_ack_m32700ut(M32R_IRQ_INT1); + disable_m32700ut_pld_irq(data->irq); } -static void end_m32700ut_pld_irq(unsigned int irq) +static void unmask_m32700ut_pld(struct irq_data *data) { - enable_m32700ut_pld_irq(irq); + enable_m32700ut_pld_irq(data->irq); enable_m32700ut_irq(M32R_IRQ_INT1); } -static unsigned int startup_m32700ut_pld_irq(unsigned int irq) -{ - enable_m32700ut_pld_irq(irq); - return (0); -} - -static void shutdown_m32700ut_pld_irq(unsigned int irq) +static void shutdown_m32700ut_pld_irq(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2pldirq(irq); -// shutdown_m32700ut_irq(M32R_IRQ_INT1); + pldirq = irq2pldirq(data->irq); port = pldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } static struct irq_chip m32700ut_pld_irq_type = { - .name = "M32700UT-PLD-IRQ", - .startup = startup_m32700ut_pld_irq, - .shutdown = shutdown_m32700ut_pld_irq, - .enable = enable_m32700ut_pld_irq, - .disable = disable_m32700ut_pld_irq, - .ack = mask_and_ack_m32700ut_pld, - .end = end_m32700ut_pld_irq + .name = "M32700UT-PLD-IRQ", + .irq_shutdown = shutdown_m32700ut_pld_irq, + .irq_mask = mask_m32700ut_pld, + .irq_unmask = unmask_m32700ut_pld, }; /* @@ -333,28 +320,33 @@ void __init init_IRQ(void) #ifdef CONFIG_SERIAL_M32R_PLDSIO /* INT#1: SIO0 Receive on PLD */ - set_irq_chip(PLD_IRQ_SIO0_RCV, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_SIO0_RCV, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_m32700ut_pld_irq(PLD_IRQ_SIO0_RCV); /* INT#1: SIO0 Send on PLD */ - set_irq_chip(PLD_IRQ_SIO0_SND, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_SIO0_SND, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_m32700ut_pld_irq(PLD_IRQ_SIO0_SND); #endif /* CONFIG_SERIAL_M32R_PLDSIO */ /* INT#1: CFC IREQ on PLD */ - set_irq_chip(PLD_IRQ_CFIREQ, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFIREQ, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_CFIREQ); /* INT#1: CFC Insert on PLD */ - set_irq_chip(PLD_IRQ_CFC_INSERT, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_INSERT, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ disable_m32700ut_pld_irq(PLD_IRQ_CFC_INSERT); /* INT#1: CFC Eject on PLD */ - set_irq_chip(PLD_IRQ_CFC_EJECT, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_EJECT, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ disable_m32700ut_pld_irq(PLD_IRQ_CFC_EJECT); -- cgit v1.2.3 From 72bd198f1d8e3341ea55dc250830a92e2816d16a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:14:21 +0100 Subject: m32r: Convert m32700ut lanpld irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/m32700ut/setup.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/arch/m32r/platforms/m32700ut/setup.c b/arch/m32r/platforms/m32700ut/setup.c index 99d92411e84f..bce70e1eb9e3 100644 --- a/arch/m32r/platforms/m32700ut/setup.c +++ b/arch/m32r/platforms/m32700ut/setup.c @@ -166,42 +166,33 @@ static void enable_m32700ut_lanpld_irq(unsigned int irq) outw(data, port); } -static void mask_and_ack_m32700ut_lanpld(unsigned int irq) +static void mask_m32700ut_lanpld(struct irq_data *data) { - disable_m32700ut_lanpld_irq(irq); + disable_m32700ut_lanpld_irq(data->irq); } -static void end_m32700ut_lanpld_irq(unsigned int irq) +static void unmask_m32700ut_lanpld(struct irq_data *data) { - enable_m32700ut_lanpld_irq(irq); + enable_m32700ut_lanpld_irq(data->irq); enable_m32700ut_irq(M32R_IRQ_INT0); } -static unsigned int startup_m32700ut_lanpld_irq(unsigned int irq) -{ - enable_m32700ut_lanpld_irq(irq); - return (0); -} - -static void shutdown_m32700ut_lanpld_irq(unsigned int irq) +static void shutdown_m32700ut_lanpld(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2lanpldirq(irq); + pldirq = irq2lanpldirq(data->irq); port = lanpldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } static struct irq_chip m32700ut_lanpld_irq_type = { - .name = "M32700UT-PLD-LAN-IRQ", - .startup = startup_m32700ut_lanpld_irq, - .shutdown = shutdown_m32700ut_lanpld_irq, - .enable = enable_m32700ut_lanpld_irq, - .disable = disable_m32700ut_lanpld_irq, - .ack = mask_and_ack_m32700ut_lanpld, - .end = end_m32700ut_lanpld_irq + .name = "M32700UT-PLD-LAN-IRQ", + .irq_shutdown = shutdown_m32700ut_lanpld, + .irq_mask = mask_m32700ut_lanpld, + .irq_unmask = unmask_m32700ut_lanpld, }; /* @@ -277,7 +268,8 @@ void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT#0: LAN controller on M32700UT-LAN (SMC91C111)*/ - set_irq_chip(M32700UT_LAN_IRQ_LAN, &m32700ut_lanpld_irq_type); + set_irq_chip_and_handler(M32700UT_LAN_IRQ_LAN, + &m32700ut_lanpld_irq_type, handle_level_irq); lanpld_icu_data[irq2lanpldirq(M32700UT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ disable_m32700ut_lanpld_irq(M32700UT_LAN_IRQ_LAN); #endif /* CONFIG_SMC91X */ -- cgit v1.2.3 From 37808e47eab064006eb17d46cf15a098655940c4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:19:42 +0100 Subject: m32r: Convert m32700ut lcdpld irq chip Convert the irq chip to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/m32700ut/setup.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/arch/m32r/platforms/m32700ut/setup.c b/arch/m32r/platforms/m32700ut/setup.c index bce70e1eb9e3..2074bcc841eb 100644 --- a/arch/m32r/platforms/m32700ut/setup.c +++ b/arch/m32r/platforms/m32700ut/setup.c @@ -226,42 +226,33 @@ static void enable_m32700ut_lcdpld_irq(unsigned int irq) outw(data, port); } -static void mask_and_ack_m32700ut_lcdpld(unsigned int irq) +static void mask_m32700ut_lcdpld(struct irq_data *data) { - disable_m32700ut_lcdpld_irq(irq); + disable_m32700ut_lcdpld_irq(data->irq); } -static void end_m32700ut_lcdpld_irq(unsigned int irq) +static void unmask_m32700ut_lcdpld(struct irq_data *data) { - enable_m32700ut_lcdpld_irq(irq); + enable_m32700ut_lcdpld_irq(data->irq); enable_m32700ut_irq(M32R_IRQ_INT2); } -static unsigned int startup_m32700ut_lcdpld_irq(unsigned int irq) -{ - enable_m32700ut_lcdpld_irq(irq); - return (0); -} - -static void shutdown_m32700ut_lcdpld_irq(unsigned int irq) +static void shutdown_m32700ut_lcdpld(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2lcdpldirq(irq); + pldirq = irq2lcdpldirq(data->irq); port = lcdpldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } static struct irq_chip m32700ut_lcdpld_irq_type = { - .name = "M32700UT-PLD-LCD-IRQ", - .startup = startup_m32700ut_lcdpld_irq, - .shutdown = shutdown_m32700ut_lcdpld_irq, - .enable = enable_m32700ut_lcdpld_irq, - .disable = disable_m32700ut_lcdpld_irq, - .ack = mask_and_ack_m32700ut_lcdpld, - .end = end_m32700ut_lcdpld_irq + .name = "M32700UT-PLD-LCD-IRQ", + .irq_shutdown = shutdown_m32700ut_lcdpld, + .irq_mask = mask_m32700ut_lcdpld, + .irq_unmask = unmask_m32700ut_lcdpld, }; void __init init_IRQ(void) @@ -358,7 +349,8 @@ void __init init_IRQ(void) #if defined(CONFIG_USB) outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ - set_irq_chip(M32700UT_LCD_IRQ_USB_INT1, &m32700ut_lcdpld_irq_type); + set_irq_chip_and_handler(M32700UT_LCD_IRQ_USB_INT1, + &m32700ut_lcdpld_irq_type, handle_level_irq); lcdpld_icu_data[irq2lcdpldirq(M32700UT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ disable_m32700ut_lcdpld_irq(M32700UT_LCD_IRQ_USB_INT1); -- cgit v1.2.3 From 1f12681ab1419a68da0f066b95e3e6e9270eb730 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:27:59 +0100 Subject: m32r: Convert mappi irq chips Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/mappi/setup.c | 53 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/arch/m32r/platforms/mappi/setup.c b/arch/m32r/platforms/mappi/setup.c index bc3fdaf21ca2..cdd8c4574027 100644 --- a/arch/m32r/platforms/mappi/setup.c +++ b/arch/m32r/platforms/mappi/setup.c @@ -38,39 +38,30 @@ static void enable_mappi_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_mappi(unsigned int irq) +static void mask_mappi(struct irq_data *data) { - disable_mappi_irq(irq); + disable_mappi_irq(data->irq); } -static void end_mappi_irq(unsigned int irq) +static void unmask_mappi(struct irq_data *data) { - enable_mappi_irq(irq); + enable_mappi_irq(data->irq); } -static unsigned int startup_mappi_irq(unsigned int irq) -{ - enable_mappi_irq(irq); - return (0); -} - -static void shutdown_mappi_irq(unsigned int irq) +static void shutdown_mappi(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip mappi_irq_type = { - .name = "MAPPI-IRQ", - .startup = startup_mappi_irq, - .shutdown = shutdown_mappi_irq, - .enable = enable_mappi_irq, - .disable = disable_mappi_irq, - .ack = mask_and_ack_mappi, - .end = end_mappi_irq + .name = "MAPPI-IRQ", + .irq_shutdown = shutdown_mappi, + .irq_mask = mask_mappi, + .irq_unmask = unmask_mappi, }; void __init init_IRQ(void) @@ -84,46 +75,54 @@ void __init init_IRQ(void) #ifdef CONFIG_NE2000 /* INT0 : LAN controller (RTL8019AS) */ - set_irq_chip(M32R_IRQ_INT0, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT0, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD11; disable_mappi_irq(M32R_IRQ_INT0); #endif /* CONFIG_M32R_NE2000 */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO0_R, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO0_S, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO1_R, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO1_S, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_SERIAL_M32R_SIO */ #if defined(CONFIG_M32R_PCC) /* INT1 : pccard0 interrupt */ - set_irq_chip(M32R_IRQ_INT1, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT1, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; disable_mappi_irq(M32R_IRQ_INT1); /* INT2 : pccard1 interrupt */ - set_irq_chip(M32R_IRQ_INT2, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT2, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT2].icucr = M32R_ICUCR_IEN | M32R_ICUCR_ISMOD00; disable_mappi_irq(M32R_IRQ_INT2); #endif /* CONFIG_M32RPCC */ -- cgit v1.2.3 From efa63c6495fb6f83a1cc183f0df2617e383392e9 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:34:51 +0100 Subject: m32r: Convert mappi2 irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/mappi2/setup.c | 59 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/arch/m32r/platforms/mappi2/setup.c b/arch/m32r/platforms/mappi2/setup.c index 87bba1e01719..9117c30ea365 100644 --- a/arch/m32r/platforms/mappi2/setup.c +++ b/arch/m32r/platforms/mappi2/setup.c @@ -46,96 +46,97 @@ static void enable_mappi2_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_mappi2(unsigned int irq) +static void mask_mappi2(struct irq_data *data) { - disable_mappi2_irq(irq); + disable_mappi2_irq(data->irq); } -static void end_mappi2_irq(unsigned int irq) +static void unmask_mappi2(struct irq_data *data) { - enable_mappi2_irq(irq); + enable_mappi2_irq(data->irq); } -static unsigned int startup_mappi2_irq(unsigned int irq) -{ - enable_mappi2_irq(irq); - return (0); -} - -static void shutdown_mappi2_irq(unsigned int irq) +static void shutdown_mappi2(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip mappi2_irq_type = { - .name = "MAPPI2-IRQ", - .startup = startup_mappi2_irq, - .shutdown = shutdown_mappi2_irq, - .enable = enable_mappi2_irq, - .disable = disable_mappi2_irq, - .ack = mask_and_ack_mappi2, - .end = end_mappi2_irq + .name = "MAPPI2-IRQ", + .irq_shutdown = shutdown_mappi2, + .irq_mask = mask_mappi2, + .irq_unmask = unmask_mappi2, }; void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT0 : LAN controller (SMC91111) */ - set_irq_chip(M32R_IRQ_INT0, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT0, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi2_irq(M32R_IRQ_INT0); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi2_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO0_R, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO0_S, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO1_R, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO1_S, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi2_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_M32R_USE_DBG_CONSOLE */ #if defined(CONFIG_USB) /* INT1 : USB Host controller interrupt */ - set_irq_chip(M32R_IRQ_INT1, &mappi2_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT1, &mappi2_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD01; disable_mappi2_irq(M32R_IRQ_INT1); #endif /* CONFIG_USB */ /* ICUCR40: CFC IREQ */ - set_irq_chip(PLD_IRQ_CFIREQ, &mappi2_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFIREQ, &mappi2_irq_type, + handle_level_irq); icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; disable_mappi2_irq(PLD_IRQ_CFIREQ); #if defined(CONFIG_M32R_CFC) /* ICUCR41: CFC Insert */ - set_irq_chip(PLD_IRQ_CFC_INSERT, &mappi2_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_INSERT, &mappi2_irq_type, + handle_level_irq); icu_data[PLD_IRQ_CFC_INSERT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; disable_mappi2_irq(PLD_IRQ_CFC_INSERT); /* ICUCR42: CFC Eject */ - set_irq_chip(PLD_IRQ_CFC_EJECT, &mappi2_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_EJECT, &mappi2_irq_type, + handle_level_irq); icu_data[PLD_IRQ_CFC_EJECT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi2_irq(PLD_IRQ_CFC_EJECT); #endif /* CONFIG_MAPPI2_CFC */ -- cgit v1.2.3 From b82727ec646578bdd3a6f31f6451f67784874675 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:39:27 +0100 Subject: m32r: Convert mappi3 irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/mappi3/setup.c | 62 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/arch/m32r/platforms/mappi3/setup.c b/arch/m32r/platforms/mappi3/setup.c index 74098142cb2c..b44f5ded2bbe 100644 --- a/arch/m32r/platforms/mappi3/setup.c +++ b/arch/m32r/platforms/mappi3/setup.c @@ -46,98 +46,98 @@ static void enable_mappi3_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_mappi3(unsigned int irq) +static void mask_mappi3(struct irq_data *data) { - disable_mappi3_irq(irq); + disable_mappi3_irq(data->irq); } -static void end_mappi3_irq(unsigned int irq) +static void unmask_mappi3(struct irq_data *data) { - enable_mappi3_irq(irq); + enable_mappi3_irq(data->irq); } -static unsigned int startup_mappi3_irq(unsigned int irq) -{ - enable_mappi3_irq(irq); - return (0); -} - -static void shutdown_mappi3_irq(unsigned int irq) +static void shutdown_mappi3(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } -static struct irq_chip mappi3_irq_type = -{ - .name = "MAPPI3-IRQ", - .startup = startup_mappi3_irq, - .shutdown = shutdown_mappi3_irq, - .enable = enable_mappi3_irq, - .disable = disable_mappi3_irq, - .ack = mask_and_ack_mappi3, - .end = end_mappi3_irq +static struct irq_chip mappi3_irq_type = { + .name = "MAPPI3-IRQ", + .irq_shutdown = shutdown_mappi3, + .irq_mask = mask_mappi3, + .irq_unmask = unmask_mappi3, }; void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT0 : LAN controller (SMC91111) */ - set_irq_chip(M32R_IRQ_INT0, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT0, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT0].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi3_irq(M32R_IRQ_INT0); #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi3_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO0_R, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO0_S, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO1_R, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO1_S, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi3_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_M32R_USE_DBG_CONSOLE */ #if defined(CONFIG_USB) /* INT1 : USB Host controller interrupt */ - set_irq_chip(M32R_IRQ_INT1, &mappi3_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT1, &mappi3_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT1].icucr = M32R_ICUCR_ISMOD01; disable_mappi3_irq(M32R_IRQ_INT1); #endif /* CONFIG_USB */ /* CFC IREQ */ - set_irq_chip(PLD_IRQ_CFIREQ, &mappi3_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFIREQ, &mappi3_irq_type, + handle_level_irq); icu_data[PLD_IRQ_CFIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD01; disable_mappi3_irq(PLD_IRQ_CFIREQ); #if defined(CONFIG_M32R_CFC) /* ICUCR41: CFC Insert & eject */ - set_irq_chip(PLD_IRQ_CFC_INSERT, &mappi3_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_INSERT, &mappi3_irq_type, + handle_level_irq); icu_data[PLD_IRQ_CFC_INSERT].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD00; disable_mappi3_irq(PLD_IRQ_CFC_INSERT); #endif /* CONFIG_M32R_CFC */ /* IDE IREQ */ - set_irq_chip(PLD_IRQ_IDEIREQ, &mappi3_irq_type); + set_irq_chip_and_handler(PLD_IRQ_IDEIREQ, &mappi3_irq_type, + handle_level_irq); icu_data[PLD_IRQ_IDEIREQ].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_mappi3_irq(PLD_IRQ_IDEIREQ); -- cgit v1.2.3 From ce1104ce67ea186bf5c23de66a2eeaf65823e1ce Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:44:10 +0100 Subject: m32r: Convert oaks32r irq chips Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/oaks32r/setup.c | 47 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/arch/m32r/platforms/oaks32r/setup.c b/arch/m32r/platforms/oaks32r/setup.c index 43bf5a0c9515..19a02db7b818 100644 --- a/arch/m32r/platforms/oaks32r/setup.c +++ b/arch/m32r/platforms/oaks32r/setup.c @@ -37,39 +37,30 @@ static void enable_oaks32r_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_mappi(unsigned int irq) +static void mask_oaks32r(struct irq_data *data) { - disable_oaks32r_irq(irq); + disable_oaks32r_irq(data->irq); } -static void end_oaks32r_irq(unsigned int irq) +static void unmask_oaks32r(struct irq_data *data) { - enable_oaks32r_irq(irq); + enable_oaks32r_irq(data->irq); } -static unsigned int startup_oaks32r_irq(unsigned int irq) -{ - enable_oaks32r_irq(irq); - return (0); -} - -static void shutdown_oaks32r_irq(unsigned int irq) +static void shutdown_oaks32r(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip oaks32r_irq_type = { - .name = "OAKS32R-IRQ", - .startup = startup_oaks32r_irq, - .shutdown = shutdown_oaks32r_irq, - .enable = enable_oaks32r_irq, - .disable = disable_oaks32r_irq, - .ack = mask_and_ack_mappi, - .end = end_oaks32r_irq + .name = "OAKS32R-IRQ", + .irq_shutdown = shutdown_oaks32r, + .irq_mask = mask_oaks32r, + .irq_unmask = unmask_oaks32r, }; void __init init_IRQ(void) @@ -83,34 +74,40 @@ void __init init_IRQ(void) #ifdef CONFIG_NE2000 /* INT3 : LAN controller (RTL8019AS) */ - set_irq_chip(M32R_IRQ_INT3, &oaks32r_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT3, &oaks32r_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_oaks32r_irq(M32R_IRQ_INT3); #endif /* CONFIG_M32R_NE2000 */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &oaks32r_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &oaks32r_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_oaks32r_irq(M32R_IRQ_MFT2); #ifdef CONFIG_SERIAL_M32R_SIO /* SIO0_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO0_R, &oaks32r_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &oaks32r_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO0_S, &oaks32r_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &oaks32r_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO1_R, &oaks32r_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &oaks32r_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO1_S, &oaks32r_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &oaks32r_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_oaks32r_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_SERIAL_M32R_SIO */ -- cgit v1.2.3 From 883c0ccde1e12e7721966626983d61ccdbd0ae99 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:48:15 +0100 Subject: m32r: Convert opsput irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/opsput/setup.c | 58 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/arch/m32r/platforms/opsput/setup.c b/arch/m32r/platforms/opsput/setup.c index a16a7fe0e6f9..8c0907dd771b 100644 --- a/arch/m32r/platforms/opsput/setup.c +++ b/arch/m32r/platforms/opsput/setup.c @@ -46,39 +46,30 @@ static void enable_opsput_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_opsput(unsigned int irq) +static void mask_opsput(struct irq_data *data) { - disable_opsput_irq(irq); + disable_opsput_irq(data->irq); } -static void end_opsput_irq(unsigned int irq) +static void unmask_opsput(struct irq_data *data) { - enable_opsput_irq(irq); + enable_opsput_irq(data->irq); } -static unsigned int startup_opsput_irq(unsigned int irq) -{ - enable_opsput_irq(irq); - return (0); -} - -static void shutdown_opsput_irq(unsigned int irq) +static void shutdown_opsput(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip opsput_irq_type = { - .name = "OPSPUT-IRQ", - .startup = startup_opsput_irq, - .shutdown = shutdown_opsput_irq, - .enable = enable_opsput_irq, - .disable = disable_opsput_irq, - .ack = mask_and_ack_opsput, - .end = end_opsput_irq + .name = "OPSPUT-IRQ", + .irq_shutdown = shutdown_opsput, + .irq_mask = mask_opsput, + .irq_unmask = unmask_opsput, }; /* @@ -100,7 +91,6 @@ static void disable_opsput_pld_irq(unsigned int irq) unsigned int pldirq; pldirq = irq2pldirq(irq); -// disable_opsput_irq(M32R_IRQ_INT1); port = pldirq2port(pldirq); data = pld_icu_data[pldirq].icucr|PLD_ICUCR_ILEVEL7; outw(data, port); @@ -112,7 +102,6 @@ static void enable_opsput_pld_irq(unsigned int irq) unsigned int pldirq; pldirq = irq2pldirq(irq); -// enable_opsput_irq(M32R_IRQ_INT1); port = pldirq2port(pldirq); data = pld_icu_data[pldirq].icucr|PLD_ICUCR_IEN|PLD_ICUCR_ILEVEL6; outw(data, port); @@ -127,7 +116,7 @@ static void mask_and_ack_opsput_pld(unsigned int irq) static void end_opsput_pld_irq(unsigned int irq) { enable_opsput_pld_irq(irq); - end_opsput_irq(M32R_IRQ_INT1); + enable_opsput_irq(M32R_IRQ_INT1); } static unsigned int startup_opsput_pld_irq(unsigned int irq) @@ -197,7 +186,7 @@ static void mask_and_ack_opsput_lanpld(unsigned int irq) static void end_opsput_lanpld_irq(unsigned int irq) { enable_opsput_lanpld_irq(irq); - end_opsput_irq(M32R_IRQ_INT0); + enable_opsput_irq(M32R_IRQ_INT0); } static unsigned int startup_opsput_lanpld_irq(unsigned int irq) @@ -266,7 +255,7 @@ static void mask_and_ack_opsput_lcdpld(unsigned int irq) static void end_opsput_lcdpld_irq(unsigned int irq) { enable_opsput_lcdpld_irq(irq); - end_opsput_irq(M32R_IRQ_INT2); + enable_opsput_irq(M32R_IRQ_INT2); } static unsigned int startup_opsput_lcdpld_irq(unsigned int irq) @@ -306,32 +295,38 @@ void __init init_IRQ(void) #endif /* CONFIG_SMC91X */ /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_opsput_irq(M32R_IRQ_MFT2); /* SIO0 : receive */ - set_irq_chip(M32R_IRQ_SIO0_R, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO0_R); /* SIO0 : send */ - set_irq_chip(M32R_IRQ_SIO0_S, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO0_S); /* SIO1 : receive */ - set_irq_chip(M32R_IRQ_SIO1_R, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO1_R); /* SIO1 : send */ - set_irq_chip(M32R_IRQ_SIO1_S, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_opsput_irq(M32R_IRQ_SIO1_S); /* DMA1 : */ - set_irq_chip(M32R_IRQ_DMA1, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_DMA1, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_DMA1].icucr = 0; disable_opsput_irq(M32R_IRQ_DMA1); @@ -394,7 +389,8 @@ void __init init_IRQ(void) /* * INT3# is used for AR */ - set_irq_chip(M32R_IRQ_INT3, &opsput_irq_type); + set_irq_chip_and_handler(M32R_IRQ_INT3, &opsput_irq_type, + handle_level_irq); icu_data[M32R_IRQ_INT3].icucr = M32R_ICUCR_IEN|M32R_ICUCR_ISMOD10; disable_opsput_irq(M32R_IRQ_INT3); #endif /* CONFIG_VIDEO_M32R_AR */ -- cgit v1.2.3 From 22cbc9384a495f2e6eb0d0fda91692431a28468e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:55:09 +0100 Subject: m32r: Convert opsput pld irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/opsput/setup.c | 46 +++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/arch/m32r/platforms/opsput/setup.c b/arch/m32r/platforms/opsput/setup.c index 8c0907dd771b..00eb3b82bd95 100644 --- a/arch/m32r/platforms/opsput/setup.c +++ b/arch/m32r/platforms/opsput/setup.c @@ -107,44 +107,33 @@ static void enable_opsput_pld_irq(unsigned int irq) outw(data, port); } -static void mask_and_ack_opsput_pld(unsigned int irq) +static void mask_opsput_pld(struct irq_data *data) { - disable_opsput_pld_irq(irq); -// mask_and_ack_opsput(M32R_IRQ_INT1); + disable_opsput_pld_irq(data->irq); } -static void end_opsput_pld_irq(unsigned int irq) +static void unmask_opsput_pld(struct irq_data *data) { - enable_opsput_pld_irq(irq); + enable_opsput_pld_irq(data->irq); enable_opsput_irq(M32R_IRQ_INT1); } -static unsigned int startup_opsput_pld_irq(unsigned int irq) -{ - enable_opsput_pld_irq(irq); - return (0); -} - -static void shutdown_opsput_pld_irq(unsigned int irq) +static void shutdown_opsput_pld(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2pldirq(irq); -// shutdown_opsput_irq(M32R_IRQ_INT1); + pldirq = irq2pldirq(data->irq); port = pldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } static struct irq_chip opsput_pld_irq_type = { - .name = "OPSPUT-PLD-IRQ", - .startup = startup_opsput_pld_irq, - .shutdown = shutdown_opsput_pld_irq, - .enable = enable_opsput_pld_irq, - .disable = disable_opsput_pld_irq, - .ack = mask_and_ack_opsput_pld, - .end = end_opsput_pld_irq + .name = "OPSPUT-PLD-IRQ", + .irq_shutdown = shutdown_opsput_pld, + .irq_mask = mask_opsput_pld, + .irq_unmask = unmask_opsput_pld, }; /* @@ -332,28 +321,33 @@ void __init init_IRQ(void) #ifdef CONFIG_SERIAL_M32R_PLDSIO /* INT#1: SIO0 Receive on PLD */ - set_irq_chip(PLD_IRQ_SIO0_RCV, &opsput_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_SIO0_RCV, &opsput_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_RCV)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_opsput_pld_irq(PLD_IRQ_SIO0_RCV); /* INT#1: SIO0 Send on PLD */ - set_irq_chip(PLD_IRQ_SIO0_SND, &opsput_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_SIO0_SND, &opsput_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_SIO0_SND)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD03; disable_opsput_pld_irq(PLD_IRQ_SIO0_SND); #endif /* CONFIG_SERIAL_M32R_PLDSIO */ /* INT#1: CFC IREQ on PLD */ - set_irq_chip(PLD_IRQ_CFIREQ, &opsput_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFIREQ, &opsput_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CFIREQ)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_opsput_pld_irq(PLD_IRQ_CFIREQ); /* INT#1: CFC Insert on PLD */ - set_irq_chip(PLD_IRQ_CFC_INSERT, &opsput_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_INSERT, &opsput_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_INSERT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD00; /* 'L' edge sense */ disable_opsput_pld_irq(PLD_IRQ_CFC_INSERT); /* INT#1: CFC Eject on PLD */ - set_irq_chip(PLD_IRQ_CFC_EJECT, &opsput_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CFC_EJECT, &opsput_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CFC_EJECT)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* 'H' edge sense */ disable_opsput_pld_irq(PLD_IRQ_CFC_EJECT); -- cgit v1.2.3 From 1899a493b991cf9ea7a57d15b64588632fe32cf0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 18:58:45 +0100 Subject: m32r: Convert opsput lanpld irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/opsput/setup.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/arch/m32r/platforms/opsput/setup.c b/arch/m32r/platforms/opsput/setup.c index 00eb3b82bd95..7648c1dd8dee 100644 --- a/arch/m32r/platforms/opsput/setup.c +++ b/arch/m32r/platforms/opsput/setup.c @@ -167,42 +167,33 @@ static void enable_opsput_lanpld_irq(unsigned int irq) outw(data, port); } -static void mask_and_ack_opsput_lanpld(unsigned int irq) +static void mask_opsput_lanpld(struct irq_data *data) { - disable_opsput_lanpld_irq(irq); + disable_opsput_lanpld_irq(data->irq); } -static void end_opsput_lanpld_irq(unsigned int irq) +static void unmask_opsput_lanpld(struct irq_data *data) { - enable_opsput_lanpld_irq(irq); + enable_opsput_lanpld_irq(data->irq); enable_opsput_irq(M32R_IRQ_INT0); } -static unsigned int startup_opsput_lanpld_irq(unsigned int irq) -{ - enable_opsput_lanpld_irq(irq); - return (0); -} - -static void shutdown_opsput_lanpld_irq(unsigned int irq) +static void shutdown_opsput_lanpld(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2lanpldirq(irq); + pldirq = irq2lanpldirq(data->irq); port = lanpldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } static struct irq_chip opsput_lanpld_irq_type = { - .name = "OPSPUT-PLD-LAN-IRQ", - .startup = startup_opsput_lanpld_irq, - .shutdown = shutdown_opsput_lanpld_irq, - .enable = enable_opsput_lanpld_irq, - .disable = disable_opsput_lanpld_irq, - .ack = mask_and_ack_opsput_lanpld, - .end = end_opsput_lanpld_irq + .name = "OPSPUT-PLD-LAN-IRQ", + .irq_shutdown = shutdown_opsput_lanpld, + .irq_mask = mask_opsput_lanpld, + .irq_unmask = unmask_opsput_lanpld, }; /* @@ -278,7 +269,8 @@ void __init init_IRQ(void) { #if defined(CONFIG_SMC91X) /* INT#0: LAN controller on OPSPUT-LAN (SMC91C111)*/ - set_irq_chip(OPSPUT_LAN_IRQ_LAN, &opsput_lanpld_irq_type); + set_irq_chip_and_handler(OPSPUT_LAN_IRQ_LAN, &opsput_lanpld_irq_type, + handle_level_irq); lanpld_icu_data[irq2lanpldirq(OPSPUT_LAN_IRQ_LAN)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD02; /* "H" edge sense */ disable_opsput_lanpld_irq(OPSPUT_LAN_IRQ_LAN); #endif /* CONFIG_SMC91X */ -- cgit v1.2.3 From 9b141fa649b160c6a0854a79fe2cd741c5c99e80 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 19:01:23 +0100 Subject: m32r: Convert opsput_lcdpld irq chip Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/opsput/setup.c | 42 +++++++++++++++----------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/arch/m32r/platforms/opsput/setup.c b/arch/m32r/platforms/opsput/setup.c index 7648c1dd8dee..12731547e8bf 100644 --- a/arch/m32r/platforms/opsput/setup.c +++ b/arch/m32r/platforms/opsput/setup.c @@ -227,42 +227,32 @@ static void enable_opsput_lcdpld_irq(unsigned int irq) outw(data, port); } -static void mask_and_ack_opsput_lcdpld(unsigned int irq) +static void mask_opsput_lcdpld(struct irq_data *data) { - disable_opsput_lcdpld_irq(irq); + disable_opsput_lcdpld_irq(data->irq); } -static void end_opsput_lcdpld_irq(unsigned int irq) +static void unmask_opsput_lcdpld(struct irq_data *data) { - enable_opsput_lcdpld_irq(irq); + enable_opsput_lcdpld_irq(data->irq); enable_opsput_irq(M32R_IRQ_INT2); } -static unsigned int startup_opsput_lcdpld_irq(unsigned int irq) -{ - enable_opsput_lcdpld_irq(irq); - return (0); -} - -static void shutdown_opsput_lcdpld_irq(unsigned int irq) +static void shutdown_opsput_lcdpld(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2lcdpldirq(irq); + pldirq = irq2lcdpldirq(data->irq); port = lcdpldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } -static struct irq_chip opsput_lcdpld_irq_type = -{ - "OPSPUT-PLD-LCD-IRQ", - startup_opsput_lcdpld_irq, - shutdown_opsput_lcdpld_irq, - enable_opsput_lcdpld_irq, - disable_opsput_lcdpld_irq, - mask_and_ack_opsput_lcdpld, - end_opsput_lcdpld_irq +static struct irq_chip opsput_lcdpld_irq_type = { + .name = "OPSPUT-PLD-LCD-IRQ", + .irq_shutdown = shutdown_opsput_lcdpld, + .irq_mask = mask_opsput_lcdpld, + .irq_unmask = unmask_opsput_lcdpld, }; void __init init_IRQ(void) @@ -358,11 +348,11 @@ void __init init_IRQ(void) enable_opsput_irq(M32R_IRQ_INT1); #if defined(CONFIG_USB) - outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ - - set_irq_chip(OPSPUT_LCD_IRQ_USB_INT1, &opsput_lcdpld_irq_type); - lcdpld_icu_data[irq2lcdpldirq(OPSPUT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ - disable_opsput_lcdpld_irq(OPSPUT_LCD_IRQ_USB_INT1); + outw(USBCR_OTGS, USBCR); /* USBCR: non-OTG */ + set_irq_chip_and_handler(OPSPUT_LCD_IRQ_USB_INT1, + &opsput_lcdpld_irq_type, handle_level_irq); + lcdpld_icu_data[irq2lcdpldirq(OPSPUT_LCD_IRQ_USB_INT1)].icucr = PLD_ICUCR_IEN|PLD_ICUCR_ISMOD01; /* "L" level sense */ + disable_opsput_lcdpld_irq(OPSPUT_LCD_IRQ_USB_INT1); #endif /* * INT2# is used for BAT, USB, AUDIO -- cgit v1.2.3 From 7a0abc7e7767455c9972de25b6c74e87271d16be Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 19:10:18 +0100 Subject: m32r: Convert usrv platform irq handling Convert the irq chips to the new functions and use proper flow handlers. handle_level_irq is appropriate. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/platforms/usrv/setup.c | 88 ++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/arch/m32r/platforms/usrv/setup.c b/arch/m32r/platforms/usrv/setup.c index d6eb586a099e..f3cff26d6e74 100644 --- a/arch/m32r/platforms/usrv/setup.c +++ b/arch/m32r/platforms/usrv/setup.c @@ -37,39 +37,30 @@ static void enable_mappi_irq(unsigned int irq) outl(data, port); } -static void mask_and_ack_mappi(unsigned int irq) +static void mask_mappi(struct irq_data *data) { - disable_mappi_irq(irq); + disable_mappi_irq(data->irq); } -static void end_mappi_irq(unsigned int irq) +static void unmask_mappi(struct irq_data *data) { - enable_mappi_irq(irq); + enable_mappi_irq(data->irq); } -static unsigned int startup_mappi_irq(unsigned int irq) -{ - enable_mappi_irq(irq); - return 0; -} - -static void shutdown_mappi_irq(unsigned int irq) +static void shutdown_mappi(struct irq_data *data) { unsigned long port; - port = irq2port(irq); + port = irq2port(data->irq); outl(M32R_ICUCR_ILEVEL7, port); } static struct irq_chip mappi_irq_type = { - .name = "M32700-IRQ", - .startup = startup_mappi_irq, - .shutdown = shutdown_mappi_irq, - .enable = enable_mappi_irq, - .disable = disable_mappi_irq, - .ack = mask_and_ack_mappi, - .end = end_mappi_irq + .name = "M32700-IRQ", + .irq_shutdown = shutdown_mappi, + .irq_mask = mask_mappi, + .irq_unmask = unmask_mappi, }; /* @@ -107,42 +98,33 @@ static void enable_m32700ut_pld_irq(unsigned int irq) outw(data, port); } -static void mask_and_ack_m32700ut_pld(unsigned int irq) +static void mask_m32700ut_pld(struct irq_data *data) { - disable_m32700ut_pld_irq(irq); + disable_m32700ut_pld_irq(data->irq); } -static void end_m32700ut_pld_irq(unsigned int irq) +static void unmask_m32700ut_pld(struct irq_data *data) { - enable_m32700ut_pld_irq(irq); - end_mappi_irq(M32R_IRQ_INT1); -} - -static unsigned int startup_m32700ut_pld_irq(unsigned int irq) -{ - enable_m32700ut_pld_irq(irq); - return 0; + enable_m32700ut_pld_irq(data->irq); + enable_mappi_irq(M32R_IRQ_INT1); } -static void shutdown_m32700ut_pld_irq(unsigned int irq) +static void shutdown_m32700ut_pld(struct irq_data *data) { unsigned long port; unsigned int pldirq; - pldirq = irq2pldirq(irq); + pldirq = irq2pldirq(data->irq); port = pldirq2port(pldirq); outw(PLD_ICUCR_ILEVEL7, port); } static struct irq_chip m32700ut_pld_irq_type = { - .name = "USRV-PLD-IRQ", - .startup = startup_m32700ut_pld_irq, - .shutdown = shutdown_m32700ut_pld_irq, - .enable = enable_m32700ut_pld_irq, - .disable = disable_m32700ut_pld_irq, - .ack = mask_and_ack_m32700ut_pld, - .end = end_m32700ut_pld_irq + .name = "USRV-PLD-IRQ", + .irq_shutdown = shutdown_m32700ut_pld, + .irq_mask = mask_m32700ut_pld, + .irq_unmask = unmask_m32700ut_pld, }; void __init init_IRQ(void) @@ -156,35 +138,42 @@ void __init init_IRQ(void) once++; /* MFT2 : system timer */ - set_irq_chip(M32R_IRQ_MFT2, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_MFT2, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_MFT2].icucr = M32R_ICUCR_IEN; disable_mappi_irq(M32R_IRQ_MFT2); #if defined(CONFIG_SERIAL_M32R_SIO) /* SIO0_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO0_R, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_R, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_R); /* SIO0_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO0_S, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO0_S, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO0_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO0_S); /* SIO1_R : uart receive data */ - set_irq_chip(M32R_IRQ_SIO1_R, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_R, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_R].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_R); /* SIO1_S : uart send data */ - set_irq_chip(M32R_IRQ_SIO1_S, &mappi_irq_type); + set_irq_chip_and_handler(M32R_IRQ_SIO1_S, &mappi_irq_type, + handle_level_irq); icu_data[M32R_IRQ_SIO1_S].icucr = 0; disable_mappi_irq(M32R_IRQ_SIO1_S); #endif /* CONFIG_SERIAL_M32R_SIO */ /* INT#67-#71: CFC#0 IREQ on PLD */ for (i = 0 ; i < CONFIG_M32R_CFC_NUM ; i++ ) { - set_irq_chip(PLD_IRQ_CF0 + i, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_CF0 + i, + &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_CF0 + i)].icucr = PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_CF0 + i); @@ -192,13 +181,15 @@ void __init init_IRQ(void) #if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE) /* INT#76: 16552D#0 IREQ on PLD */ - set_irq_chip(PLD_IRQ_UART0, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_UART0, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_UART0)].icucr = PLD_ICUCR_ISMOD03; /* 'H' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_UART0); /* INT#77: 16552D#1 IREQ on PLD */ - set_irq_chip(PLD_IRQ_UART1, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_UART1, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_UART1)].icucr = PLD_ICUCR_ISMOD03; /* 'H' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_UART1); @@ -206,7 +197,8 @@ void __init init_IRQ(void) #if defined(CONFIG_IDC_AK4524) || defined(CONFIG_IDC_AK4524_MODULE) /* INT#80: AK4524 IREQ on PLD */ - set_irq_chip(PLD_IRQ_SNDINT, &m32700ut_pld_irq_type); + set_irq_chip_and_handler(PLD_IRQ_SNDINT, &m32700ut_pld_irq_type, + handle_level_irq); pld_icu_data[irq2pldirq(PLD_IRQ_SNDINT)].icucr = PLD_ICUCR_ISMOD01; /* 'L' level sense */ disable_m32700ut_pld_irq(PLD_IRQ_SNDINT); -- cgit v1.2.3 From 0e155b2ce293382730b8473f6ceeb7ac9b939b7d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 19:17:10 +0100 Subject: m32r: Convert to generic irq Kconfig Use the generic irq Kconfig. Select GENERIC_HARDIRQS_NO_DEPRECATED as we have converted all irq_chip functions. Signed-off-by: Thomas Gleixner Cc: Hirokazu Takata Cc: Paul Mundt --- arch/m32r/Kconfig | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index 5c291d65196b..ef4c1e442be3 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -7,6 +7,9 @@ config M32R select HAVE_KERNEL_GZIP select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_LZMA + select HAVE_GENERIC_HARDIRQS + select GENERIC_HARDIRQS_NO_DEPRECATED + select GENERIC_IRQ_PROBE config SBUS bool @@ -19,14 +22,6 @@ config ZONE_DMA bool default y -config GENERIC_HARDIRQS - bool - default y - -config GENERIC_IRQ_PROBE - bool - default y - config NO_IOPORT def_bool y -- cgit v1.2.3 From 1c77ff22f539ceaa64ea43d6a26d867e84602cb7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 19:41:35 +0100 Subject: genirq: Remove __do_IRQ All architectures are finally converted. Remove the cruft. Signed-off-by: Thomas Gleixner Cc: Richard Henderson Cc: Mike Frysinger Cc: David Howells Cc: Tony Luck Cc: Greg Ungerer Cc: Michal Simek Acked-by: David Howells Cc: Kyle McMartin Acked-by: Benjamin Herrenschmidt Cc: Chen Liqin Cc: "David S. Miller" Cc: Chris Metcalf Cc: Jeff Dike --- Documentation/feature-removal-schedule.txt | 8 --- arch/alpha/Kconfig | 3 - arch/blackfin/Kconfig | 3 - arch/frv/Kconfig | 4 -- arch/ia64/Kconfig | 3 - arch/m68knommu/Kconfig | 4 -- arch/microblaze/Kconfig | 3 - arch/mips/Kconfig | 3 - arch/mn10300/Kconfig | 3 - arch/parisc/Kconfig | 4 -- arch/powerpc/Kconfig | 4 -- arch/score/Kconfig | 3 - arch/sparc/Kconfig | 4 -- arch/tile/Kconfig | 3 - arch/um/Kconfig.um | 3 - include/linux/irqdesc.h | 14 ---- kernel/irq/Kconfig | 3 - kernel/irq/handle.c | 111 ----------------------------- 18 files changed, 183 deletions(-) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 8c594c45b6a1..b959659c5df4 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -357,14 +357,6 @@ Who: Dave Jones , Matthew Garrett ----------------------------- -What: __do_IRQ all in one fits nothing interrupt handler -When: 2.6.32 -Why: __do_IRQ was kept for easy migration to the type flow handlers. - More than two years of migration time is enough. -Who: Thomas Gleixner - ------------------------------ - What: fakephp and associated sysfs files in /sys/bus/pci/slots/ When: 2011 Why: In 2.6.27, the semantics of /sys/bus/pci/slots was redefined to diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index fc95ee1bcf6f..943fe6930f77 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -68,9 +68,6 @@ config GENERIC_IOMAP bool default n -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_HARDIRQS bool default y diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 0a221d48152d..a37b2be23f18 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -50,9 +50,6 @@ config GENERIC_HARDIRQS config GENERIC_IRQ_PROBE def_bool y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_GPIO def_bool y diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index f6bcb039cd6d..e504edeb3d84 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -33,10 +33,6 @@ config GENERIC_HARDIRQS bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - bool - default y - config TIME_LOW_RES bool default y diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index e0f5b6d7f849..be1faf991813 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -684,9 +684,6 @@ source "lib/Kconfig" config GENERIC_HARDIRQS def_bool y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_IRQ_PROBE bool default y diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index 704e7b92334c..7379cb0ce1af 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -52,10 +52,6 @@ config GENERIC_HARDIRQS bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - bool - default y - config GENERIC_CALIBRATE_DELAY bool default y diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 5f5018a71a3d..5a6378493797 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -52,9 +52,6 @@ config GENERIC_TIME_VSYSCALL config GENERIC_CLOCKEVENTS def_bool y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_GPIO def_bool y diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 548e6cc3bc28..f5ecc0566bc2 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -793,9 +793,6 @@ config SCHED_OMIT_FRAME_POINTER bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - # # Select some configuration options automatically based on user selections. # diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 8ed41cf2b08d..4638269152f6 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -34,9 +34,6 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_CALIBRATE_DELAY def_bool y diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 0888675c98dd..4b94ac4dc603 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -12,7 +12,6 @@ config PARISC select HAVE_IRQ_WORK select HAVE_PERF_EVENTS select GENERIC_ATOMIC64 if !64BIT - select GENERIC_HARDIRQS_NO__DO_IRQ help The PA-RISC microprocessor is designed by Hewlett-Packard and used in many of their workstations & servers (HP9000 700 and 800 series, @@ -79,9 +78,6 @@ config IRQ_PER_CPU bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - # unless you want to implement ACPI on PA-RISC ... ;-) config PM bool diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 959f38ccb9a7..e0b185dd718a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -40,10 +40,6 @@ config GENERIC_HARDIRQS bool default y -config GENERIC_HARDIRQS_NO__DO_IRQ - bool - default y - config HAVE_SETUP_PER_CPU_AREA def_bool PPC64 diff --git a/arch/score/Kconfig b/arch/score/Kconfig index 4293fdcb5398..4f159acfbe33 100644 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -53,9 +53,6 @@ config GENERIC_CLOCKEVENTS config SCHED_NO_NO_OMIT_FRAME_POINTER def_bool y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_SYSCALL_TABLE def_bool y diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 45d9c87d083a..989bb6415ea3 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -107,10 +107,6 @@ config NEED_PER_CPU_EMBED_FIRST_CHUNK config NEED_PER_CPU_PAGE_FIRST_CHUNK def_bool y if SPARC64 -config GENERIC_HARDIRQS_NO__DO_IRQ - bool - def_bool y if SPARC64 - config MMU bool default y diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index 4e8b82bca9e5..c16b98c2435d 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -10,9 +10,6 @@ config GENERIC_CSUM config GENERIC_HARDIRQS def_bool y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config GENERIC_IRQ_PROBE def_bool y diff --git a/arch/um/Kconfig.um b/arch/um/Kconfig.um index f8d1d0d47fe6..90a438acbfaf 100644 --- a/arch/um/Kconfig.um +++ b/arch/um/Kconfig.um @@ -120,9 +120,6 @@ config SMP If you don't know what to do, say N. -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - config NR_CPUS int "Maximum number of CPUs (2-32)" range 2 32 diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 6a64c6fa81af..c1a95b7b58de 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -100,13 +100,6 @@ static inline struct irq_desc *move_irq_desc(struct irq_desc *desc, int node) #define get_irq_desc_data(desc) ((desc)->irq_data.handler_data) #define get_irq_desc_msi(desc) ((desc)->irq_data.msi_desc) -/* - * Monolithic do_IRQ implementation. - */ -#ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ -extern unsigned int __do_IRQ(unsigned int irq); -#endif - /* * Architectures call this to let the generic IRQ layer * handle an interrupt. If the descriptor is attached to an @@ -115,14 +108,7 @@ extern unsigned int __do_IRQ(unsigned int irq); */ static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc) { -#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ desc->handle_irq(irq, desc); -#else - if (likely(desc->handle_irq)) - desc->handle_irq(irq, desc); - else - __do_IRQ(irq); -#endif } static inline void generic_handle_irq(unsigned int irq) diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index 31d766bf5d2e..8e42fec7686d 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -9,9 +9,6 @@ menu "IRQ subsystem" config GENERIC_HARDIRQS def_bool y -config GENERIC_HARDIRQS_NO__DO_IRQ - def_bool y - # Select this to disable the deprecated stuff config GENERIC_HARDIRQS_NO_DEPRECATED def_bool n diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index e2347eb63306..3540a7190122 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -118,114 +118,3 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) return retval; } - -#ifndef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ - -#ifdef CONFIG_ENABLE_WARN_DEPRECATED -# warning __do_IRQ is deprecated. Please convert to proper flow handlers -#endif - -/** - * __do_IRQ - original all in one highlevel IRQ handler - * @irq: the interrupt number - * - * __do_IRQ handles all normal device IRQ's (the special - * SMP cross-CPU interrupts have their own specific - * handlers). - * - * This is the original x86 implementation which is used for every - * interrupt type. - */ -unsigned int __do_IRQ(unsigned int irq) -{ - struct irq_desc *desc = irq_to_desc(irq); - struct irqaction *action; - unsigned int status; - - kstat_incr_irqs_this_cpu(irq, desc); - - if (CHECK_IRQ_PER_CPU(desc->status)) { - irqreturn_t action_ret; - - /* - * No locking required for CPU-local interrupts: - */ - if (desc->irq_data.chip->ack) - desc->irq_data.chip->ack(irq); - if (likely(!(desc->status & IRQ_DISABLED))) { - action_ret = handle_IRQ_event(irq, desc->action); - if (!noirqdebug) - note_interrupt(irq, desc, action_ret); - } - desc->irq_data.chip->end(irq); - return 1; - } - - raw_spin_lock(&desc->lock); - if (desc->irq_data.chip->ack) - desc->irq_data.chip->ack(irq); - /* - * REPLAY is when Linux resends an IRQ that was dropped earlier - * WAITING is used by probe to mark irqs that are being tested - */ - status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); - status |= IRQ_PENDING; /* we _want_ to handle it */ - - /* - * If the IRQ is disabled for whatever reason, we cannot - * use the action we have. - */ - action = NULL; - if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { - action = desc->action; - status &= ~IRQ_PENDING; /* we commit to handling */ - status |= IRQ_INPROGRESS; /* we are handling it */ - } - desc->status = status; - - /* - * If there is no IRQ handler or it was disabled, exit early. - * Since we set PENDING, if another processor is handling - * a different instance of this same irq, the other processor - * will take care of it. - */ - if (unlikely(!action)) - goto out; - - /* - * Edge triggered interrupts need to remember - * pending events. - * This applies to any hw interrupts that allow a second - * instance of the same irq to arrive while we are in do_IRQ - * or in the handler. But the code here only handles the _second_ - * instance of the irq, not the third or fourth. So it is mostly - * useful for irq hardware that does not mask cleanly in an - * SMP environment. - */ - for (;;) { - irqreturn_t action_ret; - - raw_spin_unlock(&desc->lock); - - action_ret = handle_IRQ_event(irq, action); - if (!noirqdebug) - note_interrupt(irq, desc, action_ret); - - raw_spin_lock(&desc->lock); - if (likely(!(desc->status & IRQ_PENDING))) - break; - desc->status &= ~IRQ_PENDING; - } - desc->status &= ~IRQ_INPROGRESS; - -out: - /* - * The ->end() handler has to deal with interrupts which got - * disabled while the handler was running. - */ - desc->irq_data.chip->end(irq); - raw_spin_unlock(&desc->lock); - - return 1; -} -#endif -- cgit v1.2.3 From e27ec649a0dd49741c98173ac5cf4404912d2abf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:27:11 +0100 Subject: alpha: Use generic irq Kconfig Signed-off-by: Thomas Gleixner Cc: Richard Henderson --- arch/alpha/Kconfig | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 943fe6930f77..47f63d480141 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -8,6 +8,9 @@ config ALPHA select HAVE_IRQ_WORK select HAVE_PERF_EVENTS select HAVE_DMA_ATTRS + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE + select AUTO_IRQ_AFFINITY if SMP help The Alpha is a 64-bit general-purpose processor designed and marketed by the Digital Equipment Corporation of blessed memory, @@ -68,19 +71,6 @@ config GENERIC_IOMAP bool default n -config GENERIC_HARDIRQS - bool - default y - -config GENERIC_IRQ_PROBE - bool - default y - -config AUTO_IRQ_AFFINITY - bool - depends on SMP - default y - source "init/Kconfig" source "kernel/Kconfig.freezer" -- cgit v1.2.3 From 7b0288634921c748dfeb2ff3e7d04ae7cc94e795 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:29:58 +0100 Subject: blackfin: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Cc: Mike Frysinger --- arch/blackfin/Kconfig | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index a37b2be23f18..c09577ddc3c5 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -30,6 +30,9 @@ config BLACKFIN select HAVE_KERNEL_LZO if RAMKERNEL select HAVE_OPROFILE select ARCH_WANT_OPTIONAL_GPIOLIB + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE + select IRQ_PER_CPU if SMP config GENERIC_CSUM def_bool y @@ -44,12 +47,6 @@ config ZONE_DMA config GENERIC_FIND_NEXT_BIT def_bool y -config GENERIC_HARDIRQS - def_bool y - -config GENERIC_IRQ_PROBE - def_bool y - config GENERIC_GPIO def_bool y @@ -251,11 +248,6 @@ config HOTPLUG_CPU depends on SMP && HOTPLUG default y -config IRQ_PER_CPU - bool - depends on SMP - default y - config HAVE_LEGACY_PER_CPU_AREA def_bool y depends on SMP -- cgit v1.2.3 From f39b02d6f9af8c942961ef32c2bbce6cb6100c29 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:32:04 +0100 Subject: frv: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Acked-by: David Howells --- arch/frv/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index e504edeb3d84..747499a1b31e 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -5,6 +5,7 @@ config FRV select HAVE_ARCH_TRACEHOOK select HAVE_IRQ_WORK select HAVE_PERF_EVENTS + select HAVE_GENERIC_HARDIRQS config ZONE_DMA bool @@ -29,10 +30,6 @@ config GENERIC_CALIBRATE_DELAY bool default n -config GENERIC_HARDIRQS - bool - default y - config TIME_LOW_RES bool default y -- cgit v1.2.3 From c5e66129c1b5b6ea73128ce21b3591c00c963c0e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:32:46 +0100 Subject: ia64: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Acked-by: Tony Luck --- arch/ia64/Kconfig | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index be1faf991813..fcf3b437a2d9 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -22,6 +22,10 @@ config IA64 select HAVE_KVM select HAVE_ARCH_TRACEHOOK select HAVE_DMA_API_DEBUG + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE + select GENERIC_PENDING_IRQ if SMP + select IRQ_PER_CPU default y help The Itanium Processor Family is Intel's 64-bit successor to @@ -678,25 +682,6 @@ source "arch/ia64/kvm/Kconfig" source "lib/Kconfig" -# -# Use the generic interrupt handling code in kernel/irq/: -# -config GENERIC_HARDIRQS - def_bool y - -config GENERIC_IRQ_PROBE - bool - default y - -config GENERIC_PENDING_IRQ - bool - depends on GENERIC_HARDIRQS && SMP - default y - -config IRQ_PER_CPU - bool - default y - config IOMMU_HELPER def_bool (IA64_HP_ZX1 || IA64_HP_ZX1_SWIOTLB || IA64_GENERIC || SWIOTLB) -- cgit v1.2.3 From d8b19323478af44d243e500656bb6ef2860ced6e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:34:21 +0100 Subject: m68knommu: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Cc: Greg Ungerer --- arch/m68knommu/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index 7379cb0ce1af..8b9dacaa0f6e 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -2,6 +2,7 @@ config M68K bool default y select HAVE_IDE + select HAVE_GENERIC_HARDIRQS config MMU bool @@ -48,10 +49,6 @@ config GENERIC_HWEIGHT bool default y -config GENERIC_HARDIRQS - bool - default y - config GENERIC_CALIBRATE_DELAY bool default y -- cgit v1.2.3 From b6e31629e335e8664a11be05992d39b2ee030960 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:35:05 +0100 Subject: microblaze: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Acked-by: Michal Simek --- arch/microblaze/Kconfig | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index 5a6378493797..31680032053e 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -15,6 +15,8 @@ config MICROBLAZE select TRACING_SUPPORT select OF select OF_EARLY_FLATTREE + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE config SWAP def_bool n @@ -37,12 +39,6 @@ config GENERIC_FIND_NEXT_BIT config GENERIC_HWEIGHT def_bool y -config GENERIC_HARDIRQS - def_bool y - -config GENERIC_IRQ_PROBE - def_bool y - config GENERIC_CALIBRATE_DELAY def_bool y -- cgit v1.2.3 From b169c908a51964822dc3aa828a3febb67933f1ac Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:36:02 +0100 Subject: mn10300: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Acked-by: David Howells --- arch/mn10300/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index 4638269152f6..243bfa23fd58 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -1,6 +1,7 @@ config MN10300 def_bool y select HAVE_OPROFILE + select GENERIC_HARDIRQS config AM33_2 def_bool n @@ -76,10 +77,6 @@ config QUICKLIST config ARCH_HAS_ILOG2_U32 def_bool y -# Use the generic interrupt handling code in kernel/irq/ -config GENERIC_HARDIRQS - def_bool y - config HOTPLUG_CPU def_bool n -- cgit v1.2.3 From 72e73af1f6546ba08d9314ac0e99dcdb06b7d486 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:38:30 +0100 Subject: parisc: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Cc: Kyle McMartin Acked-by: James Bottomley --- arch/parisc/Kconfig | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 4b94ac4dc603..fed2946f7335 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -12,6 +12,10 @@ config PARISC select HAVE_IRQ_WORK select HAVE_PERF_EVENTS select GENERIC_ATOMIC64 if !64BIT + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE + select IRQ_PER_CPU + help The PA-RISC microprocessor is designed by Hewlett-Packard and used in many of their workstations & servers (HP9000 700 and 800 series, @@ -65,19 +69,9 @@ config TIME_LOW_RES depends on SMP default y -config GENERIC_HARDIRQS - def_bool y - -config GENERIC_IRQ_PROBE - def_bool y - config HAVE_LATENCYTOP_SUPPORT def_bool y -config IRQ_PER_CPU - bool - default y - # unless you want to implement ACPI on PA-RISC ... ;-) config PM bool -- cgit v1.2.3 From 2604362b2671cef309f6023e6bea0cec52ee993b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:39:39 +0100 Subject: powerpc: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Cc: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index e0b185dd718a..7d69e9bf5e64 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -36,20 +36,12 @@ config GENERIC_TIME_VSYSCALL config GENERIC_CLOCKEVENTS def_bool y -config GENERIC_HARDIRQS - bool - default y - config HAVE_SETUP_PER_CPU_AREA def_bool PPC64 config NEED_PER_CPU_EMBED_FIRST_CHUNK def_bool PPC64 -config IRQ_PER_CPU - bool - default y - config NR_IRQS int "Number of virtual interrupt numbers" range 32 32768 @@ -139,6 +131,9 @@ config PPC select HAVE_PERF_EVENTS select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_HW_BREAKPOINT if PERF_EVENTS && PPC_BOOK3S_64 + select HAVE_GENERIC_HARDIRQS + select HAVE_SPARSE_IRQ + select IRQ_PER_CPU config EARLY_PRINTK bool @@ -388,19 +383,6 @@ config IRQ_ALL_CPUS CPU. Generally saying Y is safe, although some problems have been reported with SMP Power Macintoshes with this option enabled. -config SPARSE_IRQ - bool "Support sparse irq numbering" - default n - help - This enables support for sparse irqs. This is useful for distro - kernels that want to define a high CONFIG_NR_CPUS value but still - want to have low kernel memory footprint on smaller machines. - - ( Sparse IRQs can also be beneficial on NUMA boxes, as they spread - out the irq_desc[] array in a more NUMA-friendly way. ) - - If you don't know what to do here, say N. - config NUMA bool "NUMA support" depends on PPC64 -- cgit v1.2.3 From 2fc361bfcfce4fd05eb3d2e3d400dd387a5724c4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:41:19 +0100 Subject: score: Use generic irq Kconfig No functional change Signed-off-by: Thomas Gleixner Acked-by: Chen Liqin --- arch/score/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/score/Kconfig b/arch/score/Kconfig index 4f159acfbe33..27b2295f41f3 100644 --- a/arch/score/Kconfig +++ b/arch/score/Kconfig @@ -1,5 +1,9 @@ menu "Machine selection" +config SCORE + def_bool y + select HAVE_GENERIC_HARDIRQS + choice prompt "System type" default MACH_SPCT6600 @@ -65,9 +69,6 @@ menu "Kernel type" config 32BIT def_bool y -config GENERIC_HARDIRQS - def_bool y - config ARCH_FLATMEM_ENABLE def_bool y -- cgit v1.2.3 From 373b25c3bff14fd9ed30b6ddba49cbeeb7ccdf88 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:43:56 +0100 Subject: sparc: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Acked-by: "David S. Miller" --- arch/sparc/Kconfig | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 989bb6415ea3..95695e97703e 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -50,6 +50,7 @@ config SPARC64 select RTC_DRV_STARFIRE select HAVE_PERF_EVENTS select PERF_USE_VMALLOC + select HAVE_GENERIC_HARDIRQS config ARCH_DEFCONFIG string @@ -272,10 +273,6 @@ config HOTPLUG_CPU can be controlled through /sys/devices/system/cpu/cpu#. Say N if you want to disable CPU hotplug. -config GENERIC_HARDIRQS - bool - default y if SPARC64 - source "kernel/time/Kconfig" if SPARC64 -- cgit v1.2.3 From 8df274064e02aa8929eb5087dd8841b6601ab0ae Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:44:43 +0100 Subject: tile: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Acked-by: Chris Metcalf --- arch/tile/Kconfig | 50 +++++++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index c16b98c2435d..08948e4e1503 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig @@ -1,21 +1,33 @@ # For a description of the syntax of this configuration file, # see Documentation/kbuild/config-language.txt. -config MMU - def_bool y - -config GENERIC_CSUM +config TILE def_bool y + select HAVE_KVM if !TILEGX + select GENERIC_FIND_FIRST_BIT + select GENERIC_FIND_NEXT_BIT + select USE_GENERIC_SMP_HELPERS + select CC_OPTIMIZE_FOR_SIZE + select HAVE_GENERIC_HARDIRQS + select GENERIC_IRQ_PROBE + select GENERIC_PENDING_IRQ if SMP -config GENERIC_HARDIRQS - def_bool y +# FIXME: investigate whether we need/want these options. +# select HAVE_IOREMAP_PROT +# select HAVE_OPTPROBES +# select HAVE_REGS_AND_STACK_ACCESS_API +# select HAVE_HW_BREAKPOINT +# select PERF_EVENTS +# select HAVE_USER_RETURN_NOTIFIER +# config NO_BOOTMEM +# config ARCH_SUPPORTS_DEBUG_PAGEALLOC +# config HUGETLB_PAGE_SIZE_VARIABLE -config GENERIC_IRQ_PROBE +config MMU def_bool y -config GENERIC_PENDING_IRQ +config GENERIC_CSUM def_bool y - depends on GENERIC_HARDIRQS && SMP config SEMAPHORE_SLEEPERS def_bool y @@ -94,26 +106,6 @@ config HVC_TILE select HVC_DRIVER def_bool y -config TILE - def_bool y - select HAVE_KVM if !TILEGX - select GENERIC_FIND_FIRST_BIT - select GENERIC_FIND_NEXT_BIT - select USE_GENERIC_SMP_HELPERS - select CC_OPTIMIZE_FOR_SIZE - -# FIXME: investigate whether we need/want these options. -# select HAVE_IOREMAP_PROT -# select HAVE_OPTPROBES -# select HAVE_REGS_AND_STACK_ACCESS_API -# select HAVE_HW_BREAKPOINT -# select PERF_EVENTS -# select HAVE_USER_RETURN_NOTIFIER -# config NO_BOOTMEM -# config ARCH_SUPPORTS_DEBUG_PAGEALLOC -# config HUGETLB_PAGE_SIZE_VARIABLE - - # Please note: TILE-Gx support is not yet finalized; this is # the preliminary support. TILE-Gx drivers are only provided # with the alpha or beta test versions for Tilera customers. -- cgit v1.2.3 From dfff95c394b0dd977a6b65bd52b99703fae94d9b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 19 Jan 2011 20:46:24 +0100 Subject: um: Use generic irq Kconfig No functional change. Signed-off-by: Thomas Gleixner Cc: Andrew Morton Cc: Jeff Dike --- arch/um/Kconfig.common | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index 049d048b070d..e351e14b4339 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common @@ -3,14 +3,10 @@ config DEFCONFIG_LIST option defconfig_list default "arch/$ARCH/defconfig" -# UML uses the generic IRQ subsystem -config GENERIC_HARDIRQS - bool - default y - config UML bool default y + select HAVE_GENERIC_HARDIRQS config MMU bool -- cgit v1.2.3 From cc67ba6352ecbf6891083a18f8c78fa639ebd274 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 20 Jan 2011 20:32:14 +0900 Subject: x86: Use asm-generic/cacheflush.h The implementation of the cache flushing interfaces on the x86 is identical with the default implementation in asm-generic. Signed-off-by: Akinobu Mita Cc: "H. Peter Anvin" Cc: arnd@arndb.de LKML-Reference: <1295523136-4277-2-git-send-email-akinobu.mita@gmail.com> Signed-off-by: Ingo Molnar --- arch/x86/include/asm/cacheflush.h | 42 +-------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/arch/x86/include/asm/cacheflush.h b/arch/x86/include/asm/cacheflush.h index 63e35ec9075c..62f084478f7e 100644 --- a/arch/x86/include/asm/cacheflush.h +++ b/arch/x86/include/asm/cacheflush.h @@ -1,48 +1,8 @@ #ifndef _ASM_X86_CACHEFLUSH_H #define _ASM_X86_CACHEFLUSH_H -/* Keep includes the same across arches. */ -#include - /* Caches aren't brain-dead on the intel. */ -static inline void flush_cache_all(void) { } -static inline void flush_cache_mm(struct mm_struct *mm) { } -static inline void flush_cache_dup_mm(struct mm_struct *mm) { } -static inline void flush_cache_range(struct vm_area_struct *vma, - unsigned long start, unsigned long end) { } -static inline void flush_cache_page(struct vm_area_struct *vma, - unsigned long vmaddr, unsigned long pfn) { } -#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0 -static inline void flush_dcache_page(struct page *page) { } -static inline void flush_dcache_mmap_lock(struct address_space *mapping) { } -static inline void flush_dcache_mmap_unlock(struct address_space *mapping) { } -static inline void flush_icache_range(unsigned long start, - unsigned long end) { } -static inline void flush_icache_page(struct vm_area_struct *vma, - struct page *page) { } -static inline void flush_icache_user_range(struct vm_area_struct *vma, - struct page *page, - unsigned long addr, - unsigned long len) { } -static inline void flush_cache_vmap(unsigned long start, unsigned long end) { } -static inline void flush_cache_vunmap(unsigned long start, - unsigned long end) { } - -static inline void copy_to_user_page(struct vm_area_struct *vma, - struct page *page, unsigned long vaddr, - void *dst, const void *src, - unsigned long len) -{ - memcpy(dst, src, len); -} - -static inline void copy_from_user_page(struct vm_area_struct *vma, - struct page *page, unsigned long vaddr, - void *dst, const void *src, - unsigned long len) -{ - memcpy(dst, src, len); -} +#include #ifdef CONFIG_X86_PAT /* -- cgit v1.2.3 From f21bbec9ffcaf73e99938209e52b757d93f9133b Mon Sep 17 00:00:00 2001 From: Fenghua Yu Date: Thu, 20 Jan 2011 20:12:40 -0800 Subject: x86, mcheck, therm_throt.c: Export symbol platform_thermal_notify to allow coretemp to handler intr In therm_throt.c, commit 9e76a97efd31a08cb19d0ba12013b8fb4ad3e474 patch doesn't export the symbol platform_thermal_notify. Other drivers (e.g. drivers/hwmon/coretemp.c) can not find the symbol platform_thermal_notify when defining threshould interrupt handler. Please apply this patch to allow threshold interrupt handler in coretemp. Signed-off-by: Fenghua Yu Cc: R Durgadoss Cc: khali@linux-fr.org Cc: lm-sensors@lm-sensors.org Cc: Guenter Roeck LKML-Reference: <20110121041239.GB26954@linux-os.sc.intel.com> Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/mcheck/therm_throt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index e12246ff5aa6..6f8c5e9da97f 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -59,6 +59,7 @@ struct thermal_state { /* Callback to handle core threshold interrupts */ int (*platform_thermal_notify)(__u64 msr_val); +EXPORT_SYMBOL(platform_thermal_notify); static DEFINE_PER_CPU(struct thermal_state, thermal_state); -- cgit v1.2.3 From 547e9fd7d328af261f184bf66effc5033c886498 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 19 Jan 2011 12:51:39 +0100 Subject: perf: Annotate cpuctx->ctx.mutex to avoid a lockdep splat Lockdep spotted: loop_1b_instruc/1899 is trying to acquire lock: (event_mutex){+.+.+.}, at: [] perf_trace_init+0x3b/0x2f7 but task is already holding lock: (&ctx->mutex){+.+.+.}, at: [] perf_event_init_context+0xc0/0x218 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #3 (&ctx->mutex){+.+.+.}: -> #2 (cpu_hotplug.lock){+.+.+.}: -> #1 (module_mutex){+.+...}: -> #0 (event_mutex){+.+.+.}: But because the deadlock would be cpuhotplug (cpu-event) vs fork (task-event) it cannot, in fact, happen. We can annotate this by giving the perf_event_context used for the cpuctx a different lock class from those used by tasks. Signed-off-by: Peter Zijlstra LKML-Reference: Signed-off-by: Ingo Molnar --- kernel/perf_event.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 244ca3acb0ee..c5fa717cf099 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -5380,6 +5380,8 @@ free_dev: goto out; } +static struct lock_class_key cpuctx_mutex; + int perf_pmu_register(struct pmu *pmu, char *name, int type) { int cpu, ret; @@ -5428,6 +5430,7 @@ skip_type: cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu); __perf_event_init_context(&cpuctx->ctx); + lockdep_set_class(&cpuctx->ctx.mutex, &cpuctx_mutex); cpuctx->ctx.type = cpu_context; cpuctx->ctx.pmu = pmu; cpuctx->jiffies_interval = 1; -- cgit v1.2.3 From 8e1b4cf2108488ccfb9a3e7ed7cd85a435e01d4b Mon Sep 17 00:00:00 2001 From: Stefan Bader Date: Thu, 20 Jan 2011 15:38:23 +0100 Subject: xen: p2m: correctly initialize partial p2m leaf After changing the p2m mapping to a tree by commit 58e05027b530ff081ecea68e38de8d59db8f87e0 xen: convert p2m to a 3 level tree and trying to boot a DomU with 615MB of memory, the following crash was observed in the dump: kernel direct mapping tables up to 26f00000 @ 1ec4000-1fff000 BUG: unable to handle kernel NULL pointer dereference at (null) IP: [] xen_set_pte+0x27/0x60 *pdpt = 0000000000000000 *pde = 0000000000000000 Adding further debug statements showed that when trying to set up pfn=0x26700 the returned mapping was invalid. pfn=0x266ff calling set_pte(0xc1fe77f8, 0x6b3003) pfn=0x26700 calling set_pte(0xc1fe7800, 0x3) Although the last_pfn obtained from the startup info is 0x26700, which should in turn not be hit, the additional 8MB which are added as extra memory normally seem to be ok. This lead to looking into the initial p2m tree construction, which uses the smaller value and assuming that there is other code handling the extra memory. When the p2m tree is set up, the leaves are directly pointed to the array which the domain builder set up. But if the mapping is not on a boundary that fits into one p2m page, this will result in the last leaf being only partially valid. And as the invalid entries are not initialized in that case, things go badly wrong. I am trying to fix that by checking whether the current leaf is a complete map and if not, allocate a completely new page and copy only the valid pointers there. This may not be the most efficient or elegant solution, but at least it seems to allow me booting DomUs with memory assignments all over the range. BugLink: http://bugs.launchpad.net/bugs/686692 [v2: Redid a bit of commit wording and fixed a compile warning] Signed-off-by: Stefan Bader Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/p2m.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 8f2251d2a3f8..ddc81a06edb9 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -237,7 +237,25 @@ void __init xen_build_dynamic_phys_to_machine(void) p2m_top[topidx] = mid; } - p2m_top[topidx][mididx] = &mfn_list[pfn]; + /* + * As long as the mfn_list has enough entries to completely + * fill a p2m page, pointing into the array is ok. But if + * not the entries beyond the last pfn will be undefined. + * And guessing that the 'what-ever-there-is' does not take it + * too kindly when changing it to invalid markers, a new page + * is allocated, initialized and filled with the valid part. + */ + if (unlikely(pfn + P2M_PER_PAGE > max_pfn)) { + unsigned long p2midx; + unsigned long *p2m = extend_brk(PAGE_SIZE, PAGE_SIZE); + p2m_init(p2m); + + for (p2midx = 0; pfn + p2midx < max_pfn; p2midx++) { + p2m[p2midx] = mfn_list[pfn + p2midx]; + } + p2m_top[topidx][mididx] = p2m; + } else + p2m_top[topidx][mididx] = &mfn_list[pfn]; } m2p_override_init(); -- cgit v1.2.3 From 1daeddd5962acad1bea55e524fc0fadf32654a21 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 13 Jan 2011 09:30:49 -0800 Subject: rtc: Cleanup removed UIE emulation declaration rtc_dev_update_irq_enable_emul was removed in commit 042620a018afcfba1d678062b62e463b9e43a68d (UIE emulation is now handled via hrtimer), but the declaration was missed. This patch cleans it up. Signed-off-by: John Stultz LKML-Reference: <1294939849-20608-1-git-send-email-john.stultz@linaro.org> Signed-off-by: Thomas Gleixner --- include/linux/rtc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 3c995b4d742c..ea760698e6fe 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -235,8 +235,6 @@ extern int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq); extern int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled); extern int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled); -extern int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, - unsigned int enabled); void rtc_aie_update_irq(void *private); void rtc_uie_update_irq(void *private); -- cgit v1.2.3 From db6b175fa6ad1408cbb2fb62949a6d55cfece03e Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 14 Jan 2011 09:47:26 -0800 Subject: acpi_pm: Clear pmtmr_ioport if acpi_pm initialization fails If the acpi pm timer throws invalid data, clear pmtmr_ioport so the pm timer won't accidentally be used. This was found when using Xen where there is a acpi pm reported, but gives bogus values, and other code was continuing to try to use the pm timer after the initialization failed. [jstultz: Catch additional failure and reword changelog message. ] Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: John Stultz LKML-Reference: <1295027246-11110-1-git-send-email-johnstul@us.ibm.com> Signed-off-by: Thomas Gleixner --- drivers/clocksource/acpi_pm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index cfb0f5278415..effe7974aa9a 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -202,17 +202,21 @@ static int __init init_acpi_pm_clocksource(void) printk(KERN_INFO "PM-Timer had inconsistent results:" " 0x%#llx, 0x%#llx - aborting.\n", value1, value2); + pmtmr_ioport = 0; return -EINVAL; } if (i == ACPI_PM_READ_CHECKS) { printk(KERN_INFO "PM-Timer failed consistency check " " (0x%#llx) - aborting.\n", value1); + pmtmr_ioport = 0; return -ENODEV; } } - if (verify_pmtmr_rate() != 0) + if (verify_pmtmr_rate() != 0){ + pmtmr_ioport = 0; return -ENODEV; + } return clocksource_register_hz(&clocksource_acpi_pm, PMTMR_TICKS_PER_SEC); -- cgit v1.2.3 From aa0be0f4659f91f31e45adc422b1788cb36ffddc Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 20 Jan 2011 15:26:12 -0800 Subject: RTC: Propagate error handling via rtc_timer_enqueue properly In cases where RTC hardware does not support alarms, the virtualized RTC interfaces did not have a way to propagate the error up to userland. This patch extends rtc_timer_enqueue so it catches errors from the hardware and returns them upwards to the virtualized interfaces. To simplify error handling, it also internalizes the management of the timer->enabled bit into rtc_timer_enqueue and rtc_timer_remove. Also makes rtc_timer_enqueue and rtc_timer_remove static. Reported-by: David Daney Reported-by: Andreas Schwab Reported-by: Geert Uytterhoeven Diagnosed-by: David Daney Tested-by: David Daney Signed-off-by: John Stultz LKML-Reference: <1295565973-14358-1-git-send-email-john.stultz@linaro.org> Signed-off-by: Thomas Gleixner --- drivers/rtc/interface.c | 49 ++++++++++++++++++++++++++++--------------------- include/linux/rtc.h | 2 -- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 90384b9f6b2c..f1ba2c696528 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -16,6 +16,9 @@ #include #include +static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); +static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); + static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) { int err; @@ -175,16 +178,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) return err; if (rtc->aie_timer.enabled) { rtc_timer_remove(rtc, &rtc->aie_timer); - rtc->aie_timer.enabled = 0; } rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time); rtc->aie_timer.period = ktime_set(0, 0); if (alarm->enabled) { - rtc->aie_timer.enabled = 1; - rtc_timer_enqueue(rtc, &rtc->aie_timer); + err = rtc_timer_enqueue(rtc, &rtc->aie_timer); } mutex_unlock(&rtc->ops_lock); - return 0; + return err; } EXPORT_SYMBOL_GPL(rtc_set_alarm); @@ -195,15 +196,15 @@ int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled) return err; if (rtc->aie_timer.enabled != enabled) { - if (enabled) { - rtc->aie_timer.enabled = 1; - rtc_timer_enqueue(rtc, &rtc->aie_timer); - } else { + if (enabled) + err = rtc_timer_enqueue(rtc, &rtc->aie_timer); + else rtc_timer_remove(rtc, &rtc->aie_timer); - rtc->aie_timer.enabled = 0; - } } + if (err) + return err; + if (!rtc->ops) err = -ENODEV; else if (!rtc->ops->alarm_irq_enable) @@ -235,12 +236,9 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled) now = rtc_tm_to_ktime(tm); rtc->uie_rtctimer.node.expires = ktime_add(now, onesec); rtc->uie_rtctimer.period = ktime_set(1, 0); - rtc->uie_rtctimer.enabled = 1; - rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); - } else { + err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer); + } else rtc_timer_remove(rtc, &rtc->uie_rtctimer); - rtc->uie_rtctimer.enabled = 0; - } out: mutex_unlock(&rtc->ops_lock); @@ -488,10 +486,13 @@ EXPORT_SYMBOL_GPL(rtc_irq_set_freq); * Enqueues a timer onto the rtc devices timerqueue and sets * the next alarm event appropriately. * + * Sets the enabled bit on the added timer. + * * Must hold ops_lock for proper serialization of timerqueue */ -void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) +static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) { + timer->enabled = 1; timerqueue_add(&rtc->timerqueue, &timer->node); if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) { struct rtc_wkalrm alarm; @@ -501,7 +502,13 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) err = __rtc_set_alarm(rtc, &alarm); if (err == -ETIME) schedule_work(&rtc->irqwork); + else if (err) { + timerqueue_del(&rtc->timerqueue, &timer->node); + timer->enabled = 0; + return err; + } } + return 0; } /** @@ -512,13 +519,15 @@ void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) * Removes a timer onto the rtc devices timerqueue and sets * the next alarm event appropriately. * + * Clears the enabled bit on the removed timer. + * * Must hold ops_lock for proper serialization of timerqueue */ -void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) +static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer) { struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); timerqueue_del(&rtc->timerqueue, &timer->node); - + timer->enabled = 0; if (next == &timer->node) { struct rtc_wkalrm alarm; int err; @@ -626,8 +635,7 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, timer->node.expires = expires; timer->period = period; - timer->enabled = 1; - rtc_timer_enqueue(rtc, timer); + ret = rtc_timer_enqueue(rtc, timer); mutex_unlock(&rtc->ops_lock); return ret; @@ -645,7 +653,6 @@ int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer* timer) mutex_lock(&rtc->ops_lock); if (timer->enabled) rtc_timer_remove(rtc, timer); - timer->enabled = 0; mutex_unlock(&rtc->ops_lock); return ret; } diff --git a/include/linux/rtc.h b/include/linux/rtc.h index ea760698e6fe..a0b639f8e805 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -244,8 +244,6 @@ int rtc_register(rtc_task_t *task); int rtc_unregister(rtc_task_t *task); int rtc_control(rtc_task_t *t, unsigned int cmd, unsigned long arg); -void rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer); -void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer); void rtc_timer_init(struct rtc_timer *timer, void (*f)(void* p), void* data); int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer* timer, ktime_t expires, ktime_t period); -- cgit v1.2.3 From d5553a556165535337ece8592f066407c62eec2e Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 20 Jan 2011 15:26:13 -0800 Subject: RTC: Properly handle rtc_read_alarm error propagation and fix bug In reviewing cases where the virtualized interfaces didn't propagate errors properly, I noticed rtc_read_alarm needed fixing. In doing so I noticed my RTC rework dropped a memset and that the behavior of rtc_read_alarm shouldn't be conditionalized on the alarm.enabled flag (as the alarm may be set, but the irqs may be disabled). So those were corrected as well. CC: Thomas Gleixner Signed-off-by: John Stultz LKML-Reference: <1295565973-14358-2-git-send-email-john.stultz@linaro.org> Signed-off-by: Thomas Gleixner --- drivers/rtc/interface.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index f1ba2c696528..925006d33109 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -123,12 +123,18 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = mutex_lock_interruptible(&rtc->ops_lock); if (err) return err; - alarm->enabled = rtc->aie_timer.enabled; - if (alarm->enabled) + if (rtc->ops == NULL) + err = -ENODEV; + else if (!rtc->ops->read_alarm) + err = -EINVAL; + else { + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + alarm->enabled = rtc->aie_timer.enabled; alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires); + } mutex_unlock(&rtc->ops_lock); - return 0; + return err; } EXPORT_SYMBOL_GPL(rtc_read_alarm); -- cgit v1.2.3 From b5cc8ca1c9c3a37eaddf709b2fd3e1699aee41ba Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 21 Jan 2011 16:56:46 +0100 Subject: RTC: Remove Kconfig symbol for UIE emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The effect of changing the value of this symbol is gone since 042620a (RTC: Remove UIE emulation). Remove symbol too. Signed-off-by: Uwe Kleine-König Cc: Alessandro Zummo Cc: Richard Cochran Cc: John Stultz LKML-Reference: <1295625406-15340-1-git-send-email-u.kleine-koenig@pengutronix.de> Signed-off-by: Thomas Gleixner --- drivers/rtc/Kconfig | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4941cade319f..cdd97192dc69 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -97,18 +97,6 @@ config RTC_INTF_DEV If unsure, say Y. -config RTC_INTF_DEV_UIE_EMUL - bool "RTC UIE emulation on dev interface" - depends on RTC_INTF_DEV - help - Provides an emulation for RTC_UIE if the underlying rtc chip - driver does not expose RTC_UIE ioctls. Those requests generate - once-per-second update interrupts, used for synchronization. - - The emulation code will read the time from the hardware - clock several times per second, please enable this option - only if you know that you really need it. - config RTC_DRV_TEST tristate "Test driver/device" help -- cgit v1.2.3 From db9fd8486bfe895b00b3603685c086a3c8210ffe Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Thu, 20 Jan 2011 16:23:08 -0800 Subject: MAINTAINERS: Update Roland Dreier's email address The cisco.com address will stop working soon, and besides no one can remember the second "d" in "rolandd" or how to spell "rdreier." Signed-off-by: Roland Dreier Signed-off-by: Linus Torvalds --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2261749c0aed..55592f8b672c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3150,7 +3150,7 @@ S: Orphan F: drivers/video/imsttfb.c INFINIBAND SUBSYSTEM -M: Roland Dreier +M: Roland Dreier M: Sean Hefty M: Hal Rosenstock L: linux-rdma@vger.kernel.org -- cgit v1.2.3 From 3dece370ecc7c6152b3fdd21c6de28179f6e643d Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 21 Jan 2011 08:49:56 +0100 Subject: mm: System without MMU do not need pte_mkwrite The patch "thp: export maybe_mkwrite" (commit 14fd403f2146) breaks systems without MMU. Error log: CC arch/microblaze/mm/init.o In file included from include/linux/mman.h:14, from arch/microblaze/mm/consistent.c:24: include/linux/mm.h: In function 'maybe_mkwrite': include/linux/mm.h:482: error: implicit declaration of function 'pte_mkwrite' include/linux/mm.h:482: error: incompatible types in assignment Signed-off-by: Michal Simek CC: Andrea Arcangeli Reviewed-by: Rik van Riel CC: Andrew Morton CC: Ingo Molnar Signed-off-by: Linus Torvalds --- include/linux/mm.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/mm.h b/include/linux/mm.h index 956a35532f47..f6385fc17ad4 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -470,6 +470,7 @@ static inline void set_compound_order(struct page *page, unsigned long order) page[1].lru.prev = (void *)order; } +#ifdef CONFIG_MMU /* * Do pte_mkwrite, but only if the vma says VM_WRITE. We do this when * servicing faults for write access. In the normal case, do always want @@ -482,6 +483,7 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma) pte = pte_mkwrite(pte); return pte; } +#endif /* * Multiple processes may "see" the same page. E.g. for untouched -- cgit v1.2.3 From b2f8fb237e9cc407a02aca401369c15babae35dd Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 21 Jan 2011 16:40:19 +0000 Subject: perf symbols: Fix annotation of thumb code In ARM's Thumb mode the bottom bit of the symbol address is set to mark the function as Thumb; the instructions are in reality 2 or 4 byte on 2 byte alignments, and when the +1 address is used in annotate it causes objdump to disassemble invalid instructions. The patch removes that bottom bit during symbol loading. Many thinks to Dave Martin for comments on an initial version of the patch. (For reference this corresponds to this bug https://bugs.launchpad.net/linux-linaro/+bug/677547 ) Cc: Ingo Molnar Cc: Dave Martin LKML-Reference: <20110121163922.GA31398@davesworkthinkpad> Signed-off-by: Dr. David Alan Gilbert Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 15ccfba8cdf8..e32478effed8 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1161,6 +1161,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, section_name = elf_sec__name(&shdr, secstrs); + /* On ARM, symbols for thumb functions have 1 added to + * the symbol address as a flag - remove it */ + if ((ehdr.e_machine == EM_ARM) && + (map->type == MAP__FUNCTION) && + (sym.st_value & 1)) + --sym.st_value; + if (self->kernel != DSO_TYPE_USER || kmodule) { char dso_name[PATH_MAX]; -- cgit v1.2.3 From ffb5e0fb44d0829021a51f972df90166bc9b700e Mon Sep 17 00:00:00 2001 From: Han Pingtian Date: Thu, 20 Jan 2011 19:47:07 +0800 Subject: perf test: Use cpu_map->[cpu] when setting affinity When some of CPUs are offline: # cat /sys/devices/system/cpu/online 0,6-31 perf test will fail on #3 testcase: 3: detect open syscall event on all cpus: --- start --- perf_evsel__read_on_cpu: expected to intercept 111 calls on cpu 0, got 681 perf_evsel__read_on_cpu: expected to intercept 112 calls on cpu 1, got 117 perf_evsel__read_on_cpu: expected to intercept 113 calls on cpu 2, got 118 perf_evsel__read_on_cpu: expected to intercept 114 calls on cpu 3, got 119 perf_evsel__read_on_cpu: expected to intercept 115 calls on cpu 4, got 120 perf_evsel__read_on_cpu: expected to intercept 116 calls on cpu 5, got 121 perf_evsel__read_on_cpu: expected to intercept 117 calls on cpu 6, got 122 perf_evsel__read_on_cpu: expected to intercept 118 calls on cpu 7, got 123 perf_evsel__read_on_cpu: expected to intercept 119 calls on cpu 8, got 124 perf_evsel__read_on_cpu: expected to intercept 120 calls on cpu 9, got 125 perf_evsel__read_on_cpu: expected to intercept 121 calls on cpu 10, got 126 .... This patch try to use 'cpus->map[cpu]' when setting cpu affinity, and will check the return code of sched_setaffinity() LKML-Reference: <20110120114707.GA11781@hpt.nay.redhat.com> Signed-off-by: Han Pingtian Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-test.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index ed5696198d3d..02958d6a807f 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -380,13 +380,18 @@ static int test__open_syscall_event_on_all_cpus(void) for (cpu = 0; cpu < cpus->nr; ++cpu) { unsigned int ncalls = nr_open_calls + cpu; - CPU_SET(cpu, cpu_set); - sched_setaffinity(0, cpu_set_size, cpu_set); + CPU_SET_S(cpus->map[cpu], cpu_set_size, cpu_set); + if (sched_setaffinity(0, cpu_set_size, cpu_set) < 0) { + pr_debug("sched_setaffinity() failed on CPU %d: %s ", + cpus->map[cpu], + strerror(errno)); + goto out_close_fd; + } for (i = 0; i < ncalls; ++i) { fd = open("/etc/passwd", O_RDONLY); close(fd); } - CPU_CLR(cpu, cpu_set); + CPU_CLR_S(cpus->map[cpu], cpu_set_size, cpu_set); } /* @@ -410,7 +415,7 @@ static int test__open_syscall_event_on_all_cpus(void) expected = nr_open_calls + cpu; if (evsel->counts->cpu[cpu].val != expected) { pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %Ld\n", - expected, cpu, evsel->counts->cpu[cpu].val); + expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); goto out_close_fd; } } -- cgit v1.2.3 From 806839b22cbda90176d7f8d421889bddd7826e93 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 21 Jan 2011 18:45:47 +0100 Subject: perf: perf_event_exit_task_context: s/rcu_dereference/rcu_dereference_raw/ In theory, almost every user of task->child->perf_event_ctxp[] is wrong. find_get_context() can install the new context at any moment, we need read_barrier_depends(). dbe08d82ce3967ccdf459f7951d02589cf967300 "perf: Fix find_get_context() vs perf_event_exit_task() race" added rcu_dereference() into perf_event_exit_task_context() to make the precedent, but this makes __rcu_dereference_check() unhappy. Use rcu_dereference_raw() to shut up the warning. Reported-by: Ingo Molnar Signed-off-by: Oleg Nesterov Cc: acme@redhat.com Cc: paulus@samba.org Cc: stern@rowland.harvard.edu Cc: a.p.zijlstra@chello.nl Cc: fweisbec@gmail.com Cc: roland@redhat.com Cc: prasad@linux.vnet.ibm.com Cc: Paul E. McKenney LKML-Reference: <20110121174547.GA8796@redhat.com> Signed-off-by: Ingo Molnar --- kernel/perf_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/perf_event.c b/kernel/perf_event.c index c5fa717cf099..126a302c481c 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -6136,7 +6136,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) * scheduled, so we are now safe from rescheduling changing * our context. */ - child_ctx = rcu_dereference(child->perf_event_ctxp[ctxn]); + child_ctx = rcu_dereference_raw(child->perf_event_ctxp[ctxn]); task_ctx_sched_out(child_ctx, EVENT_ALL); /* -- cgit v1.2.3 From a8b17ed019bd40d3bfa20439d9c36a99f9be9180 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Jan 2011 16:38:27 +0000 Subject: KEYS: Do some style cleanup in the key management code. Do a bit of a style clean up in the key management code. No functional changes. Done using: perl -p -i -e 's!^/[*]*/\n!!' security/keys/*.c perl -p -i -e 's!} /[*] end [a-z0-9_]*[(][)] [*]/\n!}\n!' security/keys/*.c sed -i -s -e ": next" -e N -e 's/^\n[}]$/}/' -e t -e P -e 's/^.*\n//' -e "b next" security/keys/*.c To remove /*****/ lines, remove comments on the closing brace of a function to name the function and remove blank lines before the closing brace of a function. Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- security/keys/compat.c | 4 +- security/keys/encrypted_defined.c | 1 - security/keys/key.c | 80 ++++++++++----------------------------- security/keys/keyctl.c | 77 ++++++++++--------------------------- security/keys/keyring.c | 64 ++++++++----------------------- security/keys/permission.c | 8 +--- security/keys/proc.c | 7 +--- security/keys/process_keys.c | 23 +++-------- security/keys/request_key_auth.c | 32 ++++------------ security/keys/user_defined.c | 32 ++++------------ 10 files changed, 80 insertions(+), 248 deletions(-) diff --git a/security/keys/compat.c b/security/keys/compat.c index 792c0a611a6d..3cedf6305179 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -14,7 +14,6 @@ #include #include "internal.h" -/*****************************************************************************/ /* * the key control system call, 32-bit compatibility version for 64-bit archs * - this should only be called if the 64-bit arch uses weird pointers in @@ -88,5 +87,4 @@ asmlinkage long compat_sys_keyctl(u32 option, default: return -EOPNOTSUPP; } - -} /* end compat_sys_keyctl() */ +} diff --git a/security/keys/encrypted_defined.c b/security/keys/encrypted_defined.c index 32d27c858388..28791a65740e 100644 --- a/security/keys/encrypted_defined.c +++ b/security/keys/encrypted_defined.c @@ -888,7 +888,6 @@ static int __init init_encrypted(void) out: encrypted_shash_release(); return ret; - } static void __exit cleanup_encrypted(void) diff --git a/security/keys/key.c b/security/keys/key.c index c1eac8084ade..3a92d7365870 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -56,7 +56,6 @@ void __key_check(const struct key *key) } #endif -/*****************************************************************************/ /* * get the key quota record for a user, allocating a new record if one doesn't * already exist @@ -130,10 +129,8 @@ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) kfree(candidate); out: return user; +} -} /* end key_user_lookup() */ - -/*****************************************************************************/ /* * dispose of a user structure */ @@ -146,10 +143,8 @@ void key_user_put(struct key_user *user) kfree(user); } +} -} /* end key_user_put() */ - -/*****************************************************************************/ /* * assign a key the next unique serial number * - these are assigned randomly to avoid security issues through covert @@ -211,10 +206,8 @@ serial_exists: if (key->serial < xkey->serial) goto attempt_insertion; } +} -} /* end key_alloc_serial() */ - -/*****************************************************************************/ /* * allocate a key of the specified type * - update the user's quota to reflect the existence of the key @@ -344,12 +337,10 @@ no_quota: key_user_put(user); key = ERR_PTR(-EDQUOT); goto error; - -} /* end key_alloc() */ +} EXPORT_SYMBOL(key_alloc); -/*****************************************************************************/ /* * reserve an amount of quota for the key's payload */ @@ -384,12 +375,10 @@ int key_payload_reserve(struct key *key, size_t datalen) key->datalen = datalen; return ret; - -} /* end key_payload_reserve() */ +} EXPORT_SYMBOL(key_payload_reserve); -/*****************************************************************************/ /* * instantiate a key and link it into the target keyring atomically * - called with the target keyring's semaphore writelocked @@ -441,10 +430,8 @@ static int __key_instantiate_and_link(struct key *key, wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT); return ret; +} -} /* end __key_instantiate_and_link() */ - -/*****************************************************************************/ /* * instantiate a key and link it into the target keyring atomically */ @@ -471,12 +458,10 @@ int key_instantiate_and_link(struct key *key, __key_link_end(keyring, key->type, prealloc); return ret; - -} /* end key_instantiate_and_link() */ +} EXPORT_SYMBOL(key_instantiate_and_link); -/*****************************************************************************/ /* * negatively instantiate a key and link it into the target keyring atomically */ @@ -535,12 +520,10 @@ int key_negate_and_link(struct key *key, wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT); return ret == 0 ? link_ret : ret; - -} /* end key_negate_and_link() */ +} EXPORT_SYMBOL(key_negate_and_link); -/*****************************************************************************/ /* * do cleaning up in process context so that we don't have to disable * interrupts all over the place @@ -601,10 +584,8 @@ static void key_cleanup(struct work_struct *work) /* there may, of course, be more than one key to destroy */ goto go_again; +} -} /* end key_cleanup() */ - -/*****************************************************************************/ /* * dispose of a reference to a key * - when all the references are gone, we schedule the cleanup task to come and @@ -618,12 +599,10 @@ void key_put(struct key *key) if (atomic_dec_and_test(&key->usage)) schedule_work(&key_cleanup_task); } - -} /* end key_put() */ +} EXPORT_SYMBOL(key_put); -/*****************************************************************************/ /* * find a key by its serial number */ @@ -664,10 +643,8 @@ struct key *key_lookup(key_serial_t id) error: spin_unlock(&key_serial_lock); return key; +} -} /* end key_lookup() */ - -/*****************************************************************************/ /* * find and lock the specified key type against removal * - we return with the sem readlocked @@ -690,20 +667,16 @@ struct key_type *key_type_lookup(const char *type) found_kernel_type: return ktype; +} -} /* end key_type_lookup() */ - -/*****************************************************************************/ /* * unlock a key type */ void key_type_put(struct key_type *ktype) { up_read(&key_types_sem); +} -} /* end key_type_put() */ - -/*****************************************************************************/ /* * attempt to update an existing key * - the key has an incremented refcount @@ -742,10 +715,8 @@ error: key_put(key); key_ref = ERR_PTR(ret); goto out; +} -} /* end __key_update() */ - -/*****************************************************************************/ /* * search the specified keyring for a key of the same description; if one is * found, update it, otherwise add a new one @@ -855,12 +826,10 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_ref = __key_update(key_ref, payload, plen); goto error; - -} /* end key_create_or_update() */ +} EXPORT_SYMBOL(key_create_or_update); -/*****************************************************************************/ /* * update a key */ @@ -891,12 +860,10 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) error: return ret; - -} /* end key_update() */ +} EXPORT_SYMBOL(key_update); -/*****************************************************************************/ /* * revoke a key */ @@ -926,12 +893,10 @@ void key_revoke(struct key *key) } up_write(&key->sem); - -} /* end key_revoke() */ +} EXPORT_SYMBOL(key_revoke); -/*****************************************************************************/ /* * register a type of key */ @@ -956,12 +921,10 @@ int register_key_type(struct key_type *ktype) out: up_write(&key_types_sem); return ret; - -} /* end register_key_type() */ +} EXPORT_SYMBOL(register_key_type); -/*****************************************************************************/ /* * unregister a type of key */ @@ -1010,12 +973,10 @@ void unregister_key_type(struct key_type *ktype) up_write(&key_types_sem); key_schedule_gc(0); - -} /* end unregister_key_type() */ +} EXPORT_SYMBOL(unregister_key_type); -/*****************************************************************************/ /* * initialise the key management stuff */ @@ -1037,5 +998,4 @@ void __init key_init(void) rb_insert_color(&root_key_user.node, &key_user_tree); - -} /* end key_init() */ +} diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 60924f6a52db..83ec98b7e98d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -46,7 +46,6 @@ static int key_get_type_from_user(char *type, return 0; } -/*****************************************************************************/ /* * extract the description of a new key from userspace and either add it as a * new key to the specified keyring or update a matching key in that keyring @@ -132,10 +131,8 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, kfree(description); error: return ret; +} -} /* end sys_add_key() */ - -/*****************************************************************************/ /* * search the process keyrings for a matching key * - nested keyrings may also be searched if they have Search permission @@ -222,10 +219,8 @@ error2: kfree(description); error: return ret; +} -} /* end sys_request_key() */ - -/*****************************************************************************/ /* * get the ID of the specified process keyring * - the keyring must have search permission to be found @@ -251,7 +246,6 @@ error: } /* end keyctl_get_keyring_ID() */ -/*****************************************************************************/ /* * join the session keyring * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) @@ -277,10 +271,8 @@ long keyctl_join_session_keyring(const char __user *_name) error: return ret; +} -} /* end keyctl_join_session_keyring() */ - -/*****************************************************************************/ /* * update a key's data payload * - the key must be writable @@ -326,10 +318,8 @@ error2: kfree(payload); error: return ret; +} -} /* end keyctl_update_key() */ - -/*****************************************************************************/ /* * revoke a key * - the key must be writable @@ -358,10 +348,8 @@ long keyctl_revoke_key(key_serial_t id) key_ref_put(key_ref); error: return ret; +} -} /* end keyctl_revoke_key() */ - -/*****************************************************************************/ /* * clear the specified process keyring * - the keyring must be writable @@ -383,10 +371,8 @@ long keyctl_keyring_clear(key_serial_t ringid) key_ref_put(keyring_ref); error: return ret; +} -} /* end keyctl_keyring_clear() */ - -/*****************************************************************************/ /* * link a key into a keyring * - the keyring must be writable @@ -417,10 +403,8 @@ error2: key_ref_put(keyring_ref); error: return ret; +} -} /* end keyctl_keyring_link() */ - -/*****************************************************************************/ /* * unlink the first attachment of a key from a keyring * - the keyring must be writable @@ -451,10 +435,8 @@ error2: key_ref_put(keyring_ref); error: return ret; +} -} /* end keyctl_keyring_unlink() */ - -/*****************************************************************************/ /* * describe a user key * - the key must have view permission @@ -531,10 +513,8 @@ error2: key_ref_put(key_ref); error: return ret; +} -} /* end keyctl_describe_key() */ - -/*****************************************************************************/ /* * search the specified keyring for a matching key * - the start keyring must be searchable @@ -626,10 +606,8 @@ error2: kfree(description); error: return ret; +} -} /* end keyctl_keyring_search() */ - -/*****************************************************************************/ /* * read a user key's payload * - the keyring must be readable or the key must be searchable from the @@ -688,10 +666,8 @@ error2: key_put(key); error: return ret; +} -} /* end keyctl_read_key() */ - -/*****************************************************************************/ /* * change the ownership of a key * - the keyring owned by the changer @@ -796,10 +772,8 @@ quota_overrun: zapowner = newowner; ret = -EDQUOT; goto error_put; +} -} /* end keyctl_chown_key() */ - -/*****************************************************************************/ /* * change the permission mask on a key * - the keyring owned by the changer @@ -838,8 +812,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) key_put(key); error: return ret; - -} /* end keyctl_setperm_key() */ +} /* * get the destination keyring for instantiation @@ -895,7 +868,6 @@ static int keyctl_change_reqkey_auth(struct key *key) return commit_creds(new); } -/*****************************************************************************/ /* * instantiate the key with the specified payload, and, if one is given, link * the key into the keyring @@ -973,10 +945,8 @@ error2: vfree(payload); error: return ret; +} -} /* end keyctl_instantiate_key() */ - -/*****************************************************************************/ /* * negatively instantiate the key with the given timeout (in seconds), and, if * one is given, link the key into the keyring @@ -1020,10 +990,8 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) error: return ret; +} -} /* end keyctl_negate_key() */ - -/*****************************************************************************/ /* * set the default keyring in which request_key() will cache keys * - return the old setting @@ -1079,10 +1047,8 @@ set: error: abort_creds(new); return ret; +} -} /* end keyctl_set_reqkey_keyring() */ - -/*****************************************************************************/ /* * set or clear the timeout for a key */ @@ -1136,10 +1102,8 @@ okay: ret = 0; error: return ret; +} -} /* end keyctl_set_timeout() */ - -/*****************************************************************************/ /* * assume the authority to instantiate the specified key */ @@ -1178,8 +1142,7 @@ long keyctl_assume_authority(key_serial_t id) ret = authkey->serial; error: return ret; - -} /* end keyctl_assume_authority() */ +} /* * get the security label of a key @@ -1348,7 +1311,6 @@ error_keyring: #endif /* !TIF_NOTIFY_RESUME */ } -/*****************************************************************************/ /* * the key control system call */ @@ -1439,5 +1401,4 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, default: return -EOPNOTSUPP; } - -} /* end sys_keyctl() */ +} diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d37f713e73ce..0a2b8e916d97 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -80,7 +80,6 @@ EXPORT_SYMBOL(key_type_keyring); */ static DECLARE_RWSEM(keyring_serialise_link_sem); -/*****************************************************************************/ /* * publish the name of a keyring so that it can be found by name (if it has * one) @@ -102,10 +101,8 @@ static void keyring_publish_name(struct key *keyring) write_unlock(&keyring_name_lock); } +} -} /* end keyring_publish_name() */ - -/*****************************************************************************/ /* * initialise a keyring * - we object if we were given any data @@ -123,10 +120,8 @@ static int keyring_instantiate(struct key *keyring, } return ret; +} -} /* end keyring_instantiate() */ - -/*****************************************************************************/ /* * match keyrings on their name */ @@ -134,10 +129,8 @@ static int keyring_match(const struct key *keyring, const void *description) { return keyring->description && strcmp(keyring->description, description) == 0; +} -} /* end keyring_match() */ - -/*****************************************************************************/ /* * dispose of the data dangling from the corpse of a keyring */ @@ -164,10 +157,8 @@ static void keyring_destroy(struct key *keyring) key_put(klist->keys[loop]); kfree(klist); } +} -} /* end keyring_destroy() */ - -/*****************************************************************************/ /* * describe the keyring */ @@ -187,10 +178,8 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) else seq_puts(m, ": empty"); rcu_read_unlock(); +} -} /* end keyring_describe() */ - -/*****************************************************************************/ /* * read a list of key IDs from the keyring's contents * - the keyring's semaphore is read-locked @@ -241,10 +230,8 @@ static long keyring_read(const struct key *keyring, error: return ret; +} -} /* end keyring_read() */ - -/*****************************************************************************/ /* * allocate a keyring and link into the destination keyring */ @@ -269,10 +256,8 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, } return keyring; +} -} /* end keyring_alloc() */ - -/*****************************************************************************/ /* * search the supplied keyring tree for a key that matches the criterion * - perform a breadth-then-depth search up to the prescribed limit @@ -444,10 +429,8 @@ error_2: rcu_read_unlock(); error: return key_ref; +} -} /* end keyring_search_aux() */ - -/*****************************************************************************/ /* * search the supplied keyring tree for a key that matches the criterion * - perform a breadth-then-depth search up to the prescribed limit @@ -465,12 +448,10 @@ key_ref_t keyring_search(key_ref_t keyring, return keyring_search_aux(keyring, current->cred, type, description, type->match); - -} /* end keyring_search() */ +} EXPORT_SYMBOL(keyring_search); -/*****************************************************************************/ /* * search the given keyring only (no recursion) * - keyring must be locked by caller @@ -514,10 +495,8 @@ found: atomic_inc(&key->usage); rcu_read_unlock(); return make_key_ref(key, possessed); +} -} /* end __keyring_search_one() */ - -/*****************************************************************************/ /* * find a keyring with the specified name * - all named keyrings are searched @@ -569,10 +548,8 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) out: read_unlock(&keyring_name_lock); return keyring; +} -} /* end find_keyring_by_name() */ - -/*****************************************************************************/ /* * see if a cycle will will be created by inserting acyclic tree B in acyclic * tree A at the topmost level (ie: as a direct child of A) @@ -657,8 +634,7 @@ too_deep: cycle_detected: ret = -EDEADLK; goto error; - -} /* end keyring_detect_cycle() */ +} /* * dispose of a keyring list after the RCU grace period, freeing the unlinked @@ -898,7 +874,6 @@ int key_link(struct key *keyring, struct key *key) EXPORT_SYMBOL(key_link); -/*****************************************************************************/ /* * unlink the first link to a key from a keyring */ @@ -968,12 +943,10 @@ nomem: ret = -ENOMEM; up_write(&keyring->sem); goto error; - -} /* end key_unlink() */ +} EXPORT_SYMBOL(key_unlink); -/*****************************************************************************/ /* * dispose of a keyring list after the RCU grace period, releasing the keys it * links to @@ -989,10 +962,8 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu) key_put(klist->keys[loop]); kfree(klist); +} -} /* end keyring_clear_rcu_disposal() */ - -/*****************************************************************************/ /* * clear the specified process keyring * - implements keyctl(KEYCTL_CLEAR) @@ -1027,12 +998,10 @@ int keyring_clear(struct key *keyring) } return ret; - -} /* end keyring_clear() */ +} EXPORT_SYMBOL(keyring_clear); -/*****************************************************************************/ /* * dispose of the links from a revoked keyring * - called with the key sem write-locked @@ -1050,8 +1019,7 @@ static void keyring_revoke(struct key *keyring) rcu_assign_pointer(keyring->payload.subscriptions, NULL); call_rcu(&klist->rcu, keyring_clear_rcu_disposal); } - -} /* end keyring_revoke() */ +} /* * Determine whether a key is dead diff --git a/security/keys/permission.c b/security/keys/permission.c index 28645502cd0d..6284b1496c29 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -13,7 +13,6 @@ #include #include "internal.h" -/*****************************************************************************/ /** * key_task_permission - Check a key can be used * @key_ref: The key to check @@ -79,12 +78,10 @@ use_these_perms: /* let LSM be the final arbiter */ return security_key_permission(key_ref, cred, perm); - -} /* end key_task_permission() */ +} EXPORT_SYMBOL(key_task_permission); -/*****************************************************************************/ /* * validate a key */ @@ -111,7 +108,6 @@ int key_validate(struct key *key) error: return ret; - -} /* end key_validate() */ +} EXPORT_SYMBOL(key_validate); diff --git a/security/keys/proc.c b/security/keys/proc.c index 70373966816e..ad5f64e1b320 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -60,7 +60,6 @@ static const struct file_operations proc_key_users_fops = { .release = seq_release, }; -/*****************************************************************************/ /* * declare the /proc files */ @@ -79,12 +78,10 @@ static int __init key_proc_init(void) panic("Cannot create /proc/key-users\n"); return 0; - -} /* end key_proc_init() */ +} __initcall(key_proc_init); -/*****************************************************************************/ /* * implement "/proc/keys" to provides a list of the keys on the system */ @@ -293,7 +290,6 @@ static struct rb_node *key_user_first(struct rb_root *r) return __key_user_next(n); } -/*****************************************************************************/ /* * implement "/proc/key-users" to provides a list of the key users */ @@ -351,5 +347,4 @@ static int proc_key_users_show(struct seq_file *m, void *v) maxbytes); return 0; - } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 504bdd2452bd..ea55cf9acf36 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -38,7 +38,6 @@ struct key_user root_key_user = { .user_ns = &init_user_ns, }; -/*****************************************************************************/ /* * install user and user session keyrings for a particular UID */ @@ -275,7 +274,6 @@ static int install_session_keyring(struct key *keyring) return commit_creds(new); } -/*****************************************************************************/ /* * the filesystem user ID changed */ @@ -288,10 +286,8 @@ void key_fsuid_changed(struct task_struct *tsk) tsk->cred->thread_keyring->uid = tsk->cred->fsuid; up_write(&tsk->cred->thread_keyring->sem); } +} -} /* end key_fsuid_changed() */ - -/*****************************************************************************/ /* * the filesystem group ID changed */ @@ -304,10 +300,8 @@ void key_fsgid_changed(struct task_struct *tsk) tsk->cred->thread_keyring->gid = tsk->cred->fsgid; up_write(&tsk->cred->thread_keyring->sem); } +} -} /* end key_fsgid_changed() */ - -/*****************************************************************************/ /* * search only my process keyrings for the first matching key * - we use the supplied match function to see if the description (or other @@ -428,7 +422,6 @@ found: return key_ref; } -/*****************************************************************************/ /* * search the process keyrings for the first matching key * - we use the supplied match function to see if the description (or other @@ -489,20 +482,16 @@ key_ref_t search_process_keyrings(struct key_type *type, found: return key_ref; +} -} /* end search_process_keyrings() */ - -/*****************************************************************************/ /* * see if the key we're looking at is the target key */ int lookup_user_key_possessed(const struct key *key, const void *target) { return key == target; +} -} /* end lookup_user_key_possessed() */ - -/*****************************************************************************/ /* * lookup a key given a key ID from userspace with a given permissions mask * - don't create special keyrings unless so requested @@ -711,10 +700,8 @@ invalid_key: reget_creds: put_cred(cred); goto try_again; +} -} /* end lookup_user_key() */ - -/*****************************************************************************/ /* * join the named keyring as the session keyring if possible, or attempt to * create a new one of that name if not diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 86747151ee5b..ba717b8163ab 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -38,7 +38,6 @@ struct key_type key_type_request_key_auth = { .read = request_key_auth_read, }; -/*****************************************************************************/ /* * instantiate a request-key authorisation key */ @@ -48,10 +47,8 @@ static int request_key_auth_instantiate(struct key *key, { key->payload.data = (struct request_key_auth *) data; return 0; +} -} /* end request_key_auth_instantiate() */ - -/*****************************************************************************/ /* * reading a request-key authorisation key retrieves the callout information */ @@ -63,10 +60,8 @@ static void request_key_auth_describe(const struct key *key, seq_puts(m, "key:"); seq_puts(m, key->description); seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); +} -} /* end request_key_auth_describe() */ - -/*****************************************************************************/ /* * read the callout_info data * - the key's semaphore is read-locked @@ -91,10 +86,8 @@ static long request_key_auth_read(const struct key *key, } return ret; +} -} /* end request_key_auth_read() */ - -/*****************************************************************************/ /* * handle revocation of an authorisation token key * - called with the key sem write-locked @@ -109,10 +102,8 @@ static void request_key_auth_revoke(struct key *key) put_cred(rka->cred); rka->cred = NULL; } +} -} /* end request_key_auth_revoke() */ - -/*****************************************************************************/ /* * destroy an instantiation authorisation token key */ @@ -131,10 +122,8 @@ static void request_key_auth_destroy(struct key *key) key_put(rka->dest_keyring); kfree(rka->callout_info); kfree(rka); +} -} /* end request_key_auth_destroy() */ - -/*****************************************************************************/ /* * create an authorisation token for /sbin/request-key or whoever to gain * access to the caller's security data @@ -228,10 +217,8 @@ error_alloc: kfree(rka); kleave("= %d", ret); return ERR_PTR(ret); +} -} /* end request_key_auth_new() */ - -/*****************************************************************************/ /* * see if an authorisation key is associated with a particular key */ @@ -242,10 +229,8 @@ static int key_get_instantiation_authkey_match(const struct key *key, key_serial_t id = (key_serial_t)(unsigned long) _id; return rka->target_key->serial == id; +} -} /* end key_get_instantiation_authkey_match() */ - -/*****************************************************************************/ /* * get the authorisation key for instantiation of a specific key if attached to * the current process's keyrings @@ -278,5 +263,4 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) error: return authkey; - -} /* end key_get_instantiation_authkey() */ +} diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index e9aa07929656..02807fb16340 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -35,7 +35,6 @@ struct key_type key_type_user = { EXPORT_SYMBOL_GPL(key_type_user); -/*****************************************************************************/ /* * instantiate a user defined key */ @@ -65,12 +64,10 @@ int user_instantiate(struct key *key, const void *data, size_t datalen) error: return ret; - -} /* end user_instantiate() */ +} EXPORT_SYMBOL_GPL(user_instantiate); -/*****************************************************************************/ /* * dispose of the old data from an updated user defined key */ @@ -81,10 +78,8 @@ static void user_update_rcu_disposal(struct rcu_head *rcu) upayload = container_of(rcu, struct user_key_payload, rcu); kfree(upayload); +} -} /* end user_update_rcu_disposal() */ - -/*****************************************************************************/ /* * update a user defined key * - the key's semaphore is write-locked @@ -123,24 +118,20 @@ int user_update(struct key *key, const void *data, size_t datalen) error: return ret; - -} /* end user_update() */ +} EXPORT_SYMBOL_GPL(user_update); -/*****************************************************************************/ /* * match users on their name */ int user_match(const struct key *key, const void *description) { return strcmp(key->description, description) == 0; - -} /* end user_match() */ +} EXPORT_SYMBOL_GPL(user_match); -/*****************************************************************************/ /* * dispose of the links from a revoked keyring * - called with the key sem write-locked @@ -156,12 +147,10 @@ void user_revoke(struct key *key) rcu_assign_pointer(key->payload.data, NULL); call_rcu(&upayload->rcu, user_update_rcu_disposal); } - -} /* end user_revoke() */ +} EXPORT_SYMBOL(user_revoke); -/*****************************************************************************/ /* * dispose of the data dangling from the corpse of a user key */ @@ -170,12 +159,10 @@ void user_destroy(struct key *key) struct user_key_payload *upayload = key->payload.data; kfree(upayload); - -} /* end user_destroy() */ +} EXPORT_SYMBOL_GPL(user_destroy); -/*****************************************************************************/ /* * describe the user key */ @@ -184,12 +171,10 @@ void user_describe(const struct key *key, struct seq_file *m) seq_puts(m, key->description); seq_printf(m, ": %u", key->datalen); - -} /* end user_describe() */ +} EXPORT_SYMBOL_GPL(user_describe); -/*****************************************************************************/ /* * read the key data * - the key's semaphore is read-locked @@ -213,7 +198,6 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen) } return ret; - -} /* end user_read() */ +} EXPORT_SYMBOL_GPL(user_read); -- cgit v1.2.3 From 973c9f4f49ca96a53bcf6384c4c59ccd26c33906 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 20 Jan 2011 16:38:33 +0000 Subject: KEYS: Fix up comments in key management code Fix up comments in the key management code. No functional changes. Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- security/keys/compat.c | 13 +- security/keys/gc.c | 14 +- security/keys/internal.h | 26 ++-- security/keys/key.c | 240 +++++++++++++++++++++++---------- security/keys/keyctl.c | 278 ++++++++++++++++++++++++++------------- security/keys/keyring.c | 231 ++++++++++++++++++++++---------- security/keys/permission.c | 25 ++-- security/keys/proc.c | 10 +- security/keys/process_keys.c | 112 ++++++++++------ security/keys/request_key.c | 164 +++++++++++++++++------ security/keys/request_key_auth.c | 30 ++--- 11 files changed, 777 insertions(+), 366 deletions(-) diff --git a/security/keys/compat.c b/security/keys/compat.c index 3cedf6305179..07a5f35e3970 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -1,4 +1,4 @@ -/* compat.c: 32-bit compatibility syscall for 64-bit systems +/* 32-bit compatibility syscall for 64-bit systems * * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -15,11 +15,12 @@ #include "internal.h" /* - * the key control system call, 32-bit compatibility version for 64-bit archs - * - this should only be called if the 64-bit arch uses weird pointers in - * 32-bit mode or doesn't guarantee that the top 32-bits of the argument - * registers on taking a 32-bit syscall are zero - * - if you can, you should call sys_keyctl directly + * The key control system call, 32-bit compatibility version for 64-bit archs + * + * This should only be called if the 64-bit arch uses weird pointers in 32-bit + * mode or doesn't guarantee that the top 32-bits of the argument registers on + * taking a 32-bit syscall are zero. If you can, you should call sys_keyctl() + * directly. */ asmlinkage long compat_sys_keyctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) diff --git a/security/keys/gc.c b/security/keys/gc.c index a46e825cbf02..89df6b5f203c 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -32,8 +32,8 @@ static time_t key_gc_next_run = LONG_MAX; static time_t key_gc_new_timer; /* - * Schedule a garbage collection run - * - precision isn't particularly important + * Schedule a garbage collection run. + * - time precision isn't particularly important */ void key_schedule_gc(time_t gc_at) { @@ -61,8 +61,9 @@ static void key_gc_timer_func(unsigned long data) } /* - * Garbage collect pointers from a keyring - * - return true if we altered the keyring + * Garbage collect pointers from a keyring. + * + * Return true if we altered the keyring. */ static bool key_gc_keyring(struct key *keyring, time_t limit) __releases(key_serial_lock) @@ -107,9 +108,8 @@ do_gc: } /* - * Garbage collector for keys - * - this involves scanning the keyrings for dead, expired and revoked keys - * that have overstayed their welcome + * Garbage collector for keys. This involves scanning the keyrings for dead, + * expired and revoked keys that have overstayed their welcome */ static void key_garbage_collector(struct work_struct *work) { diff --git a/security/keys/internal.h b/security/keys/internal.h index 56a133d8f37d..edfa50dbd6f5 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -1,4 +1,4 @@ -/* internal.h: authentication token and access key management internal defs +/* Authentication token and access key management internal defs * * Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -35,10 +35,12 @@ extern struct key_type key_type_user; /*****************************************************************************/ /* - * keep track of keys for a user - * - this needs to be separate to user_struct to avoid a refcount-loop - * (user_struct pins some keyrings which pin this struct) - * - this also keeps track of keys under request from userspace for this UID + * Keep track of keys for a user. + * + * This needs to be separate to user_struct to avoid a refcount-loop + * (user_struct pins some keyrings which pin this struct). + * + * We also keep track of keys under request from userspace for this UID here. */ struct key_user { struct rb_node node; @@ -62,7 +64,7 @@ extern struct key_user *key_user_lookup(uid_t uid, extern void key_user_put(struct key_user *user); /* - * key quota limits + * Key quota limits. * - root has its own separate limits to everyone else */ extern unsigned key_quota_root_maxkeys; @@ -146,13 +148,13 @@ extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); extern void key_schedule_gc(time_t expiry_at); -/* - * check to see whether permission is granted to use a key in the desired way - */ extern int key_task_permission(const key_ref_t key_ref, const struct cred *cred, key_perm_t perm); +/* + * Check to see whether permission is granted to use a key in the desired way. + */ static inline int key_permission(const key_ref_t key_ref, key_perm_t perm) { return key_task_permission(key_ref, current_cred(), perm); @@ -168,7 +170,7 @@ static inline int key_permission(const key_ref_t key_ref, key_perm_t perm) #define KEY_ALL 0x3f /* all the above permissions */ /* - * request_key authorisation + * Authorisation record for request_key(). */ struct request_key_auth { struct key *target_key; @@ -188,7 +190,7 @@ extern struct key *request_key_auth_new(struct key *target, extern struct key *key_get_instantiation_authkey(key_serial_t target_id); /* - * keyctl functions + * keyctl() functions */ extern long keyctl_get_keyring_ID(key_serial_t, int); extern long keyctl_join_session_keyring(const char __user *); @@ -214,7 +216,7 @@ extern long keyctl_get_security(key_serial_t keyid, char __user *buffer, extern long keyctl_session_to_parent(void); /* - * debugging key validation + * Debugging key validation */ #ifdef KEY_DEBUGGING extern void __key_check(const struct key *); diff --git a/security/keys/key.c b/security/keys/key.c index 3a92d7365870..84d4eb568b08 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -39,10 +39,10 @@ static DECLARE_RWSEM(key_types_sem); static void key_cleanup(struct work_struct *work); static DECLARE_WORK(key_cleanup_task, key_cleanup); -/* we serialise key instantiation and link */ +/* We serialise key instantiation and link */ DEFINE_MUTEX(key_construction_mutex); -/* any key who's type gets unegistered will be re-typed to this */ +/* Any key who's type gets unegistered will be re-typed to this */ static struct key_type key_type_dead = { .name = "dead", }; @@ -57,8 +57,8 @@ void __key_check(const struct key *key) #endif /* - * get the key quota record for a user, allocating a new record if one doesn't - * already exist + * Get the key quota record for a user, allocating a new record if one doesn't + * already exist. */ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) { @@ -66,7 +66,7 @@ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) struct rb_node *parent = NULL; struct rb_node **p; - try_again: +try_again: p = &key_user_tree.rb_node; spin_lock(&key_user_lock); @@ -123,16 +123,16 @@ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) goto out; /* okay - we found a user record for this UID */ - found: +found: atomic_inc(&user->usage); spin_unlock(&key_user_lock); kfree(candidate); - out: +out: return user; } /* - * dispose of a user structure + * Dispose of a user structure */ void key_user_put(struct key_user *user) { @@ -146,9 +146,8 @@ void key_user_put(struct key_user *user) } /* - * assign a key the next unique serial number - * - these are assigned randomly to avoid security issues through covert - * channel problems + * Allocate a serial number for a key. These are assigned randomly to avoid + * security issues through covert channel problems. */ static inline void key_alloc_serial(struct key *key) { @@ -208,14 +207,34 @@ serial_exists: } } -/* - * allocate a key of the specified type - * - update the user's quota to reflect the existence of the key - * - called from a key-type operation with key_types_sem read-locked by - * key_create_or_update() - * - this prevents unregistration of the key type - * - upon return the key is as yet uninstantiated; the caller needs to either - * instantiate the key or discard it before returning +/** + * key_alloc - Allocate a key of the specified type. + * @type: The type of key to allocate. + * @desc: The key description to allow the key to be searched out. + * @uid: The owner of the new key. + * @gid: The group ID for the new key's group permissions. + * @cred: The credentials specifying UID namespace. + * @perm: The permissions mask of the new key. + * @flags: Flags specifying quota properties. + * + * Allocate a key of the specified type with the attributes given. The key is + * returned in an uninstantiated state and the caller needs to instantiate the + * key before returning. + * + * The user's key count quota is updated to reflect the creation of the key and + * the user's key data quota has the default for the key type reserved. The + * instantiation function should amend this as necessary. If insufficient + * quota is available, -EDQUOT will be returned. + * + * The LSM security modules can prevent a key being created, in which case + * -EACCES will be returned. + * + * Returns a pointer to the new key if successful and an error code otherwise. + * + * Note that the caller needs to ensure the key type isn't uninstantiated. + * Internally this can be done by locking key_types_sem. Externally, this can + * be done by either never unregistering the key type, or making sure + * key_alloc() calls don't race with module unloading. */ struct key *key_alloc(struct key_type *type, const char *desc, uid_t uid, gid_t gid, const struct cred *cred, @@ -338,11 +357,18 @@ no_quota: key = ERR_PTR(-EDQUOT); goto error; } - EXPORT_SYMBOL(key_alloc); -/* - * reserve an amount of quota for the key's payload +/** + * key_payload_reserve - Adjust data quota reservation for the key's payload + * @key: The key to make the reservation for. + * @datalen: The amount of data payload the caller now wants. + * + * Adjust the amount of the owning user's key data quota that a key reserves. + * If the amount is increased, then -EDQUOT may be returned if there isn't + * enough free quota available. + * + * If successful, 0 is returned. */ int key_payload_reserve(struct key *key, size_t datalen) { @@ -376,12 +402,13 @@ int key_payload_reserve(struct key *key, size_t datalen) return ret; } - EXPORT_SYMBOL(key_payload_reserve); /* - * instantiate a key and link it into the target keyring atomically - * - called with the target keyring's semaphore writelocked + * Instantiate a key and link it into the target keyring atomically. Must be + * called with the target keyring's semaphore writelocked. The target key's + * semaphore need not be locked as instantiation is serialised by + * key_construction_mutex. */ static int __key_instantiate_and_link(struct key *key, const void *data, @@ -432,8 +459,21 @@ static int __key_instantiate_and_link(struct key *key, return ret; } -/* - * instantiate a key and link it into the target keyring atomically +/** + * key_instantiate_and_link - Instantiate a key and link it into the keyring. + * @key: The key to instantiate. + * @data: The data to use to instantiate the keyring. + * @datalen: The length of @data. + * @keyring: Keyring to create a link in on success (or NULL). + * @authkey: The authorisation token permitting instantiation. + * + * Instantiate a key that's in the uninstantiated state using the provided data + * and, if successful, link it in to the destination keyring if one is + * supplied. + * + * If successful, 0 is returned, the authorisation token is revoked and anyone + * waiting for the key is woken up. If the key was already instantiated, + * -EBUSY will be returned. */ int key_instantiate_and_link(struct key *key, const void *data, @@ -462,8 +502,24 @@ int key_instantiate_and_link(struct key *key, EXPORT_SYMBOL(key_instantiate_and_link); -/* - * negatively instantiate a key and link it into the target keyring atomically +/** + * key_negate_and_link - Negatively instantiate a key and link it into the keyring. + * @key: The key to instantiate. + * @timeout: The timeout on the negative key. + * @keyring: Keyring to create a link in on success (or NULL). + * @authkey: The authorisation token permitting instantiation. + * + * Negatively instantiate a key that's in the uninstantiated state and, if + * successful, set its timeout and link it in to the destination keyring if one + * is supplied. The key and any links to the key will be automatically garbage + * collected after the timeout expires. + * + * Negative keys are used to rate limit repeated request_key() calls by causing + * them to return -ENOKEY until the negative key expires. + * + * If successful, 0 is returned, the authorisation token is revoked and anyone + * waiting for the key is woken up. If the key was already instantiated, + * -EBUSY will be returned. */ int key_negate_and_link(struct key *key, unsigned timeout, @@ -525,15 +581,18 @@ int key_negate_and_link(struct key *key, EXPORT_SYMBOL(key_negate_and_link); /* - * do cleaning up in process context so that we don't have to disable - * interrupts all over the place + * Garbage collect keys in process context so that we don't have to disable + * interrupts all over the place. + * + * key_put() schedules this rather than trying to do the cleanup itself, which + * means key_put() doesn't have to sleep. */ static void key_cleanup(struct work_struct *work) { struct rb_node *_n; struct key *key; - go_again: +go_again: /* look for a dead key in the tree */ spin_lock(&key_serial_lock); @@ -547,7 +606,7 @@ static void key_cleanup(struct work_struct *work) spin_unlock(&key_serial_lock); return; - found_dead_key: +found_dead_key: /* we found a dead key - once we've removed it from the tree, we can * drop the lock */ rb_erase(&key->serial_node, &key_serial_tree); @@ -586,10 +645,13 @@ static void key_cleanup(struct work_struct *work) goto go_again; } -/* - * dispose of a reference to a key - * - when all the references are gone, we schedule the cleanup task to come and - * pull it out of the tree in definite process context +/** + * key_put - Discard a reference to a key. + * @key: The key to discard a reference from. + * + * Discard a reference to a key, and when all the references are gone, we + * schedule the cleanup task to come and pull it out of the tree in process + * context at some later time. */ void key_put(struct key *key) { @@ -600,11 +662,10 @@ void key_put(struct key *key) schedule_work(&key_cleanup_task); } } - EXPORT_SYMBOL(key_put); /* - * find a key by its serial number + * Find a key by its serial number. */ struct key *key_lookup(key_serial_t id) { @@ -626,11 +687,11 @@ struct key *key_lookup(key_serial_t id) goto found; } - not_found: +not_found: key = ERR_PTR(-ENOKEY); goto error; - found: +found: /* pretend it doesn't exist if it is awaiting deletion */ if (atomic_read(&key->usage) == 0) goto not_found; @@ -640,14 +701,16 @@ struct key *key_lookup(key_serial_t id) */ atomic_inc(&key->usage); - error: +error: spin_unlock(&key_serial_lock); return key; } /* - * find and lock the specified key type against removal - * - we return with the sem readlocked + * Find and lock the specified key type against removal. + * + * We return with the sem read-locked if successful. If the type wasn't + * available -ENOKEY is returned instead. */ struct key_type *key_type_lookup(const char *type) { @@ -665,12 +728,12 @@ struct key_type *key_type_lookup(const char *type) up_read(&key_types_sem); ktype = ERR_PTR(-ENOKEY); - found_kernel_type: +found_kernel_type: return ktype; } /* - * unlock a key type + * Unlock a key type locked by key_type_lookup(). */ void key_type_put(struct key_type *ktype) { @@ -678,9 +741,10 @@ void key_type_put(struct key_type *ktype) } /* - * attempt to update an existing key - * - the key has an incremented refcount - * - we need to put the key if we get an error + * Attempt to update an existing key. + * + * The key is given to us with an incremented refcount that we need to discard + * if we get an error. */ static inline key_ref_t __key_update(key_ref_t key_ref, const void *payload, size_t plen) @@ -717,9 +781,30 @@ error: goto out; } -/* - * search the specified keyring for a key of the same description; if one is - * found, update it, otherwise add a new one +/** + * key_create_or_update - Update or create and instantiate a key. + * @keyring_ref: A pointer to the destination keyring with possession flag. + * @type: The type of key. + * @description: The searchable description for the key. + * @payload: The data to use to instantiate or update the key. + * @plen: The length of @payload. + * @perm: The permissions mask for a new key. + * @flags: The quota flags for a new key. + * + * Search the destination keyring for a key of the same description and if one + * is found, update it, otherwise create and instantiate a new one and create a + * link to it from that keyring. + * + * If perm is KEY_PERM_UNDEF then an appropriate key permissions mask will be + * concocted. + * + * Returns a pointer to the new key if successful, -ENODEV if the key type + * wasn't available, -ENOTDIR if the keyring wasn't a keyring, -EACCES if the + * caller isn't permitted to modify the keyring or the LSM did not permit + * creation of the key. + * + * On success, the possession flag from the keyring ref will be tacked on to + * the key ref before it is returned. */ key_ref_t key_create_or_update(key_ref_t keyring_ref, const char *type, @@ -827,11 +912,20 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key_ref = __key_update(key_ref, payload, plen); goto error; } - EXPORT_SYMBOL(key_create_or_update); -/* - * update a key +/** + * key_update - Update a key's contents. + * @key_ref: The pointer (plus possession flag) to the key. + * @payload: The data to be used to update the key. + * @plen: The length of @payload. + * + * Attempt to update the contents of a key with the given payload data. The + * caller must be granted Write permission on the key. Negative keys can be + * instantiated by this method. + * + * Returns 0 on success, -EACCES if not permitted and -EOPNOTSUPP if the key + * type does not support updating. The key type may return other errors. */ int key_update(key_ref_t key_ref, const void *payload, size_t plen) { @@ -861,11 +955,16 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) error: return ret; } - EXPORT_SYMBOL(key_update); -/* - * revoke a key +/** + * key_revoke - Revoke a key. + * @key: The key to be revoked. + * + * Mark a key as being revoked and ask the type to free up its resources. The + * revocation timeout is set and the key and all its links will be + * automatically garbage collected after key_gc_delay amount of time if they + * are not manually dealt with first. */ void key_revoke(struct key *key) { @@ -894,11 +993,15 @@ void key_revoke(struct key *key) up_write(&key->sem); } - EXPORT_SYMBOL(key_revoke); -/* - * register a type of key +/** + * register_key_type - Register a type of key. + * @ktype: The new key type. + * + * Register a new key type. + * + * Returns 0 on success or -EEXIST if a type of this name already exists. */ int register_key_type(struct key_type *ktype) { @@ -918,15 +1021,19 @@ int register_key_type(struct key_type *ktype) list_add(&ktype->link, &key_types_list); ret = 0; - out: +out: up_write(&key_types_sem); return ret; } - EXPORT_SYMBOL(register_key_type); -/* - * unregister a type of key +/** + * unregister_key_type - Unregister a type of key. + * @ktype: The key type. + * + * Unregister a key type and mark all the extant keys of this type as dead. + * Those keys of this type are then destroyed to get rid of their payloads and + * they and their links will be garbage collected as soon as possible. */ void unregister_key_type(struct key_type *ktype) { @@ -974,11 +1081,10 @@ void unregister_key_type(struct key_type *ktype) key_schedule_gc(0); } - EXPORT_SYMBOL(unregister_key_type); /* - * initialise the key management stuff + * Initialise the key management state. */ void __init key_init(void) { diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 83ec98b7e98d..31a0fd8189f1 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1,4 +1,4 @@ -/* keyctl.c: userspace keyctl operations +/* Userspace key control operations * * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -31,27 +31,24 @@ static int key_get_type_from_user(char *type, int ret; ret = strncpy_from_user(type, _type, len); - if (ret < 0) return ret; - if (ret == 0 || ret >= len) return -EINVAL; - if (type[0] == '.') return -EPERM; - type[len - 1] = '\0'; - return 0; } /* - * extract the description of a new key from userspace and either add it as a - * new key to the specified keyring or update a matching key in that keyring - * - the keyring must be writable - * - returns the new key's serial number - * - implements add_key() + * Extract the description of a new key from userspace and either add it as a + * new key to the specified keyring or update a matching key in that keyring. + * + * The keyring must be writable so that we can attach the key to it. + * + * If successful, the new key's serial number is returned, otherwise an error + * code is returned. */ SYSCALL_DEFINE5(add_key, const char __user *, _type, const char __user *, _description, @@ -134,14 +131,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, } /* - * search the process keyrings for a matching key - * - nested keyrings may also be searched if they have Search permission - * - if a key is found, it will be attached to the destination keyring if - * there's one specified - * - /sbin/request-key will be invoked if _callout_info is non-NULL - * - the _callout_info string will be passed to /sbin/request-key - * - if the _callout_info string is empty, it will be rendered as "-" - * - implements request_key() + * Search the process keyrings and keyring trees linked from those for a + * matching key. Keyrings must have appropriate Search permission to be + * searched. + * + * If a key is found, it will be attached to the destination keyring if there's + * one specified and the serial number of the key will be returned. + * + * If no key is found, /sbin/request-key will be invoked if _callout_info is + * non-NULL in an attempt to create a key. The _callout_info string will be + * passed to /sbin/request-key to aid with completing the request. If the + * _callout_info string is "" then it will be changed to "-". */ SYSCALL_DEFINE4(request_key, const char __user *, _type, const char __user *, _description, @@ -222,9 +222,11 @@ error: } /* - * get the ID of the specified process keyring - * - the keyring must have search permission to be found - * - implements keyctl(KEYCTL_GET_KEYRING_ID) + * Get the ID of the specified process keyring. + * + * The requested keyring must have search permission to be found. + * + * If successful, the ID of the requested keyring will be returned. */ long keyctl_get_keyring_ID(key_serial_t id, int create) { @@ -243,12 +245,17 @@ long keyctl_get_keyring_ID(key_serial_t id, int create) key_ref_put(key_ref); error: return ret; - -} /* end keyctl_get_keyring_ID() */ +} /* - * join the session keyring - * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) + * Join a (named) session keyring. + * + * Create and join an anonymous session keyring or join a named session + * keyring, creating it if necessary. A named session keyring must have Search + * permission for it to be joined. Session keyrings without this permit will + * be skipped over. + * + * If successful, the ID of the joined session keyring will be returned. */ long keyctl_join_session_keyring(const char __user *_name) { @@ -274,9 +281,14 @@ error: } /* - * update a key's data payload - * - the key must be writable - * - implements keyctl(KEYCTL_UPDATE) + * Update a key's data payload from the given data. + * + * The key must grant the caller Write permission and the key type must support + * updating for this to work. A negative key can be positively instantiated + * with this call. + * + * If successful, 0 will be returned. If the key type does not support + * updating, then -EOPNOTSUPP will be returned. */ long keyctl_update_key(key_serial_t id, const void __user *_payload, @@ -321,9 +333,14 @@ error: } /* - * revoke a key - * - the key must be writable - * - implements keyctl(KEYCTL_REVOKE) + * Revoke a key. + * + * The key must be grant the caller Write or Setattr permission for this to + * work. The key type should give up its quota claim when revoked. The key + * and any links to the key will be automatically garbage collected after a + * certain amount of time (/proc/sys/kernel/keys/gc_delay). + * + * If successful, 0 is returned. */ long keyctl_revoke_key(key_serial_t id) { @@ -351,9 +368,11 @@ error: } /* - * clear the specified process keyring - * - the keyring must be writable - * - implements keyctl(KEYCTL_CLEAR) + * Clear the specified keyring, creating an empty process keyring if one of the + * special keyring IDs is used. + * + * The keyring must grant the caller Write permission for this to work. If + * successful, 0 will be returned. */ long keyctl_keyring_clear(key_serial_t ringid) { @@ -374,10 +393,15 @@ error: } /* - * link a key into a keyring - * - the keyring must be writable - * - the key must be linkable - * - implements keyctl(KEYCTL_LINK) + * Create a link from a keyring to a key if there's no matching key in the + * keyring, otherwise replace the link to the matching key with a link to the + * new key. + * + * The key must grant the caller Link permission and the the keyring must grant + * the caller Write permission. Furthermore, if an additional link is created, + * the keyring's quota will be extended. + * + * If successful, 0 will be returned. */ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) { @@ -406,10 +430,13 @@ error: } /* - * unlink the first attachment of a key from a keyring - * - the keyring must be writable - * - we don't need any permissions on the key - * - implements keyctl(KEYCTL_UNLINK) + * Unlink a key from a keyring. + * + * The keyring must grant the caller Write permission for this to work; the key + * itself need not grant the caller anything. If the last link to a key is + * removed then that key will be scheduled for destruction. + * + * If successful, 0 will be returned. */ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) { @@ -438,14 +465,17 @@ error: } /* - * describe a user key - * - the key must have view permission - * - if there's a buffer, we place up to buflen bytes of data into it - * - unless there's an error, we return the amount of description available, - * irrespective of how much we may have copied - * - the description is formatted thus: + * Return a description of a key to userspace. + * + * The key must grant the caller View permission for this to work. + * + * If there's a buffer, we place up to buflen bytes of data into it formatted + * in the following way: + * * type;uid;gid;perm;description - * - implements keyctl(KEYCTL_DESCRIBE) + * + * If successful, we return the amount of description available, irrespective + * of how much we may have copied into the buffer. */ long keyctl_describe_key(key_serial_t keyid, char __user *buffer, @@ -516,13 +546,14 @@ error: } /* - * search the specified keyring for a matching key - * - the start keyring must be searchable - * - nested keyrings may also be searched if they are searchable - * - only keys with search permission may be found - * - if a key is found, it will be attached to the destination keyring if - * there's one specified - * - implements keyctl(KEYCTL_SEARCH) + * Search the specified keyring and any keyrings it links to for a matching + * key. Only keyrings that grant the caller Search permission will be searched + * (this includes the starting keyring). Only keys with Search permission can + * be found. + * + * If successful, the found key will be linked to the destination keyring if + * supplied and the key has Link permission, and the found key ID will be + * returned. */ long keyctl_keyring_search(key_serial_t ringid, const char __user *_type, @@ -609,13 +640,14 @@ error: } /* - * read a user key's payload - * - the keyring must be readable or the key must be searchable from the - * process's keyrings - * - if there's a buffer, we place up to buflen bytes of data into it - * - unless there's an error, we return the amount of data in the key, - * irrespective of how much we may have copied - * - implements keyctl(KEYCTL_READ) + * Read a key's payload. + * + * The key must either grant the caller Read permission, or it must grant the + * caller Search permission when searched for from the process keyrings. + * + * If successful, we place up to buflen bytes of data into the buffer, if one + * is provided, and return the amount of data that is available in the key, + * irrespective of how much we copied into the buffer. */ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) { @@ -669,10 +701,19 @@ error: } /* - * change the ownership of a key - * - the keyring owned by the changer - * - if the uid or gid is -1, then that parameter is not changed - * - implements keyctl(KEYCTL_CHOWN) + * Change the ownership of a key + * + * The key must grant the caller Setattr permission for this to work, though + * the key need not be fully instantiated yet. For the UID to be changed, or + * for the GID to be changed to a group the caller is not a member of, the + * caller must have sysadmin capability. If either uid or gid is -1 then that + * attribute is not changed. + * + * If the UID is to be changed, the new user must have sufficient quota to + * accept the key. The quota deduction will be removed from the old user to + * the new user should the attribute be changed. + * + * If successful, 0 will be returned. */ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) { @@ -775,9 +816,11 @@ quota_overrun: } /* - * change the permission mask on a key - * - the keyring owned by the changer - * - implements keyctl(KEYCTL_SETPERM) + * Change the permission mask on a key. + * + * The key must grant the caller Setattr permission for this to work, though + * the key need not be fully instantiated yet. If the caller does not have + * sysadmin capability, it may only change the permission on keys that it owns. */ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) { @@ -815,7 +858,8 @@ error: } /* - * get the destination keyring for instantiation + * Get the destination keyring for instantiation and check that the caller has + * Write permission on it. */ static long get_instantiation_keyring(key_serial_t ringid, struct request_key_auth *rka, @@ -852,7 +896,7 @@ static long get_instantiation_keyring(key_serial_t ringid, } /* - * change the request_key authorisation key on the current process + * Change the request_key authorisation key on the current process. */ static int keyctl_change_reqkey_auth(struct key *key) { @@ -869,8 +913,13 @@ static int keyctl_change_reqkey_auth(struct key *key) } /* - * instantiate the key with the specified payload, and, if one is given, link - * the key into the keyring + * Instantiate a key with the specified payload and link the key into the + * destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority). No other permissions are required. + * + * If successful, 0 will be returned. */ long keyctl_instantiate_key(key_serial_t id, const void __user *_payload, @@ -948,8 +997,19 @@ error: } /* - * negatively instantiate the key with the given timeout (in seconds), and, if - * one is given, link the key into the keyring + * Negatively instantiate the key with the given timeout (in seconds) and link + * the key into the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority). No other permissions are required. + * + * The key and any links to the key will be automatically garbage collected + * after the timeout expires. + * + * Negative keys are used to rate limit repeated request_key() calls by causing + * them to return -ENOKEY until the negative key expires. + * + * If successful, 0 will be returned. */ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) { @@ -993,8 +1053,11 @@ error: } /* - * set the default keyring in which request_key() will cache keys - * - return the old setting + * Read or set the default keyring in which request_key() will cache keys and + * return the old setting. + * + * If a process keyring is specified then this will be created if it doesn't + * yet exist. The old setting will be returned if successful. */ long keyctl_set_reqkey_keyring(int reqkey_defl) { @@ -1050,7 +1113,16 @@ error: } /* - * set or clear the timeout for a key + * Set or clear the timeout on a key. + * + * Either the key must grant the caller Setattr permission or else the caller + * must hold an instantiation authorisation token for the key. + * + * The timeout is either 0 to clear the timeout, or a number of seconds from + * the current time. The key and any links to the key will be automatically + * garbage collected after the timeout expires. + * + * If successful, 0 is returned. */ long keyctl_set_timeout(key_serial_t id, unsigned timeout) { @@ -1105,7 +1177,21 @@ error: } /* - * assume the authority to instantiate the specified key + * Assume (or clear) the authority to instantiate the specified key. + * + * This sets the authoritative token currently in force for key instantiation. + * This must be done for a key to be instantiated. It has the effect of making + * available all the keys from the caller of the request_key() that created a + * key to request_key() calls made by the caller of this function. + * + * The caller must have the instantiation key in their process keyrings with a + * Search permission grant available to the caller. + * + * If the ID given is 0, then the setting will be cleared and 0 returned. + * + * If the ID given has a matching an authorisation key, then that key will be + * set and its ID will be returned. The authorisation key can be read to get + * the callout information passed to request_key(). */ long keyctl_assume_authority(key_serial_t id) { @@ -1145,12 +1231,14 @@ error: } /* - * get the security label of a key - * - the key must grant us view permission - * - if there's a buffer, we place up to buflen bytes of data into it - * - unless there's an error, we return the amount of information available, - * irrespective of how much we may have copied (including the terminal NUL) - * - implements keyctl(KEYCTL_GET_SECURITY) + * Get a key's the LSM security label. + * + * The key must grant the caller View permission for this to work. + * + * If there's a buffer, then up to buflen bytes of data will be placed into it. + * + * If successful, the amount of information available will be returned, + * irrespective of how much was copied (including the terminal NUL). */ long keyctl_get_security(key_serial_t keyid, char __user *buffer, @@ -1205,10 +1293,16 @@ long keyctl_get_security(key_serial_t keyid, } /* - * attempt to install the calling process's session keyring on the process's - * parent process - * - the keyring must exist and must grant us LINK permission - * - implements keyctl(KEYCTL_SESSION_TO_PARENT) + * Attempt to install the calling process's session keyring on the process's + * parent process. + * + * The keyring must exist and must grant the caller LINK permission, and the + * parent process must be single-threaded and must have the same effective + * ownership as this process and mustn't be SUID/SGID. + * + * The keyring will be emplaced on the parent when it next resumes userspace. + * + * If successful, 0 will be returned. */ long keyctl_session_to_parent(void) { @@ -1312,7 +1406,7 @@ error_keyring: } /* - * the key control system call + * The key control system call */ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 0a2b8e916d97..92024ed12e0a 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -26,13 +26,13 @@ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) /* - * when plumbing the depths of the key tree, this sets a hard limit set on how - * deep we're willing to go + * When plumbing the depths of the key tree, this sets a hard limit + * set on how deep we're willing to go. */ #define KEYRING_SEARCH_MAX_DEPTH 6 /* - * we keep all named keyrings in a hash to speed looking them up + * We keep all named keyrings in a hash to speed looking them up. */ #define KEYRING_NAME_HASH_SIZE (1 << 5) @@ -50,7 +50,9 @@ static inline unsigned keyring_hash(const char *desc) } /* - * the keyring type definition + * The keyring key type definition. Keyrings are simply keys of this type and + * can be treated as ordinary keys in addition to having their own special + * operations. */ static int keyring_instantiate(struct key *keyring, const void *data, size_t datalen); @@ -71,18 +73,17 @@ struct key_type key_type_keyring = { .describe = keyring_describe, .read = keyring_read, }; - EXPORT_SYMBOL(key_type_keyring); /* - * semaphore to serialise link/link calls to prevent two link calls in parallel - * introducing a cycle + * Semaphore to serialise link/link calls to prevent two link calls in parallel + * introducing a cycle. */ static DECLARE_RWSEM(keyring_serialise_link_sem); /* - * publish the name of a keyring so that it can be found by name (if it has - * one) + * Publish the name of a keyring so that it can be found by name (if it has + * one). */ static void keyring_publish_name(struct key *keyring) { @@ -104,8 +105,9 @@ static void keyring_publish_name(struct key *keyring) } /* - * initialise a keyring - * - we object if we were given any data + * Initialise a keyring. + * + * Returns 0 on success, -EINVAL if given any data. */ static int keyring_instantiate(struct key *keyring, const void *data, size_t datalen) @@ -123,7 +125,7 @@ static int keyring_instantiate(struct key *keyring, } /* - * match keyrings on their name + * Match keyrings on their name */ static int keyring_match(const struct key *keyring, const void *description) { @@ -132,7 +134,8 @@ static int keyring_match(const struct key *keyring, const void *description) } /* - * dispose of the data dangling from the corpse of a keyring + * Clean up a keyring when it is destroyed. Unpublish its name if it had one + * and dispose of its data. */ static void keyring_destroy(struct key *keyring) { @@ -160,7 +163,7 @@ static void keyring_destroy(struct key *keyring) } /* - * describe the keyring + * Describe a keyring for /proc. */ static void keyring_describe(const struct key *keyring, struct seq_file *m) { @@ -181,8 +184,9 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m) } /* - * read a list of key IDs from the keyring's contents - * - the keyring's semaphore is read-locked + * Read a list of key IDs from the keyring's contents in binary form + * + * The keyring's semaphore is read-locked by the caller. */ static long keyring_read(const struct key *keyring, char __user *buffer, size_t buflen) @@ -233,7 +237,7 @@ error: } /* - * allocate a keyring and link into the destination keyring + * Allocate a keyring and link into the destination keyring. */ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, unsigned long flags, @@ -258,16 +262,40 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, return keyring; } -/* - * search the supplied keyring tree for a key that matches the criterion - * - perform a breadth-then-depth search up to the prescribed limit - * - we only find keys on which we have search permission - * - we use the supplied match function to see if the description (or other - * feature of interest) matches - * - we rely on RCU to prevent the keyring lists from disappearing on us - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we only found negative matching keys - * - we propagate the possession attribute from the keyring ref to the key ref +/** + * keyring_search_aux - Search a keyring tree for a key matching some criteria + * @keyring_ref: A pointer to the keyring with possession indicator. + * @cred: The credentials to use for permissions checks. + * @type: The type of key to search for. + * @description: Parameter for @match. + * @match: Function to rule on whether or not a key is the one required. + * + * Search the supplied keyring tree for a key that matches the criteria given. + * The root keyring and any linked keyrings must grant Search permission to the + * caller to be searchable and keys can only be found if they too grant Search + * to the caller. The possession flag on the root keyring pointer controls use + * of the possessor bits in permissions checking of the entire tree. In + * addition, the LSM gets to forbid keyring searches and key matches. + * + * The search is performed as a breadth-then-depth search up to the prescribed + * limit (KEYRING_SEARCH_MAX_DEPTH). + * + * Keys are matched to the type provided and are then filtered by the match + * function, which is given the description to use in any way it sees fit. The + * match function may use any attributes of a key that it wishes to to + * determine the match. Normally the match function from the key type would be + * used. + * + * RCU is used to prevent the keyring key lists from disappearing without the + * need to take lots of locks. + * + * Returns a pointer to the found key and increments the key usage count if + * successful; -EAGAIN if no matching keys were found, or if expired or revoked + * keys were found; -ENOKEY if only negative keys were found; -ENOTDIR if the + * specified keyring wasn't a keyring. + * + * In the case of a successful return, the possession attribute from + * @keyring_ref is propagated to the returned key reference. */ key_ref_t keyring_search_aux(key_ref_t keyring_ref, const struct cred *cred, @@ -431,13 +459,14 @@ error: return key_ref; } -/* - * search the supplied keyring tree for a key that matches the criterion - * - perform a breadth-then-depth search up to the prescribed limit - * - we only find keys on which we have search permission - * - we readlock the keyrings as we search down the tree - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we only found negative matching keys +/** + * keyring_search - Search the supplied keyring tree for a matching key + * @keyring: The root of the keyring tree to be searched. + * @type: The type of keyring we want to find. + * @description: The name of the keyring we want to find. + * + * As keyring_search_aux() above, but using the current task's credentials and + * type's default matching function. */ key_ref_t keyring_search(key_ref_t keyring, struct key_type *type, @@ -449,13 +478,22 @@ key_ref_t keyring_search(key_ref_t keyring, return keyring_search_aux(keyring, current->cred, type, description, type->match); } - EXPORT_SYMBOL(keyring_search); /* - * search the given keyring only (no recursion) - * - keyring must be locked by caller - * - caller must guarantee that the keyring is a keyring + * Search the given keyring only (no recursion). + * + * The caller must guarantee that the keyring is a keyring and that the + * permission is granted to search the keyring as no check is made here. + * + * RCU is used to make it unnecessary to lock the keyring key list here. + * + * Returns a pointer to the found key with usage count incremented if + * successful and returns -ENOKEY if not found. Revoked keys and keys not + * providing the requested permission are skipped over. + * + * If successful, the possession indicator is propagated from the keyring ref + * to the returned key reference. */ key_ref_t __keyring_search_one(key_ref_t keyring_ref, const struct key_type *ktype, @@ -498,9 +536,15 @@ found: } /* - * find a keyring with the specified name - * - all named keyrings are searched - * - normally only finds keyrings with search permission for the current process + * Find a keyring with the specified name. + * + * All named keyrings in the current user namespace are searched, provided they + * grant Search permission directly to the caller (unless this check is + * skipped). Keyrings whose usage points have reached zero or who have been + * revoked are skipped. + * + * Returns a pointer to the keyring with the keyring's refcount having being + * incremented on success. -ENOKEY is returned if a key could not be found. */ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) { @@ -551,10 +595,11 @@ out: } /* - * see if a cycle will will be created by inserting acyclic tree B in acyclic - * tree A at the topmost level (ie: as a direct child of A) - * - since we are adding B to A at the top level, checking for cycles should - * just be a matter of seeing if node A is somewhere in tree B + * See if a cycle will will be created by inserting acyclic tree B in acyclic + * tree A at the topmost level (ie: as a direct child of A). + * + * Since we are adding B to A at the top level, checking for cycles should just + * be a matter of seeing if node A is somewhere in tree B. */ static int keyring_detect_cycle(struct key *A, struct key *B) { @@ -637,7 +682,7 @@ cycle_detected: } /* - * dispose of a keyring list after the RCU grace period, freeing the unlinked + * Dispose of a keyring list after the RCU grace period, freeing the unlinked * key */ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) @@ -651,7 +696,7 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) } /* - * preallocate memory so that a key can be linked into to a keyring + * Preallocate memory so that a key can be linked into to a keyring. */ int __key_link_begin(struct key *keyring, const struct key_type *type, const char *description, @@ -768,10 +813,10 @@ error_krsem: } /* - * check already instantiated keys aren't going to be a problem - * - the caller must have called __key_link_begin() - * - don't need to call this for keys that were created since __key_link_begin() - * was called + * Check already instantiated keys aren't going to be a problem. + * + * The caller must have called __key_link_begin(). Don't need to call this for + * keys that were created since __key_link_begin() was called. */ int __key_link_check_live_key(struct key *keyring, struct key *key) { @@ -783,9 +828,12 @@ int __key_link_check_live_key(struct key *keyring, struct key *key) } /* - * link a key into to a keyring - * - must be called with __key_link_begin() having being called - * - discard already extant link to matching key if there is one + * Link a key into to a keyring. + * + * Must be called with __key_link_begin() having being called. Discards any + * already extant link to matching key if there is one, so that each keyring + * holds at most one link to any given key of a particular type+description + * combination. */ void __key_link(struct key *keyring, struct key *key, struct keyring_list **_prealloc) @@ -828,8 +876,9 @@ void __key_link(struct key *keyring, struct key *key, } /* - * finish linking a key into to a keyring - * - must be called with __key_link_begin() having being called + * Finish linking a key into to a keyring. + * + * Must be called with __key_link_begin() having being called. */ void __key_link_end(struct key *keyring, struct key_type *type, struct keyring_list *prealloc) @@ -850,8 +899,25 @@ void __key_link_end(struct key *keyring, struct key_type *type, up_write(&keyring->sem); } -/* - * link a key to a keyring +/** + * key_link - Link a key to a keyring + * @keyring: The keyring to make the link in. + * @key: The key to link to. + * + * Make a link in a keyring to a key, such that the keyring holds a reference + * on that key and the key can potentially be found by searching that keyring. + * + * This function will write-lock the keyring's semaphore and will consume some + * of the user's key data quota to hold the link. + * + * Returns 0 if successful, -ENOTDIR if the keyring isn't a keyring, + * -EKEYREVOKED if the keyring has been revoked, -ENFILE if the keyring is + * full, -EDQUOT if there is insufficient key data quota remaining to add + * another link or -ENOMEM if there's insufficient memory. + * + * It is assumed that the caller has checked that it is permitted for a link to + * be made (the keyring should have Write permission and the key Link + * permission). */ int key_link(struct key *keyring, struct key *key) { @@ -871,11 +937,24 @@ int key_link(struct key *keyring, struct key *key) return ret; } - EXPORT_SYMBOL(key_link); -/* - * unlink the first link to a key from a keyring +/** + * key_unlink - Unlink the first link to a key from a keyring. + * @keyring: The keyring to remove the link from. + * @key: The key the link is to. + * + * Remove a link from a keyring to a key. + * + * This function will write-lock the keyring's semaphore. + * + * Returns 0 if successful, -ENOTDIR if the keyring isn't a keyring, -ENOENT if + * the key isn't linked to by the keyring or -ENOMEM if there's insufficient + * memory. + * + * It is assumed that the caller has checked that it is permitted for a link to + * be removed (the keyring should have Write permission; no permissions are + * required on the key). */ int key_unlink(struct key *keyring, struct key *key) { @@ -944,12 +1023,11 @@ nomem: up_write(&keyring->sem); goto error; } - EXPORT_SYMBOL(key_unlink); /* - * dispose of a keyring list after the RCU grace period, releasing the keys it - * links to + * Dispose of a keyring list after the RCU grace period, releasing the keys it + * links to. */ static void keyring_clear_rcu_disposal(struct rcu_head *rcu) { @@ -964,9 +1042,13 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu) kfree(klist); } -/* - * clear the specified process keyring - * - implements keyctl(KEYCTL_CLEAR) +/** + * keyring_clear - Clear a keyring + * @keyring: The keyring to clear. + * + * Clear the contents of the specified keyring. + * + * Returns 0 if successful or -ENOTDIR if the keyring isn't a keyring. */ int keyring_clear(struct key *keyring) { @@ -999,12 +1081,12 @@ int keyring_clear(struct key *keyring) return ret; } - EXPORT_SYMBOL(keyring_clear); /* - * dispose of the links from a revoked keyring - * - called with the key sem write-locked + * Dispose of the links from a revoked keyring. + * + * This is called with the key sem write-locked. */ static void keyring_revoke(struct key *keyring) { @@ -1022,7 +1104,7 @@ static void keyring_revoke(struct key *keyring) } /* - * Determine whether a key is dead + * Determine whether a key is dead. */ static bool key_is_dead(struct key *key, time_t limit) { @@ -1031,7 +1113,12 @@ static bool key_is_dead(struct key *key, time_t limit) } /* - * Collect garbage from the contents of a keyring + * Collect garbage from the contents of a keyring, replacing the old list with + * a new one with the pointers all shuffled down. + * + * Dead keys are classed as oned that are flagged as being dead or are revoked, + * expired or negative keys that were revoked or expired before the specified + * limit. */ void keyring_gc(struct key *keyring, time_t limit) { diff --git a/security/keys/permission.c b/security/keys/permission.c index 6284b1496c29..c35b5229e3cd 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -1,4 +1,4 @@ -/* permission.c: key permission determination +/* Key permission checking * * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -15,15 +15,17 @@ /** * key_task_permission - Check a key can be used - * @key_ref: The key to check - * @cred: The credentials to use - * @perm: The permissions to check for + * @key_ref: The key to check. + * @cred: The credentials to use. + * @perm: The permissions to check for. * * Check to see whether permission is granted to use a key in the desired way, * but permit the security modules to override. * - * The caller must hold either a ref on cred or must hold the RCU readlock or a - * spinlock. + * The caller must hold either a ref on cred or must hold the RCU readlock. + * + * Returns 0 if successful, -EACCES if access is denied based on the + * permissions bits or the LSM check. */ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, key_perm_t perm) @@ -79,11 +81,15 @@ use_these_perms: /* let LSM be the final arbiter */ return security_key_permission(key_ref, cred, perm); } - EXPORT_SYMBOL(key_task_permission); -/* - * validate a key +/** + * key_validate - Validate a key. + * @key: The key to be validated. + * + * Check that a key is valid, returning 0 if the key is okay, -EKEYREVOKED if + * the key's type has been removed or if the key has been revoked or + * -EKEYEXPIRED if the key has expired. */ int key_validate(struct key *key) { @@ -109,5 +115,4 @@ int key_validate(struct key *key) error: return ret; } - EXPORT_SYMBOL(key_validate); diff --git a/security/keys/proc.c b/security/keys/proc.c index ad5f64e1b320..525cf8a29cdd 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -1,4 +1,4 @@ -/* proc.c: proc files for key database enumeration +/* procfs files for key database enumeration * * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -61,7 +61,7 @@ static const struct file_operations proc_key_users_fops = { }; /* - * declare the /proc files + * Declare the /proc files. */ static int __init key_proc_init(void) { @@ -83,7 +83,8 @@ static int __init key_proc_init(void) __initcall(key_proc_init); /* - * implement "/proc/keys" to provides a list of the keys on the system + * Implement "/proc/keys" to provide a list of the keys on the system that + * grant View permission to the caller. */ #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS @@ -291,7 +292,8 @@ static struct rb_node *key_user_first(struct rb_root *r) } /* - * implement "/proc/key-users" to provides a list of the key users + * Implement "/proc/key-users" to provides a list of the key users and their + * quotas. */ static int proc_key_users_open(struct inode *inode, struct file *file) { diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index ea55cf9acf36..930634e45149 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -1,4 +1,4 @@ -/* Management of a process's keyrings +/* Manage a process's keyrings * * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -21,13 +21,13 @@ #include #include "internal.h" -/* session keyring create vs join semaphore */ +/* Session keyring create vs join semaphore */ static DEFINE_MUTEX(key_session_mutex); -/* user keyring creation semaphore */ +/* User keyring creation semaphore */ static DEFINE_MUTEX(key_user_keyring_mutex); -/* the root user's tracking struct */ +/* The root user's tracking struct */ struct key_user root_key_user = { .usage = ATOMIC_INIT(3), .cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock), @@ -39,7 +39,7 @@ struct key_user root_key_user = { }; /* - * install user and user session keyrings for a particular UID + * Install the user and user session keyrings for the current process's UID. */ int install_user_keyrings(void) { @@ -121,7 +121,8 @@ error: } /* - * install a fresh thread keyring directly to new credentials + * Install a fresh thread keyring directly to new credentials. This keyring is + * allowed to overrun the quota. */ int install_thread_keyring_to_cred(struct cred *new) { @@ -137,7 +138,7 @@ int install_thread_keyring_to_cred(struct cred *new) } /* - * install a fresh thread keyring, discarding the old one + * Install a fresh thread keyring, discarding the old one. */ static int install_thread_keyring(void) { @@ -160,9 +161,10 @@ static int install_thread_keyring(void) } /* - * install a process keyring directly to a credentials struct - * - returns -EEXIST if there was already a process keyring, 0 if one installed, - * and other -ve on any other error + * Install a process keyring directly to a credentials struct. + * + * Returns -EEXIST if there was already a process keyring, 0 if one installed, + * and other value on any other error */ int install_process_keyring_to_cred(struct cred *new) { @@ -191,8 +193,11 @@ int install_process_keyring_to_cred(struct cred *new) } /* - * make sure a process keyring is installed - * - we + * Make sure a process keyring is installed for the current process. The + * existing process keyring is not replaced. + * + * Returns 0 if there is a process keyring by the end of this function, some + * error otherwise. */ static int install_process_keyring(void) { @@ -213,7 +218,7 @@ static int install_process_keyring(void) } /* - * install a session keyring directly to a credentials struct + * Install a session keyring directly to a credentials struct. */ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) { @@ -253,8 +258,8 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring) } /* - * install a session keyring, discarding the old one - * - if a keyring is not supplied, an empty one is invented + * Install a session keyring, discarding the old one. If a keyring is not + * supplied, an empty one is invented. */ static int install_session_keyring(struct key *keyring) { @@ -275,7 +280,7 @@ static int install_session_keyring(struct key *keyring) } /* - * the filesystem user ID changed + * Handle the fsuid changing. */ void key_fsuid_changed(struct task_struct *tsk) { @@ -289,7 +294,7 @@ void key_fsuid_changed(struct task_struct *tsk) } /* - * the filesystem group ID changed + * Handle the fsgid changing. */ void key_fsgid_changed(struct task_struct *tsk) { @@ -303,11 +308,25 @@ void key_fsgid_changed(struct task_struct *tsk) } /* - * search only my process keyrings for the first matching key - * - we use the supplied match function to see if the description (or other - * feature of interest) matches - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys + * Search the process keyrings attached to the supplied cred for the first + * matching key. + * + * The search criteria are the type and the match function. The description is + * given to the match function as a parameter, but doesn't otherwise influence + * the search. Typically the match function will compare the description + * parameter to the key's description. + * + * This can only search keyrings that grant Search permission to the supplied + * credentials. Keyrings linked to searched keyrings will also be searched if + * they grant Search permission too. Keys can only be found if they grant + * Search permission to the credentials. + * + * Returns a pointer to the key with the key usage count incremented if + * successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only + * matched negative keys. + * + * In the case of a successful return, the possession attribute is set on the + * returned key reference. */ key_ref_t search_my_process_keyrings(struct key_type *type, const void *description, @@ -423,11 +442,12 @@ found: } /* - * search the process keyrings for the first matching key - * - we use the supplied match function to see if the description (or other - * feature of interest) matches - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys + * Search the process keyrings attached to the supplied cred for the first + * matching key in the manner of search_my_process_keyrings(), but also search + * the keys attached to the assumed authorisation key using its credentials if + * one is available. + * + * Return same as search_my_process_keyrings(). */ key_ref_t search_process_keyrings(struct key_type *type, const void *description, @@ -485,7 +505,7 @@ found: } /* - * see if the key we're looking at is the target key + * See if the key we're looking at is the target key. */ int lookup_user_key_possessed(const struct key *key, const void *target) { @@ -493,9 +513,22 @@ int lookup_user_key_possessed(const struct key *key, const void *target) } /* - * lookup a key given a key ID from userspace with a given permissions mask - * - don't create special keyrings unless so requested - * - partially constructed keys aren't found unless requested + * Look up a key ID given us by userspace with a given permissions mask to get + * the key it refers to. + * + * Flags can be passed to request that special keyrings be created if referred + * to directly, to permit partially constructed keys to be found and to skip + * validity and permission checks on the found key. + * + * Returns a pointer to the key with an incremented usage count if successful; + * -EINVAL if the key ID is invalid; -ENOKEY if the key ID does not correspond + * to a key or the best found key was a negative key; -EKEYREVOKED or + * -EKEYEXPIRED if the best found key was revoked or expired; -EACCES if the + * found key doesn't grant the requested permit or the LSM denied access to it; + * or -ENOMEM if a special keyring couldn't be created. + * + * In the case of a successful return, the possession attribute is set on the + * returned key reference. */ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags, key_perm_t perm) @@ -703,10 +736,15 @@ reget_creds: } /* - * join the named keyring as the session keyring if possible, or attempt to - * create a new one of that name if not - * - if the name is NULL, an empty anonymous keyring is installed instead - * - named session keyring joining is done with a semaphore held + * Join the named keyring as the session keyring if possible else attempt to + * create a new one of that name and join that. + * + * If the name is NULL, an empty anonymous keyring will be installed as the + * session keyring. + * + * Named session keyrings are joined with a semaphore held to prevent the + * keyrings from going away whilst the attempt is made to going them and also + * to prevent a race in creating compatible session keyrings. */ long join_session_keyring(const char *name) { @@ -778,8 +816,8 @@ error: } /* - * Replace a process's session keyring when that process resumes userspace on - * behalf of one of its children + * Replace a process's session keyring on behalf of one of its children when + * the target process is about to resume userspace execution. */ void key_replace_session_keyring(void) { diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0ea52d25a6bd..9a7fb3914b27 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -39,8 +39,14 @@ static int key_wait_bit_intr(void *flags) return signal_pending(current) ? -ERESTARTSYS : 0; } -/* - * call to complete the construction of a key +/** + * complete_request_key - Complete the construction of a key. + * @cons: The key construction record. + * @error: The success or failute of the construction. + * + * Complete the attempt to construct a key. The key will be negated + * if an error is indicated. The authorisation key will be revoked + * unconditionally. */ void complete_request_key(struct key_construction *cons, int error) { @@ -58,23 +64,33 @@ void complete_request_key(struct key_construction *cons, int error) } EXPORT_SYMBOL(complete_request_key); +/* + * Initialise a usermode helper that is going to have a specific session + * keyring. + * + * This is called in context of freshly forked kthread before kernel_execve(), + * so we can simply install the desired session_keyring at this point. + */ static int umh_keys_init(struct subprocess_info *info) { struct cred *cred = (struct cred*)current_cred(); struct key *keyring = info->data; - /* - * This is called in context of freshly forked kthread before - * kernel_execve(), we can just change our ->session_keyring. - */ + return install_session_keyring_to_cred(cred, keyring); } +/* + * Clean up a usermode helper with session keyring. + */ static void umh_keys_cleanup(struct subprocess_info *info) { struct key *keyring = info->data; key_put(keyring); } +/* + * Call a usermode helper with a specific session keyring. + */ static int call_usermodehelper_keys(char *path, char **argv, char **envp, struct key *session_keyring, enum umh_wait wait) { @@ -91,7 +107,7 @@ static int call_usermodehelper_keys(char *path, char **argv, char **envp, } /* - * request userspace finish the construction of a key + * Request userspace finish the construction of a key * - execute "/sbin/request-key " */ static int call_sbin_request_key(struct key_construction *cons, @@ -198,8 +214,9 @@ error_alloc: } /* - * call out to userspace for key construction - * - we ignore program failure and go on key status instead + * Call out to userspace for key construction. + * + * Program failure is ignored in favour of key status. */ static int construct_key(struct key *key, const void *callout_info, size_t callout_len, void *aux, @@ -246,9 +263,10 @@ static int construct_key(struct key *key, const void *callout_info, } /* - * get the appropriate destination keyring for the request - * - we return whatever keyring we select with an extra reference upon it which - * the caller must release + * Get the appropriate destination keyring for the request. + * + * The keyring selected is returned with an extra reference upon it which the + * caller must release. */ static void construct_get_dest_keyring(struct key **_dest_keyring) { @@ -321,9 +339,11 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) } /* - * allocate a new key in under-construction state and attempt to link it in to - * the requested place - * - may return a key that's already under construction instead + * Allocate a new key in under-construction state and attempt to link it in to + * the requested keyring. + * + * May return a key that's already under construction instead if there was a + * race between two thread calling request_key(). */ static int construct_alloc_key(struct key_type *type, const char *description, @@ -414,7 +434,7 @@ alloc_failed: } /* - * commence key construction + * Commence key construction. */ static struct key *construct_key_and_link(struct key_type *type, const char *description, @@ -465,12 +485,32 @@ construction_failed: return ERR_PTR(ret); } -/* - * request a key - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - cache the key in an appropriate keyring +/** + * request_key_and_link - Request a key and cache it in a keyring. + * @type: The type of key we want. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * @dest_keyring: Where to cache the key. + * @flags: Flags to key_alloc(). + * + * A key matching the specified criteria is searched for in the process's + * keyrings and returned with its usage count incremented if found. Otherwise, + * if callout_info is not NULL, a key will be allocated and some service + * (probably in userspace) will be asked to instantiate it. + * + * If successfully found or created, the key will be linked to the destination + * keyring if one is provided. + * + * Returns a pointer to the key if successful; -EACCES, -ENOKEY, -EKEYREVOKED + * or -EKEYEXPIRED if an inaccessible, negative, revoked or expired key was + * found; -ENOKEY if no key was found and no @callout_info was given; -EDQUOT + * if insufficient key quota was available to create a new key; or -ENOMEM if + * insufficient memory was available. + * + * If the returned key was created, then it may still be under construction, + * and wait_for_key_construction() should be used to wait for that to complete. */ struct key *request_key_and_link(struct key_type *type, const char *description, @@ -524,8 +564,16 @@ error: return key; } -/* - * wait for construction of a key to complete +/** + * wait_for_key_construction - Wait for construction of a key to complete + * @key: The key being waited for. + * @intr: Whether to wait interruptibly. + * + * Wait for a key to finish being constructed. + * + * Returns 0 if successful; -ERESTARTSYS if the wait was interrupted; -ENOKEY + * if the key was negated; or -EKEYREVOKED or -EKEYEXPIRED if the key was + * revoked or expired. */ int wait_for_key_construction(struct key *key, bool intr) { @@ -542,12 +590,19 @@ int wait_for_key_construction(struct key *key, bool intr) } EXPORT_SYMBOL(wait_for_key_construction); -/* - * request a key - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - waits uninterruptible for creation to complete +/** + * request_key - Request a key and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found, new keys are always allocated in the user's quota, + * the callout_info must be a NUL-terminated string and no auxiliary data can + * be passed. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait. */ struct key *request_key(struct key_type *type, const char *description, @@ -572,12 +627,19 @@ struct key *request_key(struct key_type *type, } EXPORT_SYMBOL(request_key); -/* - * request a key with auxiliary data for the upcaller - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - waits uninterruptible for creation to complete +/** + * request_key_with_auxdata - Request a key with auxiliary data for the upcaller + * @type: The type of key we want. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found and new keys are always allocated in the user's quota. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait. */ struct key *request_key_with_auxdata(struct key_type *type, const char *description, @@ -602,10 +664,18 @@ struct key *request_key_with_auxdata(struct key_type *type, EXPORT_SYMBOL(request_key_with_auxdata); /* - * request a key (allow async construction) - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided + * request_key_async - Request a key (allow async construction) + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found, new keys are always allocated in the user's quota and + * no auxiliary data can be passed. + * + * The caller should call wait_for_key_construction() to wait for the + * completion of the returned key if it is still undergoing construction. */ struct key *request_key_async(struct key_type *type, const char *description, @@ -620,9 +690,17 @@ EXPORT_SYMBOL(request_key_async); /* * request a key with auxiliary data for the upcaller (allow async construction) - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found and new keys are always allocated in the user's quota. + * + * The caller should call wait_for_key_construction() to wait for the + * completion of the returned key if it is still undergoing construction. */ struct key *request_key_async_with_auxdata(struct key_type *type, const char *description, diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index ba717b8163ab..68164031a74e 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -1,4 +1,4 @@ -/* request_key_auth.c: request key authorisation controlling key def +/* Request key authorisation token key definition. * * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) @@ -26,7 +26,7 @@ static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char __user *, size_t); /* - * the request-key authorisation key type definition + * The request-key authorisation key type definition. */ struct key_type key_type_request_key_auth = { .name = ".request_key_auth", @@ -39,7 +39,7 @@ struct key_type key_type_request_key_auth = { }; /* - * instantiate a request-key authorisation key + * Instantiate a request-key authorisation key. */ static int request_key_auth_instantiate(struct key *key, const void *data, @@ -50,7 +50,7 @@ static int request_key_auth_instantiate(struct key *key, } /* - * reading a request-key authorisation key retrieves the callout information + * Describe an authorisation token. */ static void request_key_auth_describe(const struct key *key, struct seq_file *m) @@ -63,7 +63,7 @@ static void request_key_auth_describe(const struct key *key, } /* - * read the callout_info data + * Read the callout_info data (retrieves the callout information). * - the key's semaphore is read-locked */ static long request_key_auth_read(const struct key *key, @@ -89,8 +89,9 @@ static long request_key_auth_read(const struct key *key, } /* - * handle revocation of an authorisation token key - * - called with the key sem write-locked + * Handle revocation of an authorisation token key. + * + * Called with the key sem write-locked. */ static void request_key_auth_revoke(struct key *key) { @@ -105,7 +106,7 @@ static void request_key_auth_revoke(struct key *key) } /* - * destroy an instantiation authorisation token key + * Destroy an instantiation authorisation token key. */ static void request_key_auth_destroy(struct key *key) { @@ -125,8 +126,8 @@ static void request_key_auth_destroy(struct key *key) } /* - * create an authorisation token for /sbin/request-key or whoever to gain - * access to the caller's security data + * Create an authorisation token for /sbin/request-key or whoever to gain + * access to the caller's security data. */ struct key *request_key_auth_new(struct key *target, const void *callout_info, size_t callout_len, struct key *dest_keyring) @@ -220,7 +221,7 @@ error_alloc: } /* - * see if an authorisation key is associated with a particular key + * See if an authorisation key is associated with a particular key. */ static int key_get_instantiation_authkey_match(const struct key *key, const void *_id) @@ -232,11 +233,8 @@ static int key_get_instantiation_authkey_match(const struct key *key, } /* - * get the authorisation key for instantiation of a specific key if attached to - * the current process's keyrings - * - this key is inserted into a keyring and that is set as /sbin/request-key's - * session keyring - * - a target_id of zero specifies any valid token + * Search the current process's keyrings for the authorisation key for + * instantiation of a key. */ struct key *key_get_instantiation_authkey(key_serial_t target_id) { -- cgit v1.2.3 From 2221eca0a2c0f7f9918efdcaa52fb8e1adff991f Mon Sep 17 00:00:00 2001 From: Vasiliy Kulikov Date: Fri, 21 Jan 2011 16:24:23 -0800 Subject: atm: idt77105: fix fetch_stats() result copy_to_user() used PRIV(dev)->stats instead of local stats variable. Zero stats were returned to user in case of (zero != 0), also memcpy() was pointless. Signed-off-by: Vasiliy Kulikov Signed-off-by: David S. Miller --- drivers/atm/idt77105.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/atm/idt77105.c b/drivers/atm/idt77105.c index bca9cb89a118..487a54739854 100644 --- a/drivers/atm/idt77105.c +++ b/drivers/atm/idt77105.c @@ -151,7 +151,7 @@ static int fetch_stats(struct atm_dev *dev,struct idt77105_stats __user *arg,int spin_unlock_irqrestore(&idt77105_priv_lock, flags); if (arg == NULL) return 0; - return copy_to_user(arg, &PRIV(dev)->stats, + return copy_to_user(arg, &stats, sizeof(struct idt77105_stats)) ? -EFAULT : 0; } -- cgit v1.2.3 From 93789b32dbf355e70f18b17a82e8661677a7f7fb Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Thu, 20 Jan 2011 15:42:52 +0100 Subject: x86, hotplug: Fix powersavings with offlined cores on AMD ea53069231f9317062910d6e772cca4ce93de8c8 made a CPU use monitor/mwait when offline. This is not the optimal choice for AMD wrt to powersavings and we'd prefer our cores to halt (i.e. enter C1) instead. For this, the same selection whether to use monitor/mwait has to be used as when we select the idle routine for the machine. With this patch, offlining cores 1-5 on a X6 machine allows core0 to boost again. [ hpa: putting this in urgent since it is a (power) regression fix ] Reported-by: Andreas Herrmann Cc: stable@kernel.org # 37.x Cc: H. Peter Anvin Cc: Arjan van de Ven Cc: Len Brown Cc: Venkatesh Pallipadi Cc: Peter Zijlstra Signed-off-by: Borislav Petkov LKML-Reference: <1295534572-10730-1-git-send-email-bp@amd64.org> Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/cpu.h | 1 + arch/x86/kernel/process.c | 3 ++- arch/x86/kernel/smpboot.c | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 4fab24de26b1..6e6e7558e702 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -32,5 +32,6 @@ extern void arch_unregister_cpu(int); DECLARE_PER_CPU(int, cpu_state); +int __cpuinit mwait_usable(const struct cpuinfo_x86 *); #endif /* _ASM_X86_CPU_H */ diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index d8286ed54ffa..e764fc05d700 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -505,7 +506,7 @@ static void poll_idle(void) #define MWAIT_ECX_EXTENDED_INFO 0x01 #define MWAIT_EDX_C1 0xf0 -static int __cpuinit mwait_usable(const struct cpuinfo_x86 *c) +int __cpuinit mwait_usable(const struct cpuinfo_x86 *c) { u32 eax, ebx, ecx, edx; diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 763df77343dd..0cbe8c0b35ed 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -1402,8 +1402,9 @@ static inline void mwait_play_dead(void) unsigned int highest_subcstate = 0; int i; void *mwait_ptr; + struct cpuinfo_x86 *c = __this_cpu_ptr(&cpu_info); - if (!cpu_has(__this_cpu_ptr(&cpu_info), X86_FEATURE_MWAIT)) + if (!(cpu_has(c, X86_FEATURE_MWAIT) && mwait_usable(c))) return; if (!cpu_has(__this_cpu_ptr(&cpu_info), X86_FEATURE_CLFLSH)) return; -- cgit v1.2.3 From 1bae4ce27c9c90344f23c65ea6966c50ffeae2f5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 21 Jan 2011 19:01:34 -0800 Subject: Linux 2.6.38-rc2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index abb49bf8596e..1f474953427f 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* -- cgit v1.2.3 From 03fd3cf5a179da12e6bee5e9d74b648aff68dc4c Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Tue, 11 Jan 2011 04:32:31 +0000 Subject: can: add driver for Softing card This patch adds a driver for the platform:softing device. This will create (up to) 2 CAN network devices from 1 platform:softing device Signed-off-by: Kurt Van Dijck Acked-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/Kconfig | 2 + drivers/net/can/Makefile | 1 + drivers/net/can/softing/Kconfig | 16 + drivers/net/can/softing/Makefile | 5 + drivers/net/can/softing/softing.h | 167 ++++++ drivers/net/can/softing/softing_fw.c | 691 ++++++++++++++++++++++ drivers/net/can/softing/softing_main.c | 893 +++++++++++++++++++++++++++++ drivers/net/can/softing/softing_platform.h | 40 ++ 8 files changed, 1815 insertions(+) create mode 100644 drivers/net/can/softing/Kconfig create mode 100644 drivers/net/can/softing/Makefile create mode 100644 drivers/net/can/softing/softing.h create mode 100644 drivers/net/can/softing/softing_fw.c create mode 100644 drivers/net/can/softing/softing_main.c create mode 100644 drivers/net/can/softing/softing_platform.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d5a9db60ade9..986195eaa57c 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -117,6 +117,8 @@ source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/usb/Kconfig" +source "drivers/net/can/softing/Kconfig" + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 07ca159ba3f9..53c82a71778e 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o obj-y += usb/ +obj-y += softing/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ diff --git a/drivers/net/can/softing/Kconfig b/drivers/net/can/softing/Kconfig new file mode 100644 index 000000000000..072f337f42e3 --- /dev/null +++ b/drivers/net/can/softing/Kconfig @@ -0,0 +1,16 @@ +config CAN_SOFTING + tristate "Softing Gmbh CAN generic support" + depends on CAN_DEV + ---help--- + Support for CAN cards from Softing Gmbh & some cards + from Vector Gmbh. + Softing Gmbh CAN cards come with 1 or 2 physical busses. + Those cards typically use Dual Port RAM to communicate + with the host CPU. The interface is then identical for PCI + and PCMCIA cards. This driver operates on a platform device, + which has been created by softing_cs or softing_pci driver. + Warning: + The API of the card does not allow fine control per bus, but + controls the 2 busses on the card together. + As such, some actions (start/stop/busoff recovery) on 1 bus + must bring down the other bus too temporarily. diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile new file mode 100644 index 000000000000..7db04452deb6 --- /dev/null +++ b/drivers/net/can/softing/Makefile @@ -0,0 +1,5 @@ + +softing-y := softing_main.o softing_fw.o +obj-$(CONFIG_CAN_SOFTING) += softing.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h new file mode 100644 index 000000000000..7ec9f4db3d52 --- /dev/null +++ b/drivers/net/can/softing/softing.h @@ -0,0 +1,167 @@ +/* + * softing common interfaces + * + * by Kurt Van Dijck, 2008-2010 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "softing_platform.h" + +struct softing; + +struct softing_priv { + struct can_priv can; /* must be the first member! */ + struct net_device *netdev; + struct softing *card; + struct { + int pending; + /* variables wich hold the circular buffer */ + int echo_put; + int echo_get; + } tx; + struct can_bittiming_const btr_const; + int index; + uint8_t output; + uint16_t chip; +}; +#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev)) + +struct softing { + const struct softing_platform_data *pdat; + struct platform_device *pdev; + struct net_device *net[2]; + spinlock_t spin; /* protect this structure & DPRAM access */ + ktime_t ts_ref; + ktime_t ts_overflow; /* timestamp overflow value, in ktime */ + + struct { + /* indication of firmware status */ + int up; + /* protection of the 'up' variable */ + struct mutex lock; + } fw; + struct { + int nr; + int requested; + int svc_count; + unsigned int dpram_position; + } irq; + struct { + int pending; + int last_bus; + /* + * keep the bus that last tx'd a message, + * in order to let every netdev queue resume + */ + } tx; + __iomem uint8_t *dpram; + unsigned long dpram_phys; + unsigned long dpram_size; + struct { + uint16_t fw_version, hw_version, license, serial; + uint16_t chip[2]; + unsigned int freq; /* remote cpu's operating frequency */ + } id; +}; + +extern int softing_default_output(struct net_device *netdev); + +extern ktime_t softing_raw2ktime(struct softing *card, u32 raw); + +extern int softing_chip_poweron(struct softing *card); + +extern int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg); + +/* Load firmware after reset */ +extern int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *virt, unsigned int size, int offset); + +/* Load final application firmware after bootloader */ +extern int softing_load_app_fw(const char *file, struct softing *card); + +/* + * enable or disable irq + * only called with fw.lock locked + */ +extern int softing_enable_irq(struct softing *card, int enable); + +/* start/stop 1 bus on card */ +extern int softing_startstop(struct net_device *netdev, int up); + +/* netif_rx() */ +extern int softing_netdev_rx(struct net_device *netdev, + const struct can_frame *msg, ktime_t ktime); + +/* SOFTING DPRAM mappings */ +#define DPRAM_RX 0x0000 + #define DPRAM_RX_SIZE 32 + #define DPRAM_RX_CNT 16 +#define DPRAM_RX_RD 0x0201 /* uint8_t */ +#define DPRAM_RX_WR 0x0205 /* uint8_t */ +#define DPRAM_RX_LOST 0x0207 /* uint8_t */ + +#define DPRAM_FCT_PARAM 0x0300 /* int16_t [20] */ +#define DPRAM_FCT_RESULT 0x0328 /* int16_t */ +#define DPRAM_FCT_HOST 0x032b /* uint16_t */ + +#define DPRAM_INFO_BUSSTATE 0x0331 /* uint16_t */ +#define DPRAM_INFO_BUSSTATE2 0x0335 /* uint16_t */ +#define DPRAM_INFO_ERRSTATE 0x0339 /* uint16_t */ +#define DPRAM_INFO_ERRSTATE2 0x033d /* uint16_t */ +#define DPRAM_RESET 0x0341 /* uint16_t */ +#define DPRAM_CLR_RECV_FIFO 0x0345 /* uint16_t */ +#define DPRAM_RESET_TIME 0x034d /* uint16_t */ +#define DPRAM_TIME 0x0350 /* uint64_t */ +#define DPRAM_WR_START 0x0358 /* uint8_t */ +#define DPRAM_WR_END 0x0359 /* uint8_t */ +#define DPRAM_RESET_RX_FIFO 0x0361 /* uint16_t */ +#define DPRAM_RESET_TX_FIFO 0x0364 /* uint8_t */ +#define DPRAM_READ_FIFO_LEVEL 0x0365 /* uint8_t */ +#define DPRAM_RX_FIFO_LEVEL 0x0366 /* uint16_t */ +#define DPRAM_TX_FIFO_LEVEL 0x0366 /* uint16_t */ + +#define DPRAM_TX 0x0400 /* uint16_t */ + #define DPRAM_TX_SIZE 16 + #define DPRAM_TX_CNT 32 +#define DPRAM_TX_RD 0x0601 /* uint8_t */ +#define DPRAM_TX_WR 0x0605 /* uint8_t */ + +#define DPRAM_COMMAND 0x07e0 /* uint16_t */ +#define DPRAM_RECEIPT 0x07f0 /* uint16_t */ +#define DPRAM_IRQ_TOHOST 0x07fe /* uint8_t */ +#define DPRAM_IRQ_TOCARD 0x07ff /* uint8_t */ + +#define DPRAM_V2_RESET 0x0e00 /* uint8_t */ +#define DPRAM_V2_IRQ_TOHOST 0x0e02 /* uint8_t */ + +#define TXMAX (DPRAM_TX_CNT - 1) + +/* DPRAM return codes */ +#define RES_NONE 0 +#define RES_OK 1 +#define RES_NOK 2 +#define RES_UNKNOWN 3 +/* DPRAM flags */ +#define CMD_TX 0x01 +#define CMD_ACK 0x02 +#define CMD_XTD 0x04 +#define CMD_RTR 0x08 +#define CMD_ERR 0x10 +#define CMD_BUS2 0x80 + +/* returned fifo entry bus state masks */ +#define SF_MASK_BUSOFF 0x80 +#define SF_MASK_EPASSIVE 0x60 + +/* bus states */ +#define STATE_BUSOFF 2 +#define STATE_EPASSIVE 1 +#define STATE_EACTIVE 0 diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c new file mode 100644 index 000000000000..b520784fb197 --- /dev/null +++ b/drivers/net/can/softing/softing_fw.c @@ -0,0 +1,691 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "softing.h" + +/* + * low level DPRAM command. + * Make sure that card->dpram[DPRAM_FCT_HOST] is preset + */ +static int _softing_fct_cmd(struct softing *card, int16_t cmd, uint16_t vector, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(cmd, &card->dpram[DPRAM_FCT_PARAM]); + iowrite8(vector >> 8, &card->dpram[DPRAM_FCT_HOST + 1]); + iowrite8(vector, &card->dpram[DPRAM_FCT_HOST]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 1 * HZ; + /* wait for card */ + do { + /* DPRAM_FCT_HOST is _not_ aligned */ + ret = ioread8(&card->dpram[DPRAM_FCT_HOST]) + + (ioread8(&card->dpram[DPRAM_FCT_HOST + 1]) << 8); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + /* read return-value now */ + return ioread16(&card->dpram[DPRAM_FCT_RESULT]); + + if ((ret != vector) || time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (1); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "firmware %s failed (%i)\n", msg, ret); + return ret; +} + +static int softing_fct_cmd(struct softing *card, int16_t cmd, const char *msg) +{ + int ret; + + ret = _softing_fct_cmd(card, cmd, 0, msg); + if (ret > 0) { + dev_alert(&card->pdev->dev, "%s returned %u\n", msg, ret); + ret = -EIO; + } + return ret; +} + +int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(RES_NONE, &card->dpram[DPRAM_RECEIPT]); + iowrite16(cmd, &card->dpram[DPRAM_COMMAND]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 3 * HZ; + /* wait for card */ + do { + ret = ioread16(&card->dpram[DPRAM_RECEIPT]); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + return 0; + if (time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (!signal_pending(current)); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "bootloader %s failed (%i)\n", msg, ret); + return ret; +} + +static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, + uint16_t *plen, const uint8_t **pdat) +{ + uint16_t checksum[2]; + const uint8_t *mem; + const uint8_t *end; + + /* + * firmware records are a binary, unaligned stream composed of: + * uint16_t type; + * uint32_t addr; + * uint16_t len; + * uint8_t dat[len]; + * uint16_t checksum; + * all values in little endian. + * We could define a struct for this, with __attribute__((packed)), + * but would that solve the alignment in _all_ cases (cfr. the + * struct itself may be an odd address)? + * + * I chose to use leXX_to_cpup() since this solves both + * endianness & alignment. + */ + mem = *pmem; + *ptype = le16_to_cpup((void *)&mem[0]); + *paddr = le32_to_cpup((void *)&mem[2]); + *plen = le16_to_cpup((void *)&mem[6]); + *pdat = &mem[8]; + /* verify checksum */ + end = &mem[8 + *plen]; + checksum[0] = le16_to_cpup((void *)end); + for (checksum[1] = 0; mem < end; ++mem) + checksum[1] += *mem; + if (checksum[0] != checksum[1]) + return -EINVAL; + /* increment */ + *pmem += 10 + *plen; + return 0; +} + +int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *dpram, unsigned int size, int offset) +{ + const struct firmware *fw; + int ret; + const uint8_t *mem, *end, *dat; + uint16_t type, len; + uint32_t addr; + uint8_t *buf = NULL; + int buflen = 0; + int8_t type_end = 0; + + ret = request_firmware(&fw, file, &card->pdev->dev); + if (ret < 0) + return ret; + dev_dbg(&card->pdev->dev, "%s, firmware(%s) got %u bytes" + ", offset %c0x%04x\n", + card->pdat->name, file, (unsigned int)fw->size, + (offset >= 0) ? '+' : '-', (unsigned int)abs(offset)); + /* parse the firmware */ + mem = fw->data; + end = &mem[fw->size]; + /* look for header record */ + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret < 0) + goto failed; + if (type != 0xffff) + goto failed; + if (strncmp("Structured Binary Format, Softing GmbH" , dat, len)) { + ret = -EINVAL; + goto failed; + } + /* ok, we had a header */ + while (mem < end) { + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret < 0) + goto failed; + if (type == 3) { + /* start address, not used here */ + continue; + } else if (type == 1) { + /* eof */ + type_end = 1; + break; + } else if (type != 0) { + ret = -EINVAL; + goto failed; + } + + if ((addr + len + offset) > size) + goto failed; + memcpy_toio(&dpram[addr + offset], dat, len); + /* be sure to flush caches from IO space */ + mb(); + if (len > buflen) { + /* align buflen */ + buflen = (len + (1024-1)) & ~(1024-1); + buf = krealloc(buf, buflen, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto failed; + } + } + /* verify record data */ + memcpy_fromio(buf, &dpram[addr + offset], len); + if (memcmp(buf, dat, len)) { + /* is not ok */ + dev_alert(&card->pdev->dev, "DPRAM readback failed\n"); + ret = -EIO; + goto failed; + } + } + if (!type_end) + /* no end record seen */ + goto failed; + ret = 0; +failed: + kfree(buf); + release_firmware(fw); + if (ret < 0) + dev_info(&card->pdev->dev, "firmware %s failed\n", file); + return ret; +} + +int softing_load_app_fw(const char *file, struct softing *card) +{ + const struct firmware *fw; + const uint8_t *mem, *end, *dat; + int ret, j; + uint16_t type, len; + uint32_t addr, start_addr = 0; + unsigned int sum, rx_sum; + int8_t type_end = 0, type_entrypoint = 0; + + ret = request_firmware(&fw, file, &card->pdev->dev); + if (ret) { + dev_alert(&card->pdev->dev, "request_firmware(%s) got %i\n", + file, ret); + return ret; + } + dev_dbg(&card->pdev->dev, "firmware(%s) got %lu bytes\n", + file, (unsigned long)fw->size); + /* parse the firmware */ + mem = fw->data; + end = &mem[fw->size]; + /* look for header record */ + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret) + goto failed; + ret = -EINVAL; + if (type != 0xffff) { + dev_alert(&card->pdev->dev, "firmware starts with type 0x%x\n", + type); + goto failed; + } + if (strncmp("Structured Binary Format, Softing GmbH", dat, len)) { + dev_alert(&card->pdev->dev, "firmware string '%.*s' fault\n", + len, dat); + goto failed; + } + /* ok, we had a header */ + while (mem < end) { + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret) + goto failed; + + if (type == 3) { + /* start address */ + start_addr = addr; + type_entrypoint = 1; + continue; + } else if (type == 1) { + /* eof */ + type_end = 1; + break; + } else if (type != 0) { + dev_alert(&card->pdev->dev, + "unknown record type 0x%04x\n", type); + ret = -EINVAL; + goto failed; + } + + /* regualar data */ + for (sum = 0, j = 0; j < len; ++j) + sum += dat[j]; + /* work in 16bit (target) */ + sum &= 0xffff; + + memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len); + iowrite32(card->pdat->app.offs + card->pdat->app.addr, + &card->dpram[DPRAM_COMMAND + 2]); + iowrite32(addr, &card->dpram[DPRAM_COMMAND + 6]); + iowrite16(len, &card->dpram[DPRAM_COMMAND + 10]); + iowrite8(1, &card->dpram[DPRAM_COMMAND + 12]); + ret = softing_bootloader_command(card, 1, "loading app."); + if (ret < 0) + goto failed; + /* verify checksum */ + rx_sum = ioread16(&card->dpram[DPRAM_RECEIPT + 2]); + if (rx_sum != sum) { + dev_alert(&card->pdev->dev, "SRAM seems to be damaged" + ", wanted 0x%04x, got 0x%04x\n", sum, rx_sum); + ret = -EIO; + goto failed; + } + } + if (!type_end || !type_entrypoint) + goto failed; + /* start application in card */ + iowrite32(start_addr, &card->dpram[DPRAM_COMMAND + 2]); + iowrite8(1, &card->dpram[DPRAM_COMMAND + 6]); + ret = softing_bootloader_command(card, 3, "start app."); + if (ret < 0) + goto failed; + ret = 0; +failed: + release_firmware(fw); + if (ret < 0) + dev_info(&card->pdev->dev, "firmware %s failed\n", file); + return ret; +} + +static int softing_reset_chip(struct softing *card) +{ + int ret; + + do { + /* reset chip */ + iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO]); + iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO+1]); + iowrite8(1, &card->dpram[DPRAM_RESET]); + iowrite8(0, &card->dpram[DPRAM_RESET+1]); + + ret = softing_fct_cmd(card, 0, "reset_can"); + if (!ret) + break; + if (signal_pending(current)) + /* don't wait any longer */ + break; + } while (1); + card->tx.pending = 0; + return ret; +} + +int softing_chip_poweron(struct softing *card) +{ + int ret; + /* sync */ + ret = _softing_fct_cmd(card, 99, 0x55, "sync-a"); + if (ret < 0) + goto failed; + + ret = _softing_fct_cmd(card, 99, 0xaa, "sync-b"); + if (ret < 0) + goto failed; + + ret = softing_reset_chip(card); + if (ret < 0) + goto failed; + /* get_serial */ + ret = softing_fct_cmd(card, 43, "get_serial_number"); + if (ret < 0) + goto failed; + card->id.serial = ioread32(&card->dpram[DPRAM_FCT_PARAM]); + /* get_version */ + ret = softing_fct_cmd(card, 12, "get_version"); + if (ret < 0) + goto failed; + card->id.fw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 2]); + card->id.hw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 4]); + card->id.license = ioread16(&card->dpram[DPRAM_FCT_PARAM + 6]); + card->id.chip[0] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 8]); + card->id.chip[1] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 10]); + return 0; +failed: + return ret; +} + +static void softing_initialize_timestamp(struct softing *card) +{ + uint64_t ovf; + + card->ts_ref = ktime_get(); + + /* 16MHz is the reference */ + ovf = 0x100000000ULL * 16; + do_div(ovf, card->pdat->freq ?: 16); + + card->ts_overflow = ktime_add_us(ktime_set(0, 0), ovf); +} + +ktime_t softing_raw2ktime(struct softing *card, u32 raw) +{ + uint64_t rawl; + ktime_t now, real_offset; + ktime_t target; + ktime_t tmp; + + now = ktime_get(); + real_offset = ktime_sub(ktime_get_real(), now); + + /* find nsec from card */ + rawl = raw * 16; + do_div(rawl, card->pdat->freq ?: 16); + target = ktime_add_us(card->ts_ref, rawl); + /* test for overflows */ + tmp = ktime_add(target, card->ts_overflow); + while (unlikely(ktime_to_ns(tmp) > ktime_to_ns(now))) { + card->ts_ref = ktime_add(card->ts_ref, card->ts_overflow); + target = tmp; + tmp = ktime_add(target, card->ts_overflow); + } + return ktime_add(target, real_offset); +} + +static inline int softing_error_reporting(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + ? 1 : 0; +} + +int softing_startstop(struct net_device *dev, int up) +{ + int ret; + struct softing *card; + struct softing_priv *priv; + struct net_device *netdev; + int bus_bitmask_start; + int j, error_reporting; + struct can_frame msg; + const struct can_bittiming *bt; + + priv = netdev_priv(dev); + card = priv->card; + + if (!card->fw.up) + return -EIO; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return ret; + + bus_bitmask_start = 0; + if (dev && up) + /* prepare to start this bus as well */ + bus_bitmask_start |= (1 << priv->index); + /* bring netdevs down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + + if (dev != netdev) + netif_stop_queue(netdev); + + if (netif_running(netdev)) { + if (dev != netdev) + bus_bitmask_start |= (1 << j); + priv->tx.pending = 0; + priv->tx.echo_put = 0; + priv->tx.echo_get = 0; + /* + * this bus' may just have called open_candev() + * which is rather stupid to call close_candev() + * already + * but we may come here from busoff recovery too + * in which case the echo_skb _needs_ flushing too. + * just be sure to call open_candev() again + */ + close_candev(netdev); + } + priv->can.state = CAN_STATE_STOPPED; + } + card->tx.pending = 0; + + softing_enable_irq(card, 0); + ret = softing_reset_chip(card); + if (ret) + goto failed; + if (!bus_bitmask_start) + /* no busses to be brought up */ + goto card_done; + + if ((bus_bitmask_start & 1) && (bus_bitmask_start & 2) + && (softing_error_reporting(card->net[0]) + != softing_error_reporting(card->net[1]))) { + dev_alert(&card->pdev->dev, + "err_reporting flag differs for busses\n"); + goto invalid; + } + error_reporting = 0; + if (bus_bitmask_start & 1) { + netdev = card->net[0]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip 1 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 1, "initialize_chip[0]"); + if (ret < 0) + goto failed; + /* set mode */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 3, "set_mode[0]"); + if (ret < 0) + goto failed; + /* set filter */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 7, "set_filter[0]"); + if (ret < 0) + goto failed; + /* set output control */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 5, "set_output[0]"); + if (ret < 0) + goto failed; + } + if (bus_bitmask_start & 2) { + netdev = card->net[1]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip2 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 2, "initialize_chip[1]"); + if (ret < 0) + goto failed; + /* set mode2 */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 4, "set_mode[1]"); + if (ret < 0) + goto failed; + /* set filter2 */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 8, "set_filter[1]"); + if (ret < 0) + goto failed; + /* set output control2 */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 6, "set_output[1]"); + if (ret < 0) + goto failed; + } + /* enable_error_frame */ + /* + * Error reporting is switched off at the moment since + * the receiving of them is not yet 100% verified + * This should be enabled sooner or later + * + if (error_reporting) { + ret = softing_fct_cmd(card, 51, "enable_error_frame"); + if (ret < 0) + goto failed; + } + */ + /* initialize interface */ + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 12]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 14]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 16]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 18]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 20]); + ret = softing_fct_cmd(card, 17, "initialize_interface"); + if (ret < 0) + goto failed; + /* enable_fifo */ + ret = softing_fct_cmd(card, 36, "enable_fifo"); + if (ret < 0) + goto failed; + /* enable fifo tx ack */ + ret = softing_fct_cmd(card, 13, "fifo_tx_ack[0]"); + if (ret < 0) + goto failed; + /* enable fifo tx ack2 */ + ret = softing_fct_cmd(card, 14, "fifo_tx_ack[1]"); + if (ret < 0) + goto failed; + /* start_chip */ + ret = softing_fct_cmd(card, 11, "start_chip"); + if (ret < 0) + goto failed; + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE]); + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE2]); + if (card->pdat->generation < 2) { + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + /* flush the DPRAM caches */ + wmb(); + } + + softing_initialize_timestamp(card); + + /* + * do socketcan notifications/status changes + * from here, no errors should occur, or the failed: part + * must be reviewed + */ + memset(&msg, 0, sizeof(msg)); + msg.can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED; + msg.can_dlc = CAN_ERR_DLC; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!(bus_bitmask_start & (1 << j))) + continue; + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + open_candev(netdev); + if (dev != netdev) { + /* notify other busses on the restart */ + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + ++priv->can.can_stats.restarts; + } + netif_wake_queue(netdev); + } + + /* enable interrupts */ + ret = softing_enable_irq(card, 1); + if (ret) + goto failed; +card_done: + mutex_unlock(&card->fw.lock); + return 0; +invalid: + ret = -EINVAL; +failed: + softing_enable_irq(card, 0); + softing_reset_chip(card); + mutex_unlock(&card->fw.lock); + /* bring all other interfaces down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + dev_close(netdev); + } + return ret; +} + +int softing_default_output(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + struct softing *card = priv->card; + + switch (priv->chip) { + case 1000: + return (card->pdat->generation < 2) ? 0xfb : 0xfa; + case 5: + return 0x60; + default: + return 0x40; + } +} diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c new file mode 100644 index 000000000000..5157e15e96eb --- /dev/null +++ b/drivers/net/can/softing/softing_main.c @@ -0,0 +1,893 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include + +#include "softing.h" + +#define TX_ECHO_SKB_MAX (((TXMAX+1)/2)-1) + +/* + * test is a specific CAN netdev + * is online (ie. up 'n running, not sleeping, not busoff + */ +static inline int canif_is_active(struct net_device *netdev) +{ + struct can_priv *can = netdev_priv(netdev); + + if (!netif_running(netdev)) + return 0; + return (can->state <= CAN_STATE_ERROR_PASSIVE); +} + +/* reset DPRAM */ +static inline void softing_set_reset_dpram(struct softing *card) +{ + if (card->pdat->generation >= 2) { + spin_lock_bh(&card->spin); + iowrite8(ioread8(&card->dpram[DPRAM_V2_RESET]) & ~1, + &card->dpram[DPRAM_V2_RESET]); + spin_unlock_bh(&card->spin); + } +} + +static inline void softing_clr_reset_dpram(struct softing *card) +{ + if (card->pdat->generation >= 2) { + spin_lock_bh(&card->spin); + iowrite8(ioread8(&card->dpram[DPRAM_V2_RESET]) | 1, + &card->dpram[DPRAM_V2_RESET]); + spin_unlock_bh(&card->spin); + } +} + +/* trigger the tx queue-ing */ +static netdev_tx_t softing_netdev_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct softing_priv *priv = netdev_priv(dev); + struct softing *card = priv->card; + int ret; + uint8_t *ptr; + uint8_t fifo_wr, fifo_rd; + struct can_frame *cf = (struct can_frame *)skb->data; + uint8_t buf[DPRAM_TX_SIZE]; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + spin_lock(&card->spin); + + ret = NETDEV_TX_BUSY; + if (!card->fw.up || + (card->tx.pending >= TXMAX) || + (priv->tx.pending >= TX_ECHO_SKB_MAX)) + goto xmit_done; + fifo_wr = ioread8(&card->dpram[DPRAM_TX_WR]); + fifo_rd = ioread8(&card->dpram[DPRAM_TX_RD]); + if (fifo_wr == fifo_rd) + /* fifo full */ + goto xmit_done; + memset(buf, 0, sizeof(buf)); + ptr = buf; + *ptr = CMD_TX; + if (cf->can_id & CAN_RTR_FLAG) + *ptr |= CMD_RTR; + if (cf->can_id & CAN_EFF_FLAG) + *ptr |= CMD_XTD; + if (priv->index) + *ptr |= CMD_BUS2; + ++ptr; + *ptr++ = cf->can_dlc; + *ptr++ = (cf->can_id >> 0); + *ptr++ = (cf->can_id >> 8); + if (cf->can_id & CAN_EFF_FLAG) { + *ptr++ = (cf->can_id >> 16); + *ptr++ = (cf->can_id >> 24); + } else { + /* increment 1, not 2 as you might think */ + ptr += 1; + } + if (!(cf->can_id & CAN_RTR_FLAG)) + memcpy(ptr, &cf->data[0], cf->can_dlc); + memcpy_toio(&card->dpram[DPRAM_TX + DPRAM_TX_SIZE * fifo_wr], + buf, DPRAM_TX_SIZE); + if (++fifo_wr >= DPRAM_TX_CNT) + fifo_wr = 0; + iowrite8(fifo_wr, &card->dpram[DPRAM_TX_WR]); + card->tx.last_bus = priv->index; + ++card->tx.pending; + ++priv->tx.pending; + can_put_echo_skb(skb, dev, priv->tx.echo_put); + ++priv->tx.echo_put; + if (priv->tx.echo_put >= TX_ECHO_SKB_MAX) + priv->tx.echo_put = 0; + /* can_put_echo_skb() saves the skb, safe to return TX_OK */ + ret = NETDEV_TX_OK; +xmit_done: + spin_unlock(&card->spin); + if (card->tx.pending >= TXMAX) { + int j; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->net[j]) + netif_stop_queue(card->net[j]); + } + } + if (ret != NETDEV_TX_OK) + netif_stop_queue(dev); + + return ret; +} + +/* + * shortcut for skb delivery + */ +int softing_netdev_rx(struct net_device *netdev, const struct can_frame *msg, + ktime_t ktime) +{ + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_skb(netdev, &cf); + if (!skb) + return -ENOMEM; + memcpy(cf, msg, sizeof(*msg)); + skb->tstamp = ktime; + return netif_rx(skb); +} + +/* + * softing_handle_1 + * pop 1 entry from the DPRAM queue, and process + */ +static int softing_handle_1(struct softing *card) +{ + struct net_device *netdev; + struct softing_priv *priv; + ktime_t ktime; + struct can_frame msg; + int cnt = 0, lost_msg; + uint8_t fifo_rd, fifo_wr, cmd; + uint8_t *ptr; + uint32_t tmp_u32; + uint8_t buf[DPRAM_RX_SIZE]; + + memset(&msg, 0, sizeof(msg)); + /* test for lost msgs */ + lost_msg = ioread8(&card->dpram[DPRAM_RX_LOST]); + if (lost_msg) { + int j; + /* reset condition */ + iowrite8(0, &card->dpram[DPRAM_RX_LOST]); + /* prepare msg */ + msg.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL; + msg.can_dlc = CAN_ERR_DLC; + msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + /* + * service to all busses, we don't know which it was applicable + * but only service busses that are online + */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + if (!canif_is_active(netdev)) + /* a dead bus has no overflows */ + continue; + ++netdev->stats.rx_over_errors; + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + } + /* prepare for other use */ + memset(&msg, 0, sizeof(msg)); + ++cnt; + } + + fifo_rd = ioread8(&card->dpram[DPRAM_RX_RD]); + fifo_wr = ioread8(&card->dpram[DPRAM_RX_WR]); + + if (++fifo_rd >= DPRAM_RX_CNT) + fifo_rd = 0; + if (fifo_wr == fifo_rd) + return cnt; + + memcpy_fromio(buf, &card->dpram[DPRAM_RX + DPRAM_RX_SIZE*fifo_rd], + DPRAM_RX_SIZE); + mb(); + /* trigger dual port RAM */ + iowrite8(fifo_rd, &card->dpram[DPRAM_RX_RD]); + + ptr = buf; + cmd = *ptr++; + if (cmd == 0xff) + /* not quite usefull, probably the card has got out */ + return 0; + netdev = card->net[0]; + if (cmd & CMD_BUS2) + netdev = card->net[1]; + priv = netdev_priv(netdev); + + if (cmd & CMD_ERR) { + uint8_t can_state, state; + + state = *ptr++; + + msg.can_id = CAN_ERR_FLAG; + msg.can_dlc = CAN_ERR_DLC; + + if (state & SF_MASK_BUSOFF) { + can_state = CAN_STATE_BUS_OFF; + msg.can_id |= CAN_ERR_BUSOFF; + state = STATE_BUSOFF; + } else if (state & SF_MASK_EPASSIVE) { + can_state = CAN_STATE_ERROR_PASSIVE; + msg.can_id |= CAN_ERR_CRTL; + msg.data[1] = CAN_ERR_CRTL_TX_PASSIVE; + state = STATE_EPASSIVE; + } else { + can_state = CAN_STATE_ERROR_ACTIVE; + msg.can_id |= CAN_ERR_CRTL; + state = STATE_EACTIVE; + } + /* update DPRAM */ + iowrite8(state, &card->dpram[priv->index ? + DPRAM_INFO_BUSSTATE2 : DPRAM_INFO_BUSSTATE]); + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + + ++netdev->stats.rx_errors; + /* update internal status */ + if (can_state != priv->can.state) { + priv->can.state = can_state; + if (can_state == CAN_STATE_ERROR_PASSIVE) + ++priv->can.can_stats.error_passive; + else if (can_state == CAN_STATE_BUS_OFF) { + /* this calls can_close_cleanup() */ + can_bus_off(netdev); + netif_stop_queue(netdev); + } + /* trigger socketcan */ + softing_netdev_rx(netdev, &msg, ktime); + } + + } else { + if (cmd & CMD_RTR) + msg.can_id |= CAN_RTR_FLAG; + msg.can_dlc = get_can_dlc(*ptr++); + if (cmd & CMD_XTD) { + msg.can_id |= CAN_EFF_FLAG; + msg.can_id |= le32_to_cpup((void *)ptr); + ptr += 4; + } else { + msg.can_id |= le16_to_cpup((void *)ptr); + ptr += 2; + } + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + if (!(msg.can_id & CAN_RTR_FLAG)) + memcpy(&msg.data[0], ptr, 8); + ptr += 8; + /* update socket */ + if (cmd & CMD_ACK) { + /* acknowledge, was tx msg */ + struct sk_buff *skb; + skb = priv->can.echo_skb[priv->tx.echo_get]; + if (skb) + skb->tstamp = ktime; + can_get_echo_skb(netdev, priv->tx.echo_get); + ++priv->tx.echo_get; + if (priv->tx.echo_get >= TX_ECHO_SKB_MAX) + priv->tx.echo_get = 0; + if (priv->tx.pending) + --priv->tx.pending; + if (card->tx.pending) + --card->tx.pending; + ++netdev->stats.tx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.tx_bytes += msg.can_dlc; + } else { + int ret; + + ret = softing_netdev_rx(netdev, &msg, ktime); + if (ret == NET_RX_SUCCESS) { + ++netdev->stats.rx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.rx_bytes += msg.can_dlc; + } else { + ++netdev->stats.rx_dropped; + } + } + } + ++cnt; + return cnt; +} + +/* + * real interrupt handler + */ +static irqreturn_t softing_irq_thread(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + struct net_device *netdev; + struct softing_priv *priv; + int j, offset, work_done; + + work_done = 0; + spin_lock_bh(&card->spin); + while (softing_handle_1(card) > 0) { + ++card->irq.svc_count; + ++work_done; + } + spin_unlock_bh(&card->spin); + /* resume tx queue's */ + offset = card->tx.last_bus; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->tx.pending >= TXMAX) + break; + netdev = card->net[(j + offset + 1) % card->pdat->nbus]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + if (!canif_is_active(netdev)) + /* it makes no sense to wake dead busses */ + continue; + if (priv->tx.pending >= TX_ECHO_SKB_MAX) + continue; + ++work_done; + netif_wake_queue(netdev); + } + return work_done ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * interrupt routines: + * schedule the 'real interrupt handler' + */ +static irqreturn_t softing_irq_v2(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_V2_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + return (1 == ir) ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +static irqreturn_t softing_irq_v1(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_IRQ_TOHOST]); + return ir ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +/* + * netdev/candev inter-operability + */ +static int softing_netdev_open(struct net_device *ndev) +{ + int ret; + + /* check or determine and set bittime */ + ret = open_candev(ndev); + if (!ret) + ret = softing_startstop(ndev, 1); + return ret; +} + +static int softing_netdev_stop(struct net_device *ndev) +{ + int ret; + + netif_stop_queue(ndev); + + /* softing cycle does close_candev() */ + ret = softing_startstop(ndev, 0); + return ret; +} + +static int softing_candev_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + /* softing_startstop does close_candev() */ + ret = softing_startstop(ndev, 1); + return ret; + case CAN_MODE_STOP: + case CAN_MODE_SLEEP: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Softing device management helpers + */ +int softing_enable_irq(struct softing *card, int enable) +{ + int ret; + + if (!card->irq.nr) { + return 0; + } else if (card->irq.requested && !enable) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } else if (!card->irq.requested && enable) { + ret = request_threaded_irq(card->irq.nr, + (card->pdat->generation >= 2) ? + softing_irq_v2 : softing_irq_v1, + softing_irq_thread, IRQF_SHARED, + dev_name(&card->pdev->dev), card); + if (ret) { + dev_alert(&card->pdev->dev, + "request_threaded_irq(%u) failed\n", + card->irq.nr); + return ret; + } + card->irq.requested = 1; + } + return 0; +} + +static void softing_card_shutdown(struct softing *card) +{ + int fw_up = 0; + + if (mutex_lock_interruptible(&card->fw.lock)) + /* return -ERESTARTSYS */; + fw_up = card->fw.up; + card->fw.up = 0; + + if (card->irq.requested && card->irq.nr) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } + if (fw_up) { + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + } + mutex_unlock(&card->fw.lock); +} + +static __devinit int softing_card_boot(struct softing *card) +{ + int ret, j; + static const uint8_t stream[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }; + unsigned char back[sizeof(stream)]; + + if (mutex_lock_interruptible(&card->fw.lock)) + return -ERESTARTSYS; + if (card->fw.up) { + mutex_unlock(&card->fw.lock); + return 0; + } + /* reset board */ + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 1); + /* boot card */ + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + for (j = 0; (j + sizeof(stream)) < card->dpram_size; + j += sizeof(stream)) { + + memcpy_toio(&card->dpram[j], stream, sizeof(stream)); + /* flush IO cache */ + mb(); + memcpy_fromio(back, &card->dpram[j], sizeof(stream)); + + if (!memcmp(back, stream, sizeof(stream))) + continue; + /* memory is not equal */ + dev_alert(&card->pdev->dev, "dpram failed at 0x%04x\n", j); + ret = -EIO; + goto failed; + } + wmb(); + /* load boot firmware */ + ret = softing_load_fw(card->pdat->boot.fw, card, card->dpram, + card->dpram_size, + card->pdat->boot.offs - card->pdat->boot.addr); + if (ret < 0) + goto failed; + /* load loader firmware */ + ret = softing_load_fw(card->pdat->load.fw, card, card->dpram, + card->dpram_size, + card->pdat->load.offs - card->pdat->load.addr); + if (ret < 0) + goto failed; + + if (card->pdat->reset) + card->pdat->reset(card->pdev, 0); + softing_clr_reset_dpram(card); + ret = softing_bootloader_command(card, 0, "card boot"); + if (ret < 0) + goto failed; + ret = softing_load_app_fw(card->pdat->app.fw, card); + if (ret < 0) + goto failed; + + ret = softing_chip_poweron(card); + if (ret < 0) + goto failed; + + card->fw.up = 1; + mutex_unlock(&card->fw.lock); + return 0; +failed: + card->fw.up = 0; + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + mutex_unlock(&card->fw.lock); + return ret; +} + +/* + * netdev sysfs + */ +static ssize_t show_channel(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "%i\n", priv->index); +} + +static ssize_t show_chip(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "%i\n", priv->chip); +} + +static ssize_t show_output(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "0x%02x\n", priv->output); +} + +static ssize_t store_output(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + struct softing *card = priv->card; + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + val &= 0xFF; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return -ERESTARTSYS; + if (netif_running(ndev)) { + mutex_unlock(&card->fw.lock); + return -EBUSY; + } + priv->output = val; + mutex_unlock(&card->fw.lock); + return count; +} + +static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); +static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL); +static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output); + +static const struct attribute *const netdev_sysfs_attrs[] = { + &dev_attr_channel.attr, + &dev_attr_chip.attr, + &dev_attr_output.attr, + NULL, +}; +static const struct attribute_group netdev_sysfs_group = { + .name = NULL, + .attrs = (struct attribute **)netdev_sysfs_attrs, +}; + +static const struct net_device_ops softing_netdev_ops = { + .ndo_open = softing_netdev_open, + .ndo_stop = softing_netdev_stop, + .ndo_start_xmit = softing_netdev_start_xmit, +}; + +static const struct can_bittiming_const softing_btr_const = { + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, /* overruled */ + .brp_min = 1, + .brp_max = 32, /* overruled */ + .brp_inc = 1, +}; + + +static __devinit struct net_device *softing_netdev_create(struct softing *card, + uint16_t chip_id) +{ + struct net_device *netdev; + struct softing_priv *priv; + + netdev = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX); + if (!netdev) { + dev_alert(&card->pdev->dev, "alloc_candev failed\n"); + return NULL; + } + priv = netdev_priv(netdev); + priv->netdev = netdev; + priv->card = card; + memcpy(&priv->btr_const, &softing_btr_const, sizeof(priv->btr_const)); + priv->btr_const.brp_max = card->pdat->max_brp; + priv->btr_const.sjw_max = card->pdat->max_sjw; + priv->can.bittiming_const = &priv->btr_const; + priv->can.clock.freq = 8000000; + priv->chip = chip_id; + priv->output = softing_default_output(netdev); + SET_NETDEV_DEV(netdev, &card->pdev->dev); + + netdev->flags |= IFF_ECHO; + netdev->netdev_ops = &softing_netdev_ops; + priv->can.do_set_mode = softing_candev_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + return netdev; +} + +static __devinit int softing_netdev_register(struct net_device *netdev) +{ + int ret; + + netdev->sysfs_groups[0] = &netdev_sysfs_group; + ret = register_candev(netdev); + if (ret) { + dev_alert(&netdev->dev, "register failed\n"); + return ret; + } + return 0; +} + +static void softing_netdev_cleanup(struct net_device *netdev) +{ + unregister_candev(netdev); + free_candev(netdev); +} + +/* + * sysfs for Platform device + */ +#define DEV_ATTR_RO(name, member) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + return sprintf(buf, "%u\n", card->member); \ +} \ +static DEVICE_ATTR(name, 0444, show_##name, NULL) + +#define DEV_ATTR_RO_STR(name, member) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + return sprintf(buf, "%s\n", card->member); \ +} \ +static DEVICE_ATTR(name, 0444, show_##name, NULL) + +DEV_ATTR_RO(serial, id.serial); +DEV_ATTR_RO_STR(firmware, pdat->app.fw); +DEV_ATTR_RO(firmware_version, id.fw_version); +DEV_ATTR_RO_STR(hardware, pdat->name); +DEV_ATTR_RO(hardware_version, id.hw_version); +DEV_ATTR_RO(license, id.license); +DEV_ATTR_RO(frequency, id.freq); +DEV_ATTR_RO(txpending, tx.pending); + +static struct attribute *softing_pdev_attrs[] = { + &dev_attr_serial.attr, + &dev_attr_firmware.attr, + &dev_attr_firmware_version.attr, + &dev_attr_hardware.attr, + &dev_attr_hardware_version.attr, + &dev_attr_license.attr, + &dev_attr_frequency.attr, + &dev_attr_txpending.attr, + NULL, +}; + +static const struct attribute_group softing_pdev_group = { + .name = NULL, + .attrs = softing_pdev_attrs, +}; + +/* + * platform driver + */ +static __devexit int softing_pdev_remove(struct platform_device *pdev) +{ + struct softing *card = platform_get_drvdata(pdev); + int j; + + /* first, disable card*/ + softing_card_shutdown(card); + + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!card->net[j]) + continue; + softing_netdev_cleanup(card->net[j]); + card->net[j] = NULL; + } + sysfs_remove_group(&pdev->dev.kobj, &softing_pdev_group); + + iounmap(card->dpram); + kfree(card); + return 0; +} + +static __devinit int softing_pdev_probe(struct platform_device *pdev) +{ + const struct softing_platform_data *pdat = pdev->dev.platform_data; + struct softing *card; + struct net_device *netdev; + struct softing_priv *priv; + struct resource *pres; + int ret; + int j; + + if (!pdat) { + dev_warn(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + if (pdat->nbus > ARRAY_SIZE(card->net)) { + dev_warn(&pdev->dev, "%u nets??\n", pdat->nbus); + return -EINVAL; + } + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + card->pdat = pdat; + card->pdev = pdev; + platform_set_drvdata(pdev, card); + mutex_init(&card->fw.lock); + spin_lock_init(&card->spin); + + ret = -EINVAL; + pres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pres) + goto platform_resource_failed;; + card->dpram_phys = pres->start; + card->dpram_size = pres->end - pres->start + 1; + card->dpram = ioremap_nocache(card->dpram_phys, card->dpram_size); + if (!card->dpram) { + dev_alert(&card->pdev->dev, "dpram ioremap failed\n"); + goto ioremap_failed; + } + + pres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (pres) + card->irq.nr = pres->start; + + /* reset card */ + ret = softing_card_boot(card); + if (ret < 0) { + dev_alert(&pdev->dev, "failed to boot\n"); + goto boot_failed; + } + + /* only now, the chip's are known */ + card->id.freq = card->pdat->freq; + + ret = sysfs_create_group(&pdev->dev.kobj, &softing_pdev_group); + if (ret < 0) { + dev_alert(&card->pdev->dev, "sysfs failed\n"); + goto sysfs_failed; + } + + ret = -ENOMEM; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + card->net[j] = netdev = + softing_netdev_create(card, card->id.chip[j]); + if (!netdev) { + dev_alert(&pdev->dev, "failed to make can[%i]", j); + goto netdev_failed; + } + priv = netdev_priv(card->net[j]); + priv->index = j; + ret = softing_netdev_register(netdev); + if (ret) { + free_candev(netdev); + card->net[j] = NULL; + dev_alert(&card->pdev->dev, + "failed to register can[%i]\n", j); + goto netdev_failed; + } + } + dev_info(&card->pdev->dev, "%s ready.\n", card->pdat->name); + return 0; + +netdev_failed: + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!card->net[j]) + continue; + softing_netdev_cleanup(card->net[j]); + } + sysfs_remove_group(&pdev->dev.kobj, &softing_pdev_group); +sysfs_failed: + softing_card_shutdown(card); +boot_failed: + iounmap(card->dpram); +ioremap_failed: +platform_resource_failed: + kfree(card); + return ret; +} + +static struct platform_driver softing_driver = { + .driver = { + .name = "softing", + .owner = THIS_MODULE, + }, + .probe = softing_pdev_probe, + .remove = __devexit_p(softing_pdev_remove), +}; + +MODULE_ALIAS("platform:softing"); + +static int __init softing_start(void) +{ + return platform_driver_register(&softing_driver); +} + +static void __exit softing_stop(void) +{ + platform_driver_unregister(&softing_driver); +} + +module_init(softing_start); +module_exit(softing_stop); + +MODULE_DESCRIPTION("Softing DPRAM CAN driver"); +MODULE_AUTHOR("Kurt Van Dijck "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/softing/softing_platform.h b/drivers/net/can/softing/softing_platform.h new file mode 100644 index 000000000000..ebbf69815623 --- /dev/null +++ b/drivers/net/can/softing/softing_platform.h @@ -0,0 +1,40 @@ + +#include + +#ifndef _SOFTING_DEVICE_H_ +#define _SOFTING_DEVICE_H_ + +/* softing firmware directory prefix */ +#define fw_dir "softing-4.6/" + +struct softing_platform_data { + unsigned int manf; + unsigned int prod; + /* + * generation + * 1st with NEC or SJA1000 + * 8bit, exclusive interrupt, ... + * 2nd only SJA1000 + * 16bit, shared interrupt + */ + int generation; + int nbus; /* # busses on device */ + unsigned int freq; /* operating frequency in Hz */ + unsigned int max_brp; + unsigned int max_sjw; + unsigned long dpram_size; + const char *name; + struct { + unsigned long offs; + unsigned long addr; + const char *fw; + } boot, load, app; + /* + * reset() function + * bring pdev in or out of reset, depending on value + */ + int (*reset)(struct platform_device *pdev, int value); + int (*enable_irq)(struct platform_device *pdev, int value); +}; + +#endif -- cgit v1.2.3 From 0a0b7a5f7a043d86a95990d3227cf7e823ae52ac Mon Sep 17 00:00:00 2001 From: Kurt Van Dijck Date: Tue, 11 Jan 2011 04:34:28 +0000 Subject: can: add driver for Softing card This patch adds the driver that creates a platform:softing device from a pcmcia_device Note: the Kconfig indicates a dependency on the softing.ko driver, but this is purely to make configuration intuitive. This driver will work independent, but no CAN network devices appear until softing.ko is loaded too. Signed-off-by: Kurt Van Dijck Acked-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/softing/Kconfig | 14 ++ drivers/net/can/softing/Makefile | 1 + drivers/net/can/softing/softing_cs.c | 359 +++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 drivers/net/can/softing/softing_cs.c diff --git a/drivers/net/can/softing/Kconfig b/drivers/net/can/softing/Kconfig index 072f337f42e3..92bd6bdde5e3 100644 --- a/drivers/net/can/softing/Kconfig +++ b/drivers/net/can/softing/Kconfig @@ -14,3 +14,17 @@ config CAN_SOFTING controls the 2 busses on the card together. As such, some actions (start/stop/busoff recovery) on 1 bus must bring down the other bus too temporarily. + +config CAN_SOFTING_CS + tristate "Softing Gmbh CAN pcmcia cards" + depends on PCMCIA + select CAN_SOFTING + ---help--- + Support for PCMCIA cards from Softing Gmbh & some cards + from Vector Gmbh. + You need firmware for these, which you can get at + http://developer.berlios.de/projects/socketcan/ + This version of the driver is written against + firmware version 4.6 (softing-fw-4.6-binaries.tar.gz) + In order to use the card as CAN device, you need the Softing generic + support too. diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile index 7db04452deb6..c5e5016c742e 100644 --- a/drivers/net/can/softing/Makefile +++ b/drivers/net/can/softing/Makefile @@ -1,5 +1,6 @@ softing-y := softing_main.o softing_fw.o obj-$(CONFIG_CAN_SOFTING) += softing.o +obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c new file mode 100644 index 000000000000..300fe75dd1a7 --- /dev/null +++ b/drivers/net/can/softing/softing_cs.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include + +#include "softing_platform.h" + +static int softingcs_index; +static spinlock_t softingcs_index_lock; + +static int softingcs_reset(struct platform_device *pdev, int v); +static int softingcs_enable_irq(struct platform_device *pdev, int v); + +/* + * platform_data descriptions + */ +#define MHZ (1000*1000) +static const struct softing_platform_data softingcs_platform_data[] = { +{ + .name = "CANcard", + .manf = 0x0168, .prod = 0x001, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-NEC", + .manf = 0x0168, .prod = 0x002, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-SJA", + .manf = 0x0168, .prod = 0x004, + .generation = 1, + .nbus = 2, + .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-2", + .manf = 0x0168, .prod = 0x005, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + .name = "Vector-CANcard", + .manf = 0x0168, .prod = 0x081, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "Vector-CANcard-SJA", + .manf = 0x0168, .prod = 0x084, + .generation = 1, + .nbus = 2, + .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "Vector-CANcard-2", + .manf = 0x0168, .prod = 0x085, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + .name = "EDICcard-NEC", + .manf = 0x0168, .prod = 0x102, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "EDICcard-2", + .manf = 0x0168, .prod = 0x105, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + 0, 0, +}, +}; + +MODULE_FIRMWARE(fw_dir "bcard.bin"); +MODULE_FIRMWARE(fw_dir "ldcard.bin"); +MODULE_FIRMWARE(fw_dir "cancard.bin"); +MODULE_FIRMWARE(fw_dir "cansja.bin"); + +MODULE_FIRMWARE(fw_dir "bcard2.bin"); +MODULE_FIRMWARE(fw_dir "ldcard2.bin"); +MODULE_FIRMWARE(fw_dir "cancrd2.bin"); + +static __devinit const struct softing_platform_data +*softingcs_find_platform_data(unsigned int manf, unsigned int prod) +{ + const struct softing_platform_data *lp; + + for (lp = softingcs_platform_data; lp->manf; ++lp) { + if ((lp->manf == manf) && (lp->prod == prod)) + return lp; + } + return NULL; +} + +/* + * platformdata callbacks + */ +static int softingcs_reset(struct platform_device *pdev, int v) +{ + struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); + + dev_dbg(&pdev->dev, "pcmcia config [2] %02x\n", v ? 0 : 0x20); + return pcmcia_write_config_byte(pcmcia, 2, v ? 0 : 0x20); +} + +static int softingcs_enable_irq(struct platform_device *pdev, int v) +{ + struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); + + dev_dbg(&pdev->dev, "pcmcia config [0] %02x\n", v ? 0x60 : 0); + return pcmcia_write_config_byte(pcmcia, 0, v ? 0x60 : 0); +} + +/* + * pcmcia check + */ +static __devinit int softingcs_probe_config(struct pcmcia_device *pcmcia, + void *priv_data) +{ + struct softing_platform_data *pdat = priv_data; + struct resource *pres; + int memspeed = 0; + + WARN_ON(!pdat); + pres = pcmcia->resource[PCMCIA_IOMEM_0]; + if (resource_size(pres) < 0x1000) + return -ERANGE; + + pres->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE; + if (pdat->generation < 2) { + pres->flags |= WIN_USE_WAIT | WIN_DATA_WIDTH_8; + memspeed = 3; + } else { + pres->flags |= WIN_DATA_WIDTH_16; + } + return pcmcia_request_window(pcmcia, pres, memspeed); +} + +static __devexit void softingcs_remove(struct pcmcia_device *pcmcia) +{ + struct platform_device *pdev = pcmcia->priv; + + /* free bits */ + platform_device_unregister(pdev); + /* release pcmcia stuff */ + pcmcia_disable_device(pcmcia); +} + +/* + * platform_device wrapper + * pdev->resource has 2 entries: io & irq + */ +static void softingcs_pdev_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + kfree(pdev); +} + +static __devinit int softingcs_probe(struct pcmcia_device *pcmcia) +{ + int ret; + struct platform_device *pdev; + const struct softing_platform_data *pdat; + struct resource *pres; + struct dev { + struct platform_device pdev; + struct resource res[2]; + } *dev; + + /* find matching platform_data */ + pdat = softingcs_find_platform_data(pcmcia->manf_id, pcmcia->card_id); + if (!pdat) + return -ENOTTY; + + /* setup pcmcia device */ + pcmcia->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IOMEM | + CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + ret = pcmcia_loop_config(pcmcia, softingcs_probe_config, (void *)pdat); + if (ret) + goto pcmcia_failed; + + ret = pcmcia_enable_device(pcmcia); + if (ret < 0) + goto pcmcia_failed; + + pres = pcmcia->resource[PCMCIA_IOMEM_0]; + if (!pres) { + ret = -EBADF; + goto pcmcia_bad; + } + + /* create softing platform device */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto mem_failed; + } + dev->pdev.resource = dev->res; + dev->pdev.num_resources = ARRAY_SIZE(dev->res); + dev->pdev.dev.release = softingcs_pdev_release; + + pdev = &dev->pdev; + pdev->dev.platform_data = (void *)pdat; + pdev->dev.parent = &pcmcia->dev; + pcmcia->priv = pdev; + + /* platform device resources */ + pdev->resource[0].flags = IORESOURCE_MEM; + pdev->resource[0].start = pres->start; + pdev->resource[0].end = pres->end; + + pdev->resource[1].flags = IORESOURCE_IRQ; + pdev->resource[1].start = pcmcia->irq; + pdev->resource[1].end = pdev->resource[1].start; + + /* platform device setup */ + spin_lock(&softingcs_index_lock); + pdev->id = softingcs_index++; + spin_unlock(&softingcs_index_lock); + pdev->name = "softing"; + dev_set_name(&pdev->dev, "softingcs.%i", pdev->id); + ret = platform_device_register(pdev); + if (ret < 0) + goto platform_failed; + + dev_info(&pcmcia->dev, "created %s\n", dev_name(&pdev->dev)); + return 0; + +platform_failed: + kfree(dev); +mem_failed: +pcmcia_bad: +pcmcia_failed: + pcmcia_disable_device(pcmcia); + pcmcia->priv = NULL; + return ret ?: -ENODEV; +} + +static /*const*/ struct pcmcia_device_id softingcs_ids[] = { + /* softing */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005), + /* vector, manufacturer? */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085), + /* EDIC */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, softingcs_ids); + +static struct pcmcia_driver softingcs_driver = { + .owner = THIS_MODULE, + .name = "softingcs", + .id_table = softingcs_ids, + .probe = softingcs_probe, + .remove = __devexit_p(softingcs_remove), +}; + +static int __init softingcs_start(void) +{ + spin_lock_init(&softingcs_index_lock); + return pcmcia_register_driver(&softingcs_driver); +} + +static void __exit softingcs_stop(void) +{ + pcmcia_unregister_driver(&softingcs_driver); +} + +module_init(softingcs_start); +module_exit(softingcs_stop); + +MODULE_DESCRIPTION("softing CANcard driver" + ", links PCMCIA card to softing driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 57b84e53171ce672683faf1cab2e660965a6bdaf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 22 Jan 2011 23:14:20 -0200 Subject: perf test: Fix build on older glibcs Where we don't have CPU_ALLOC & friends. As the tools are being used in older distros where the only allowed change are to replace the kernel, like RHEL4 and 5. Reported-by: Eric Dumazet Cc: Eric Dumazet Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Paul Mackerras Cc: Stephane Eranian LKML-Reference: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-test.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 02958d6a807f..0e01834a48d0 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -332,8 +332,7 @@ static int test__open_syscall_event_on_all_cpus(void) struct perf_evsel *evsel; struct perf_event_attr attr; unsigned int nr_open_calls = 111, i; - cpu_set_t *cpu_set; - size_t cpu_set_size; + cpu_set_t cpu_set; int id = trace_event__id("sys_enter_open"); if (id < 0) { @@ -353,13 +352,8 @@ static int test__open_syscall_event_on_all_cpus(void) return -1; } - cpu_set = CPU_ALLOC(cpus->nr); - if (cpu_set == NULL) - goto out_thread_map_delete; - - cpu_set_size = CPU_ALLOC_SIZE(cpus->nr); - CPU_ZERO_S(cpu_set_size, cpu_set); + CPU_ZERO(&cpu_set); memset(&attr, 0, sizeof(attr)); attr.type = PERF_TYPE_TRACEPOINT; @@ -367,7 +361,7 @@ static int test__open_syscall_event_on_all_cpus(void) evsel = perf_evsel__new(&attr, 0); if (evsel == NULL) { pr_debug("perf_evsel__new\n"); - goto out_cpu_free; + goto out_thread_map_delete; } if (perf_evsel__open(evsel, cpus, threads) < 0) { @@ -379,9 +373,19 @@ static int test__open_syscall_event_on_all_cpus(void) for (cpu = 0; cpu < cpus->nr; ++cpu) { unsigned int ncalls = nr_open_calls + cpu; + /* + * XXX eventually lift this restriction in a way that + * keeps perf building on older glibc installations + * without CPU_ALLOC. 1024 cpus in 2010 still seems + * a reasonable upper limit tho :-) + */ + if (cpus->map[cpu] >= CPU_SETSIZE) { + pr_debug("Ignoring CPU %d\n", cpus->map[cpu]); + continue; + } - CPU_SET_S(cpus->map[cpu], cpu_set_size, cpu_set); - if (sched_setaffinity(0, cpu_set_size, cpu_set) < 0) { + CPU_SET(cpus->map[cpu], &cpu_set); + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { pr_debug("sched_setaffinity() failed on CPU %d: %s ", cpus->map[cpu], strerror(errno)); @@ -391,7 +395,7 @@ static int test__open_syscall_event_on_all_cpus(void) fd = open("/etc/passwd", O_RDONLY); close(fd); } - CPU_CLR_S(cpus->map[cpu], cpu_set_size, cpu_set); + CPU_CLR(cpus->map[cpu], &cpu_set); } /* @@ -407,6 +411,9 @@ static int test__open_syscall_event_on_all_cpus(void) for (cpu = 0; cpu < cpus->nr; ++cpu) { unsigned int expected; + if (cpus->map[cpu] >= CPU_SETSIZE) + continue; + if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { pr_debug("perf_evsel__open_read_on_cpu\n"); goto out_close_fd; @@ -425,8 +432,6 @@ out_close_fd: perf_evsel__close_fd(evsel, 1, threads->nr); out_evsel_delete: perf_evsel__delete(evsel); -out_cpu_free: - CPU_FREE(cpu_set); out_thread_map_delete: thread_map__delete(threads); return err; -- cgit v1.2.3 From 9486aa38771661e96fbb51c549b9901b5df609d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 22 Jan 2011 20:37:02 -0200 Subject: perf tools: Fix 64 bit integer format strings Using %L[uxd] has issues in some architectures, like on ppc64. Fix it by making our 64 bit integers typedefs of stdint.h types and using PRI[ux]64 like, for instance, git does. Reported by Denis Kirjanov that provided a patch for one case, I went and changed all cases. Reported-by: Denis Kirjanov Tested-by: Denis Kirjanov LKML-Reference: <20110120093246.GA8031@hera.kernel.org> Cc: Denis Kirjanov Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Pingtian Han Cc: Stephane Eranian Cc: Tom Zanussi Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 6 +++--- tools/perf/builtin-kmem.c | 4 ++-- tools/perf/builtin-lock.c | 6 +++--- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-sched.c | 20 ++++++++++---------- tools/perf/builtin-script.c | 6 +++--- tools/perf/builtin-stat.c | 4 ++-- tools/perf/builtin-test.c | 14 +++++++------- tools/perf/builtin-top.c | 9 +++++---- tools/perf/util/event.c | 5 +++-- tools/perf/util/header.c | 4 ++-- tools/perf/util/hist.c | 17 +++++++++-------- tools/perf/util/map.c | 3 ++- tools/perf/util/parse-events.c | 2 +- tools/perf/util/probe-event.c | 2 +- tools/perf/util/session.c | 28 +++++++++++++++------------- tools/perf/util/svghelper.c | 5 +++-- tools/perf/util/symbol.c | 9 +++++---- tools/perf/util/types.h | 10 ++++++---- tools/perf/util/ui/browsers/hists.c | 2 +- tools/perf/util/ui/browsers/map.c | 5 +++-- tools/perf/util/values.c | 10 +++++----- 23 files changed, 93 insertions(+), 82 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index c056cdc06912..8879463807e4 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -212,7 +212,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename) continue; offset = start + i; - sprintf(cmd, "addr2line -e %s %016llx", filename, offset); + sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); fp = popen(cmd, "r"); if (!fp) continue; @@ -270,9 +270,9 @@ static void hist_entry__print_hits(struct hist_entry *self) for (offset = 0; offset < len; ++offset) if (h->ip[offset] != 0) - printf("%*Lx: %Lu\n", BITS_PER_LONG / 2, + printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, sym->start + offset, h->ip[offset]); - printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum); + printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); } static int hist_entry__tty_annotate(struct hist_entry *he) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index def7ddc2fd4f..d97256d65980 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -371,10 +371,10 @@ static void __print_result(struct rb_root *root, struct perf_session *session, addr = data->ptr; if (sym != NULL) - snprintf(buf, sizeof(buf), "%s+%Lx", sym->name, + snprintf(buf, sizeof(buf), "%s+%" PRIx64 "", sym->name, addr - map->unmap_ip(map, sym->start)); else - snprintf(buf, sizeof(buf), "%#Lx", addr); + snprintf(buf, sizeof(buf), "%#" PRIx64 "", addr); printf(" %-34s |", buf); printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n", diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index b9c6e5432971..2b36defc5d73 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -782,9 +782,9 @@ static void print_result(void) pr_info("%10u ", st->nr_acquired); pr_info("%10u ", st->nr_contended); - pr_info("%15llu ", st->wait_time_total); - pr_info("%15llu ", st->wait_time_max); - pr_info("%15llu ", st->wait_time_min == ULLONG_MAX ? + pr_info("%15" PRIu64 " ", st->wait_time_total); + pr_info("%15" PRIu64 " ", st->wait_time_max); + pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ? 0 : st->wait_time_min); pr_info("\n"); } diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index fcd29e8af29f..b2f729fdb317 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -817,7 +817,7 @@ static int __cmd_record(int argc, const char **argv) * Approximate RIP event size: 24 bytes. */ fprintf(stderr, - "[ perf record: Captured and wrote %.3f MB %s (~%lld samples) ]\n", + "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", (double)bytes_written / 1024.0 / 1024.0, output_name, bytes_written / 24); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 75183a4518e6..c27e31f289e6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -197,7 +197,7 @@ static int process_read_event(event_t *event, struct sample_data *sample __used, event->read.value); } - dump_printf(": %d %d %s %Lu\n", event->read.pid, event->read.tid, + dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, attr ? __event_name(attr->type, attr->config) : "FAIL", event->read.value); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 29e7ffd85690..29acb894e035 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -193,7 +193,7 @@ static void calibrate_run_measurement_overhead(void) } run_measurement_overhead = min_delta; - printf("run measurement overhead: %Ld nsecs\n", min_delta); + printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta); } static void calibrate_sleep_measurement_overhead(void) @@ -211,7 +211,7 @@ static void calibrate_sleep_measurement_overhead(void) min_delta -= 10000; sleep_measurement_overhead = min_delta; - printf("sleep measurement overhead: %Ld nsecs\n", min_delta); + printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta); } static struct sched_atom * @@ -617,13 +617,13 @@ static void test_calibrations(void) burn_nsecs(1e6); T1 = get_nsecs(); - printf("the run test took %Ld nsecs\n", T1-T0); + printf("the run test took %" PRIu64 " nsecs\n", T1 - T0); T0 = get_nsecs(); sleep_nsecs(1e6); T1 = get_nsecs(); - printf("the sleep test took %Ld nsecs\n", T1-T0); + printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0); } #define FILL_FIELD(ptr, field, event, data) \ @@ -816,10 +816,10 @@ replay_switch_event(struct trace_switch_event *switch_event, delta = 0; if (delta < 0) - die("hm, delta: %Ld < 0 ?\n", delta); + die("hm, delta: %" PRIu64 " < 0 ?\n", delta); if (verbose) { - printf(" ... switch from %s/%d to %s/%d [ran %Ld nsecs]\n", + printf(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n", switch_event->prev_comm, switch_event->prev_pid, switch_event->next_comm, switch_event->next_pid, delta); @@ -1048,7 +1048,7 @@ latency_switch_event(struct trace_switch_event *switch_event, delta = 0; if (delta < 0) - die("hm, delta: %Ld < 0 ?\n", delta); + die("hm, delta: %" PRIu64 " < 0 ?\n", delta); sched_out = perf_session__findnew(session, switch_event->prev_pid); @@ -1221,7 +1221,7 @@ static void output_lat_thread(struct work_atoms *work_list) avg = work_list->total_lat / work_list->nb_atoms; - printf("|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n", + printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n", (double)work_list->total_runtime / 1e6, work_list->nb_atoms, (double)avg / 1e6, (double)work_list->max_lat / 1e6, @@ -1423,7 +1423,7 @@ map_switch_event(struct trace_switch_event *switch_event, delta = 0; if (delta < 0) - die("hm, delta: %Ld < 0 ?\n", delta); + die("hm, delta: %" PRIu64 " < 0 ?\n", delta); sched_out = perf_session__findnew(session, switch_event->prev_pid); @@ -1713,7 +1713,7 @@ static void __cmd_lat(void) } printf(" -----------------------------------------------------------------------------------------\n"); - printf(" TOTAL: |%11.3f ms |%9Ld |\n", + printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n", (double)all_runtime/1e6, all_count); printf(" ---------------------------------------------------\n"); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 150a606002eb..b766c2a9ac97 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -77,8 +77,8 @@ static int process_sample_event(event_t *event, struct sample_data *sample, if (session->sample_type & PERF_SAMPLE_RAW) { if (debug_mode) { if (sample->time < last_timestamp) { - pr_err("Samples misordered, previous: %llu " - "this: %llu\n", last_timestamp, + pr_err("Samples misordered, previous: %" PRIu64 + " this: %" PRIu64 "\n", last_timestamp, sample->time); nr_unordered++; } @@ -126,7 +126,7 @@ static int __cmd_script(struct perf_session *session) ret = perf_session__process_events(session, &event_ops); if (debug_mode) - pr_err("Misordered timestamps: %llu\n", nr_unordered); + pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered); return ret; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0ff11d9b13be..a482a191a0ca 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -206,8 +206,8 @@ static int read_counter_aggr(struct perf_evsel *counter) update_stats(&ps->res_stats[i], count[i]); if (verbose) { - fprintf(stderr, "%s: %Ld %Ld %Ld\n", event_name(counter), - count[0], count[1], count[2]); + fprintf(stderr, "%s: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n", + event_name(counter), count[0], count[1], count[2]); } /* diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 0e01834a48d0..5dcdba653d70 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -146,7 +146,7 @@ next_pair: if (llabs(skew) < page_size) continue; - pr_debug("%#Lx: diff end addr for %s v: %#Lx k: %#Lx\n", + pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", sym->start, sym->name, sym->end, pair->end); } else { struct rb_node *nnd; @@ -168,11 +168,11 @@ detour: goto detour; } - pr_debug("%#Lx: diff name v: %s k: %s\n", + pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", sym->start, sym->name, pair->name); } } else - pr_debug("%#Lx: %s not on kallsyms\n", sym->start, sym->name); + pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); err = -1; } @@ -211,10 +211,10 @@ detour: if (pair->start == pos->start) { pair->priv = 1; - pr_info(" %Lx-%Lx %Lx %s in kallsyms as", + pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", pos->start, pos->end, pos->pgoff, pos->dso->name); if (pos->pgoff != pair->pgoff || pos->end != pair->end) - pr_info(": \n*%Lx-%Lx %Lx", + pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", pair->start, pair->end, pair->pgoff); pr_info(" %s\n", pair->dso->name); pair->priv = 1; @@ -307,7 +307,7 @@ static int test__open_syscall_event(void) } if (evsel->counts->cpu[0].val != nr_open_calls) { - pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %Ld\n", + pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", nr_open_calls, evsel->counts->cpu[0].val); goto out_close_fd; } @@ -421,7 +421,7 @@ static int test__open_syscall_event_on_all_cpus(void) expected = nr_open_calls + cpu; if (evsel->counts->cpu[cpu].val != expected) { - pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %Ld\n", + pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); goto out_close_fd; } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 05344c6210ac..b6998e055767 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,7 @@ static int parse_source(struct sym_entry *syme) len = sym->end - sym->start; sprintf(command, - "objdump --start-address=%#0*Lx --stop-address=%#0*Lx -dS %s", + "objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s", BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); @@ -308,7 +309,7 @@ static void lookup_sym_source(struct sym_entry *syme) struct source_line *line; char pattern[PATTERN_LEN + 1]; - sprintf(pattern, "%0*Lx <", BITS_PER_LONG / 4, + sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4, map__rip_2objdump(syme->map, symbol->start)); pthread_mutex_lock(&syme->src->lock); @@ -537,7 +538,7 @@ static void print_sym_table(void) if (nr_counters == 1 || !display_weighted) { struct perf_evsel *first; first = list_entry(evsel_list.next, struct perf_evsel, node); - printf("%Ld", first->attr.sample_period); + printf("%" PRIu64, (uint64_t)first->attr.sample_period); if (freq) printf("Hz "); else @@ -640,7 +641,7 @@ static void print_sym_table(void) percent_color_fprintf(stdout, "%4.1f%%", pcnt); if (verbose) - printf(" %016llx", sym->start); + printf(" %016" PRIx64, sym->start); printf(" %-*.*s", sym_width, sym_width, sym->name); printf(" %-*.*s\n", dso_width, dso_width, dso_width >= syme->map->dso->long_name_len ? diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2302ec051bb4..1478ab4ee222 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -459,7 +459,8 @@ int event__process_comm(event_t *self, struct sample_data *sample __used, int event__process_lost(event_t *self, struct sample_data *sample __used, struct perf_session *session) { - dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); + dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", + self->lost.id, self->lost.lost); session->hists.stats.total_lost += self->lost.lost; return 0; } @@ -575,7 +576,7 @@ int event__process_mmap(event_t *self, struct sample_data *sample __used, u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; int ret = 0; - dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", + dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", self->mmap.pid, self->mmap.tid, self->mmap.start, self->mmap.len, self->mmap.pgoff, self->mmap.filename); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 989fa2dee2fd..f6a929e74981 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -798,8 +798,8 @@ static int perf_file_section__process(struct perf_file_section *self, int feat, int fd) { if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { - pr_debug("Failed to lseek to %Ld offset for feature %d, " - "continuing...\n", self->offset, feat); + pr_debug("Failed to lseek to %" PRIu64 " offset for feature " + "%d, continuing...\n", self->offset, feat); return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c749ba6136a0..32f4f1f2f6e4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -636,13 +636,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, } } } else - ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); + ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); + ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); else - ret += snprintf(s + ret, size - ret, "%11lld", period); + ret += snprintf(s + ret, size - ret, "%11" PRIu64, period); } if (pair_hists) { @@ -971,7 +971,7 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) sym_size = sym->end - sym->start; offset = ip - sym->start; - pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); + pr_debug3("%s: ip=%#" PRIx64 "\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); if (offset >= sym_size) return 0; @@ -980,8 +980,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) h->sum++; h->ip[offset]++; - pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, - self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); + pr_debug3("%#" PRIx64 " %s: period++ [ip: %#" PRIx64 ", %#" PRIx64 + "] => %" PRIu64 "\n", self->ms.sym->start, self->ms.sym->name, + ip, ip - self->ms.sym->start, h->ip[offset]); return 0; } @@ -1132,7 +1133,7 @@ fallback: goto out_free_filename; } - pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, + pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, filename, sym->name, map->unmap_ip(map, sym->start), map->unmap_ip(map, sym->end)); @@ -1142,7 +1143,7 @@ fallback: dso, dso->long_name, sym, sym->name); snprintf(command, sizeof(command), - "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", + "objdump --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), symfs_filename, filename); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 3a7eb6ec0eec..a16ecab5229d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -1,5 +1,6 @@ #include "symbol.h" #include +#include #include #include #include @@ -195,7 +196,7 @@ int map__overlap(struct map *l, struct map *r) size_t map__fprintf(struct map *self, FILE *fp) { - return fprintf(fp, " %Lx-%Lx %Lx %s\n", + return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", self->start, self->end, self->pgoff, self->dso->name); } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index bc2732ee23eb..135f69baf966 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -279,7 +279,7 @@ const char *__event_name(int type, u64 config) static char buf[32]; if (type == PERF_TYPE_RAW) { - sprintf(buf, "raw 0x%llx", config); + sprintf(buf, "raw 0x%" PRIx64, config); return buf; } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 128aaab0aeda..6e29d9c9dccc 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -172,7 +172,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, sym = __find_kernel_function_by_name(tp->symbol, &map); if (sym) { addr = map->unmap_ip(map, sym->start + tp->offset); - pr_debug("try to find %s+%ld@%llx\n", tp->symbol, + pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, tp->offset, addr); ret = find_perf_probe_point((unsigned long)addr, pp); } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 313dac2d94ce..105f00bfd555 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -652,10 +652,11 @@ static void callchain__printf(struct sample_data *sample) { unsigned int i; - printf("... chain: nr:%Lu\n", sample->callchain->nr); + printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr); for (i = 0; i < sample->callchain->nr; i++) - printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); + printf("..... %2d: %016" PRIx64 "\n", + i, sample->callchain->ips[i]); } static void perf_session__print_tstamp(struct perf_session *session, @@ -672,7 +673,7 @@ static void perf_session__print_tstamp(struct perf_session *session, printf("%u ", sample->cpu); if (session->sample_type & PERF_SAMPLE_TIME) - printf("%Lu ", sample->time); + printf("%" PRIu64 " ", sample->time); } static void dump_event(struct perf_session *session, event_t *event, @@ -681,16 +682,16 @@ static void dump_event(struct perf_session *session, event_t *event, if (!dump_trace) return; - printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size, - event->header.type); + printf("\n%#" PRIx64 " [%#x]: event: %d\n", + file_offset, event->header.size, event->header.type); trace_event(event); if (sample) perf_session__print_tstamp(session, event, sample); - printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, - event__get_event_name(event->header.type)); + printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, + event->header.size, event__get_event_name(event->header.type)); } static void dump_sample(struct perf_session *session, event_t *event, @@ -699,8 +700,9 @@ static void dump_sample(struct perf_session *session, event_t *event, if (!dump_trace) return; - printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, - sample->pid, sample->tid, sample->ip, sample->period); + printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n", + event->header.misc, sample->pid, sample->tid, sample->ip, + sample->period); if (session->sample_type & PERF_SAMPLE_CALLCHAIN) callchain__printf(sample); @@ -843,8 +845,8 @@ static void perf_session__warn_about_errors(const struct perf_session *session, { if (ops->lost == event__process_lost && session->hists.stats.total_lost != 0) { - ui__warning("Processed %Lu events and LOST %Lu!\n\n" - "Check IO/CPU overload!\n\n", + ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 + "!\n\nCheck IO/CPU overload!\n\n", session->hists.stats.total_period, session->hists.stats.total_lost); } @@ -918,7 +920,7 @@ more: if (size == 0 || (skip = perf_session__process_event(self, &event, ops, head)) < 0) { - dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", + dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", head, event.header.size, event.header.type); /* * assume we lost track of the stream, check alignment, and @@ -1023,7 +1025,7 @@ more: if (size == 0 || perf_session__process_event(session, event, ops, file_pos) < 0) { - dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", + dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", file_offset + head, event->header.size, event->header.type); /* diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index b3637db025a2..805220afc083 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -12,6 +12,7 @@ * of the License. */ +#include #include #include #include @@ -94,7 +95,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; fprintf(svgfile, " \n"); - fprintf(svgfile, "\n", svg_page_width, total_height); + fprintf(svgfile, "\n", svg_page_width, total_height); fprintf(svgfile, "\n